mobizt / Firebase-ESP32

[DEPRECATED]🔥 Firebase RTDB Arduino Library for ESP32. The complete, fast, secured and reliable Firebase Arduino client library that supports CRUD (create, read, update, delete) and Stream operations.
MIT License
415 stars 118 forks source link

A way to set the timestamp inside a json object? #91

Closed mbariola closed 4 years ago

mbariola commented 4 years ago

Hi there @mobizt

I need to save a moderately complex JSON object, which has an array of integers, some integers, and some strings. By following your examples, I am able to set a JSON object and commit it in one go.

This JSON object also has a timestamp, but I was not able to set it in the json object to commit. Only to do it in 2 steps: first the object without the timestamp, then fetching the object key and save the timestamp in it by giving a path within the object I just created.

Is there a way to do it in just one step?

to clarify I need to save something like this at /clients/config/[clientName]/current and would like it to happen in one go

configOptions: [int, int, int.......... int],
configVersion int,
name: string,
CreatedAt: Timestamp

Many thanks

mobizt commented 4 years ago

Please share the code that you create the JSON object with timestamp.

mobizt commented 4 years ago

No more feedback?

mbariola commented 4 years ago

Hi @mobizt

I'm away from the computer till later today. I'll add my code asap

On Sun, Aug 9, 2020, 08:01 mobizt notifications@github.com wrote:

No more feedback?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mobizt/Firebase-ESP32/issues/91#issuecomment-671011079, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABFKTX5UWV6NPXPHTBAVRYDR7Y3TFANCNFSM4PX3HEVQ .

mbariola commented 4 years ago

Hi @mobizt , thanks for waiting

here is the code. It does not compile (error on the timestamp line) ... as you have seen, I have tried to do it with a FirebaseData too, no luck. probably it's something trivial but I have difficulty understanding the separation of concerns between FirebaseJson objects and FirebaseData ones.

Ideally, I would like to create in memory the JSON object, then push it to a /...../clientID/readings path which will contain N (an array) of the objects I am building

FirebaseData firebaseData; // Firebase Object
FirebaseJsonArray readingsJsonArray;
FirebaseJson testObj;
      ....

                    /* code */
                    // build the obj to send
                    Firebase.setTimestamp(firebaseData, "/datetime");
                    for(int i = 0; i< numSteps; i++)
                    {
                      readingsJsonArray.add(stepStatus[i]);
                    }
                    testObj.set("/readings", readingsJsonArray);
                    //Firebase.set(firebaseData, "/readings", readingsJsonArray);
                    testObj.setTimestamp("/readingDateTime");
                    //Firebase.setTimestamp(firebaseData, "/readingDateTime" );
                    testObj.set("/gloveID", "DemoGloveID");
                    //Firebase.set(firebaseData, "/gloveID", "DemoGloveID" );
                    Firebase.push(firebaseData, readingsSavePath, testObj);
                    readingsJsonArray.clear();
                    testObj.clear();
mobizt commented 4 years ago

That is because you use the wrong syntax.

The setTimestamp function is not available (not existed) for FirebaseJson object.

The data types i.e. bool, int, float, double, string, JSON object and JSON array object are supported with set and add function of FirebaseJson and FirebaseJsonArray.

The FirebaseJson is the JSON object (class) that used for JSON data manipulation (create, edit and parsing)
and FirebaseJsonArray is the array object (class) which can creat, edit and then add to the JSON object (FirebaseJson).

The FirebaseJsonData (similar to FirebaseData object) is the class object that holds the parsing result when you want to get or extract the value from specific key in the FirebaseJson object.

The Firebase.setTimestamp function is for Firebase class only. This timestamp value was hidden from Firebase web console but you can read its double value (UNIX timestamp value of milliseconds) from specific node with the FirebasegetDouble function as seen in Timestamp.ino example.

To check all supported function please see this.

If you want to set the timestamp to the JSON object, you just use

testObj.set("/readingDateTime", xxxxxxxxxxxx);

Where as xxxxxxxxxxxx is the integer number of timestamp which you need to assign or get it from the system time of the device (NTP server or RTC chip).

This is total difference from Firebase.setTimestamp which you don't need to provide the timestamp value to the function, Firebase will be set it for you automatically at the server side.

mbariola commented 4 years ago

Hi @mobizt ,

I understand a little bit better, and that I had a confusion between client set timestamp and firebase set timestamp. So, what I want to achieve, is to create an array of JSON objects. each of the objects is made like this:

So, I seem to understand that

1) I need to copy my normal int array into a FirebaseJsonArray, then add this array to a .... FirebaseJson? (i.e. testObj) 2) Add other key/values pairs to this FirebaseJson object. Is it correct to say that at this moment, the object has not been saved to Firebase and all paths are relative to the object itself?

3) push this object in the firebase path (i.e. save to firebase) where the list of readings is. I am not sure at this point what syntax is best to use. 3.1) If I want to have an explicitly set timestamp from Firebase, how should I specify it while building the object befoee pushing it to firebase?

Thanks

mbariola commented 4 years ago

