-
-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathAllServices.ino
375 lines (312 loc) · 16 KB
/
AllServices.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/**
* ABOUT:
*
* The beare minimum code example for using all Firebase services.
*
* The steps which are generally required are explained below.
*
* Step 1. Include the network, SSL client and Firebase libraries.
* ===============================================================
*
* Step 2. Define the user functions that requred for the library usage.
* =====================================================================
*
* Step 3. Define the authentication config (identifier) class.
* ============================================================
* In the Firebase/Google Cloud services REST APIs, the auth tokens are used for authentication/authorization.
*
* The auth token is a short-lived token that will be expired in 60 minutes and need to be refreshed or re-created when it expired.
*
* There can be some special use case that some services provided the non-authentication usages e.g. using database secret
* in Realtime Database, setting the security rules in Realtime Database, Firestore and Firebase Storage to allow public read/write access.
*
* The UserAuth (user authentication with email/password) is the basic authentication for Realtime Database,
* Firebase Storage and Firestore services except for some Firestore services that involved with the Google Cloud services.
*
* It stores the email, password and API keys for authentication process.
*
* In Google Cloud services e.g. Cloud Storage and Cloud Functions, the higest authentication level is required and
* the ServiceAuth class (OAuth2.0 authen) and AccessToken class will be use for this case.
*
* While the CustomAuth provides the same authentication level as user authentication unless it allows the custom UID and claims.
*
* Step 4. Define the authentication handler class.
* ================================================
* The FirebaseApp actually works as authentication handler.
* It also maintains the authentication or re-authentication when you place the FirebaseApp::loop() inside the main loop.
*
* Step 5. Define the SSL client.
* ==============================
* It handles server connection and data transfer works.
*
* In this beare minimum example we use only one SSL client for all processes.
* In some use cases e.g. Realtime Database Stream connection, you may have to define the SSL client for it separately.
*
* Step 6. Define the Async Client.
* ================================
* This is the class that is used with the functions where the server data transfer is involved.
* It stores all sync/async taks in its queue.
*
* It requires the SSL client and network config (identifier) data for its class constructor for its network re-connection
* (e.g. WiFi and GSM), network connection status checking, server connection, and data transfer processes.
*
* This makes this library reliable and operates precisely under various server and network conditions.
*
* Step 7. Define the class that provides the Firebase/Google Cloud services.
* ==========================================================================
* The Firebase/Google Cloud services classes provide the member functions that works with AsyncClient.
*
* Step 8. Start the authenticate process.
* ========================================
* At this step, the authentication credential will be used to generate the auth tokens for authentication by
* calling initializeApp.
*
* This allows us to use different authentications for each Firebase/Google Cloud services with different
* FirebaseApps (authentication handler)s.
*
* When calling initializeApp with timeout, the authenication process will begin immediately and wait at this process
* until it finished or timed out. It works in sync mode.
*
* If no timeout was assigned, it will work in async mode. The authentication task will be added to async client queue
* to process later e.g. in the loop by calling FirebaseApp::loop.
*
* The workflow of authentication process.
*
* -----------------------------------------------------------------------------------------------------------------
* Setup | FirebaseApp [account credentials/tokens] ───> InitializeApp (w/wo timeout) ───> FirebaseApp::getApp
* -----------------------------------------------------------------------------------------------------------------
* Loop | FirebaseApp::loop ───> FirebaseApp::ready ───> Firebase Service API [auth token]
* ---------------------------------------------------------------------------------------------------
*
* Step 9. Bind the FirebaseApp (authentication handler) with your Firebase/Google Cloud services classes.
* ========================================================================================================
* This allows us to use different authentications for each Firebase/Google Cloud services.
*
* It is easy to bind/unbind/change the authentication method for different Firebase/Google Cloud services APIs.
*
* Step 10. Set the Realtime Database URL (for Realtime Database only)
* ===================================================================
*
* Step 11. Maintain the authentication and async tasks in the loop.
* ==============================================================
* This is required for authentication/re-authentication process and keeping the async task running.
*
* Step 12. Checking the authentication status before use.
* =======================================================
* Before calling the Firebase/Google Cloud services functions, the FirebaseApp::ready() of authentication handler that bined to it
* should return true.
*
* Step 13. Process the results of async tasks the end of the loop.
* ============================================================================
* This requires only when async result was assigned to the Firebase/Google Cloud services functions.
*/
// Step 1
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <FirebaseClient.h>
// Step 2
void processData(AsyncResult &aResult);
void getMsg(Messages::Message &msg);
// Step 3
ServiceAuth sa_auth("CLIENT_EMAIL", "PROJECT_ID", "PRIVATE_KEY", 3000);
// Step 4
FirebaseApp app;
// Step 5
WiFiClientSecure ssl_client1, ssl_client2;
// Step 6
// Use two AsyncClients for sync and async tasks for demonstation only.
// This uses built-in core WiFi/Ethernet for network connection.
// See examples/App/NetworkInterfaces for more network examples.
using AsyncClient = AsyncClientClass;
AsyncClient async_client1(ssl_client1), async_client2(ssl_client2);
// Step 7
RealtimeDatabase Database;
Messaging messaging;
Firestore::Documents Docs;
Storage storage;
CloudStorage cstorage;
CloudFunctions cfunctions;
bool onetimeTest = false;
// The Optional proxy object that provides the data/information
// when used in async mode without callback.
AsyncResult dbResult, fcmResult, firestoreResult, functionResult, storageResult, cloudStorageResult;
uint32_t getTime()
{
uint32_t ts = 0;
Serial.print("Getting time from NTP server... ");
#if defined(ESP8266) || defined(ESP32) || defined(CORE_ARDUINO_PICO)
int max_try = 10, retry = 0;
while (time(nullptr) < FIREBASE_DEFAULT_TS && retry < max_try)
{
configTime(3 * 3600, 0, "pool.ntp.org");
unsigned long ms = millis();
while (time(nullptr) < FIREBASE_DEFAULT_TS && millis() - ms < 10 * 1000)
{
delay(100);
}
Serial.print(ts == 0 ? " failed, retry... " : "");
retry++;
}
ts = time(nullptr);
#elif __has_include(<WiFiNINA.h>) || __has_include(<WiFi101.h>)
ts = WiFi.getTime();
#endif
Serial.println(ts > 0 ? "success" : "failed");
return ts;
}
void setup()
{
Serial.begin(115200);
WiFi.begin("WIFI_AP", "WIFI_PASSWORD");
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
delay(300);
}
Serial.println();
Serial.print("Connected with IP: ");
Serial.println(WiFi.localIP());
Serial.println();
// The SSL client options depend on the SSL client used.
// Skip certificate verification
ssl_client1.setInsecure();
ssl_client2.setInsecure();
// Set timeout
ssl_client1.setConnectionTimeout(1000);
ssl_client1.setHandshakeTimeout(5);
ssl_client2.setConnectionTimeout(1000);
ssl_client2.setHandshakeTimeout(5);
// ESP8266 Set buffer size
// ssl_client1.setBufferSizes(4096, 1024);
// ssl_client2.setBufferSizes(4096, 1024);
// Assign the valid time only required for authentication process with ServiceAuth and CustomAuth.
app.setTime(getTime());
// Step 8
initializeApp(async_client1, app, getAuth(sa_auth), processData, "🔐 authTask");
// Step 9
app.getApp<RealtimeDatabase>(Database);
app.getApp<Messaging>(messaging);
app.getApp<Firestore::Documents>(Docs);
app.getApp<Storage>(storage);
app.getApp<CloudStorage>(cstorage);
app.getApp<CloudFunctions>(cfunctions);
// Step 10
Database.url("DATABASE_URL");
}
void loop()
{
// Step 11
app.loop();
// Step 12
if (app.ready() && !onetimeTest)
{
onetimeTest = true;
// Realtime Database set value.
// ============================
// Async call with callback function
Database.set<object_t>(async_client1, "/examples/BareMinimum/data/set4", object_t("{\"title\":\"Greeting!\",\"message\":\"Hi, I'm JSON.\"}"), processData, "RealtimeDatabase_SetTask");
// Async call with AsyncResult for returning result.
Database.set<number_t>(async_client1, "/examples/BareMinimum/data/set5", number_t(21.824976, 6), dbResult);
// Sync call which waits until the result was received.
bool status = Database.set<object_t>(async_client2, "/examples/BareMinimum/data/set6", object_t("[100,200,300]"));
if (status)
Serial.println("Value set complete.");
else
Firebase.printf("Error, msg: %s, code: %d\n", async_client2.lastError().message().c_str(), async_client2.lastError().code());
Messages::Message msg;
getMsg(msg);
// Cloud Messaging send message.
// =============================
// Async call with callback function
messaging.send(async_client1, Messages::Parent("PROJECT_ID"), msg, processData, "Messaging_SendMessageTask");
// Async call with AsyncResult for returning result.
messaging.send(async_client1, Messages::Parent("PROJECT_ID"), msg, fcmResult);
// Sync call which waits until the result was received.
String payload = messaging.send(async_client2, Messages::Parent("PROJECT_ID"), msg);
if (async_client2.lastError().code() == 0)
Serial.println(payload);
else
Firebase.printf("Error, msg: %s, code: %d\n", async_client2.lastError().message().c_str(), async_client2.lastError().code());
// Firestore get document.
// =======================
// Async call with callback function
Docs.get(async_client1, Firestore::Parent("PROJECT_ID"), "/examples/BareMinimum", GetDocumentOptions(DocumentMask("Singapore")), processData, "Firestore_GetDocumentTask");
// Async call with AsyncResult for returning result.
Docs.get(async_client1, Firestore::Parent("PROJECT_ID"), "/examples/BareMinimum", GetDocumentOptions(DocumentMask("Singapore")), firestoreResult);
// Sync call which waits until the result was received.
payload = Docs.get(async_client2, Firestore::Parent("PROJECT_ID"), "/examples/BareMinimum", GetDocumentOptions(DocumentMask("Singapore")));
if (async_client2.lastError().code() == 0)
Serial.println(payload);
else
Firebase.printf("Error, msg: %s, code: %d\n", async_client2.lastError().message().c_str(), async_client2.lastError().code());
// Firebase Storage get object metadata
// ====================================
// Async call with callback function
storage.getMetadata(async_client1, FirebaseStorage::Parent("STORAGE_BUCKET_ID", "/examples/BareMinimum/media.mp4"), processData, "Storage_GetMetadataTask");
// Async call with AsyncResult for returning result.
storage.getMetadata(async_client1, FirebaseStorage::Parent("STORAGE_BUCKET_ID", "/examples/BareMinimum/media.mp4"), storageResult);
// Sync call which waits until the result was received.
payload = storage.getMetadata(async_client2, FirebaseStorage::Parent("STORAGE_BUCKET_ID", "/examples/BareMinimum/media.mp4"));
if (async_client2.lastError().code() == 0)
Serial.println(payload);
else
Firebase.printf("Error, msg: %s, code: %d\n", async_client2.lastError().message().c_str(), async_client2.lastError().code());
// Cloud Storage list objects.
// ===========================
GoogleCloudStorage::ListOptions options;
options.maxResults(3);
options.prefix("media");
// Async call with callback function
cstorage.list(async_client1, GoogleCloudStorage::Parent("STORAGE_BUCKET_ID"), options, processData, "CloudStorage_ListObjectTask");
// Async call with AsyncResult for returning result.
cstorage.list(async_client1, GoogleCloudStorage::Parent("STORAGE_BUCKET_ID"), options, cloudStorageResult);
// Sync call which waits until the result was received.
payload = cstorage.list(async_client2, GoogleCloudStorage::Parent("STORAGE_BUCKET_ID"), options);
if (async_client2.lastError().code() == 0)
Serial.println(payload);
else
Firebase.printf("Error, msg: %s, code: %d\n", async_client2.lastError().message().c_str(), async_client2.lastError().code());
// Cloud Functions call function.
// ==============================
// Async call with callback function
cfunctions.call(async_client1, GoogleCloudFunctions::Parent("PROJECT_ID", "PROJECT_LOCATION"), "helloWorld", "test", processData, "Functions_CallTask");
// Async call with AsyncResult for returning result.
cfunctions.call(async_client1, GoogleCloudFunctions::Parent("PROJECT_ID", "PROJECT_LOCATION"), "helloWorld", "test", functionResult);
// Sync call which waits until the result was received.
payload = cfunctions.call(async_client2, GoogleCloudFunctions::Parent("PROJECT_ID", "PROJECT_LOCATION"), "helloWorld", "test");
if (async_client2.lastError().code() == 0)
Serial.println(payload);
else
Firebase.printf("Error, msg: %s, code: %d\n", async_client2.lastError().message().c_str(), async_client2.lastError().code());
}
// Step 13
processData(dbResult);
processData(fcmResult);
processData(firestoreResult);
processData(functionResult);
processData(storageResult);
processData(cloudStorageResult);
}
void processData(AsyncResult &aResult)
{
// Exits when no result available when calling from the loop.
if (!aResult.isResult())
return;
if (aResult.isEvent())
Firebase.printf("Event task: %s, msg: %s, code: %d\n", aResult.uid().c_str(), aResult.eventLog().message().c_str(), aResult.eventLog().code());
if (aResult.isDebug())
Firebase.printf("Debug task: %s, msg: %s\n", aResult.uid().c_str(), aResult.debug().c_str());
if (aResult.isError())
Firebase.printf("Error task: %s, msg: %s, code: %d\n", aResult.uid().c_str(), aResult.error().message().c_str(), aResult.error().code());
if (aResult.available())
Firebase.printf("task: %s, payload: %s\n", aResult.uid().c_str(), aResult.c_str());
}
void getMsg(Messages::Message &msg)
{
msg.topic("test");
Messages::Notification notification;
notification.body("Notification body").title("Notification title");
msg.notification(notification);
}