ok, this is the current version of the code.....

                    for(int i = 0; i< numSteps; i++)
                    {
                      readingsJsonArray.add((int)stepStatus[i]);
                    }
                    testObj.set("/readings", readingsJsonArray);
                    testObj.set("/sockID", "DemoSockID");
                    // TODO:  /readingDateTime needs to be set by Firebase server
                    Firebase.push(firebaseData, readingsSavePath, testObj);
                    readingsJsonArray.clear(); // will this deallocate the memory / best way to avoid leaks / fragmentation?
                    testObj.clear();

It works. And I also saw that I can construct the object in memory and push it in one go with Firebase.push.

But I still need to set the readingsDateTime key/value with the Firebase server value . This bit I do not know how to do in a short way.

mobizt commented 4 years ago

You can't set the server value in to any json data.

You need to make a single request for set server value timestamp.

Please read the Firebase REST API document.

mbariola commented 4 years ago

Ok, so I need to set it on a different call, then read it and put that value in the json, right?

On Tue, Aug 11, 2020, 02:02 mobizt notifications@github.com wrote:

You can't set the server value in to any json data.

You need to make a single request for set server value timestamp.

Please read the Firebase REST API document.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mobizt/Firebase-ESP32/issues/91#issuecomment-671648575, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABFKTX2FHM2KJSTE3LZ7WYTSACC77ANCNFSM4PX3HEVQ .

mobizt commented 4 years ago

Yes.

mbariola commented 4 years ago

The point is, I do not trust the client devices to give me an accurate and consistent timestamp. This is why I need to find the equivalent of Firebase::serverTimestamp (if I recall correctly) EDIT: of this: firebase.database.ServerValue.TIMESTAMP

On Tue, Aug 11, 2020, 02:04 Massimiliano Bariola m.bariola@gmail.com wrote:

Ok, so I need to set it on a different call, then read it and put that value in the json, right?

On Tue, Aug 11, 2020, 02:02 mobizt notifications@github.com wrote:

You can't set the server value in to any json data.

You need to make a single request for set server value timestamp.

Please read the Firebase REST API document.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mobizt/Firebase-ESP32/issues/91#issuecomment-671648575, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABFKTX2FHM2KJSTE3LZ7WYTSACC77ANCNFSM4PX3HEVQ .

mbariola commented 4 years ago

Ok got it. Thanks!

On Tue, Aug 11, 2020, 02:05 mobizt notifications@github.com wrote:

Yes.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mobizt/Firebase-ESP32/issues/91#issuecomment-671649664, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABFKTX6UIT7WMDB5UX747MDSACDNRANCNFSM4PX3HEVQ .

mobizt commented 4 years ago

For server timestamp, the pay load for the request sent to server must be {".sv":"timestamp"} which should not mixed by any user data.

mbariola commented 4 years ago

I am unsure how to use it. Can you provide a line of code passing that payload?

Alternatively, does Firebase provide a current timestamp that can be read? (so that I can skip the "setting the timestamp first" part)? Thanks

On Tue, Aug 11, 2020, 02:10 mobizt notifications@github.com wrote:

For server timestamp, the pay load for the request sent to server must be {".sv":"timestamp"} which should not mixed by any user data.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/mobizt/Firebase-ESP32/issues/91#issuecomment-671651020, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABFKTX3NJDMUKOJXA66LGELSACD6XANCNFSM4PX3HEVQ .

mobizt commented 4 years ago

No, I only explain what the server timestamp payload (data that being send to server) should be.

When you call the Firebasse.setTimestamp function, the library sent this JSON {".sv":"timestamp"} to Firebase server. This kind of JSON payload is treated like a command not a user data.

mbariola commented 4 years ago

Hi @mobizt , yes that I understood, but had problems to find out how to make sure to pass that special value at the time of object creation.

So I experimented a little with what you said, and found a way. I don't know if it's the most elegant one, but it works and it does all with just one call to firebase


FirebaseJson testObj;

                    for(int i = 0; i< numSteps; i++)
                    {
                      readingsJsonArray.add((int)stepStatus[i]);
                    }
                    testObj.set("/readings", readingsJsonArray);
                    testObj.set("/shirtID", "DemoShirtID");
                    testObj.set("/readingDateTime/.sv", "timestamp");
                    Firebase.push(firebaseData, readingsSavePath, testObj) || Serial.println(firebaseData.errorReason());
                    readingsJsonArray.clear();
                    testObj.clear();

Thanks for your input, it nudged me in the right direction. Cheers!

mobizt commented 4 years ago

You can trust the device time as you can get the time from NTP server that give you the same and accurate timestamp for every client device.

The millisecond timestamp is only the second timestamp multiplied by 1000.

Some Time library provides more precision timestamp from NTP server with the request/response time delay compensation.

mbariola commented 4 years ago

Unfortunately I am developing a battery powered device and thus it is most of the time in super low power mode and offline. it comes online only to send data in bursts, then goes offline again. Any extra call shortens battery life. But I found a way that works (see comment above)! thanks for the info

ksb86 commented 3 years ago

@mbariola Thanks for posting, I had the same question. Your implementation worked for me too. Using the '.set' with a path instead of the '.add' method on the json object is the trick. Also, thanks @mobizt for this library, works great!