mobizt/Firebase-Arduino-WiFiNINA

Writing data does not work with two streams

tickietackie opened this issue · 10 comments

When I am using two concurrent streams, writing data does not work anymore:

Example:

/*
 * Created by K. Suwatchai (Mobizt)
 * 
 * Email: k_suwatchai@hotmail.com
 * 
 * Github: https://github.com/mobizt
 * 
 * Copyright (c) 2021 mobizt
 *
*/

//Example shows how to connect to Firebase RTDB and get stream connection

//Required WiFiNINA Library for Arduino from https://github.com/arduino-libraries/WiFiNINA

#include "Firebase_Arduino_WiFiNINA.h"

#define DATABASE_URL "xx" //<databaseName>.firebaseio.com or <databaseName>.<region>.firebasedatabase.app
#define DATABASE_SECRET "xx"

#define WIFI_SSID "xx"
#define WIFI_PASSWORD "xx"

//Define Firebase data object
FirebaseData fbdo;

FirebaseData stream;
FirebaseData stream2;

unsigned long sendDataPrevMillis = 0;

String path = "/table_config/CUywk9RhoFTWTZTD4KuNcMvFDBC2/mcu_read/123456789";
String path2 = "/table_config/CUywk9RhoFTWTZTD4KuNcMvFDBC2/mcu_read_write/123456789";

int count = 0;

void setup()
{

  Serial.begin(115200);
  delay(100);
  Serial.println();

  Serial.print("Connecting to Wi-Fi");
  int status = WL_IDLE_STATUS;
  while (status != WL_CONNECTED)
  {
    status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    Serial.print(".");
    delay(100);
  }
  Serial.println();
  Serial.print("Connected with IP: ");
  Serial.println(WiFi.localIP());
  Serial.println();

  //Provide the autntication data
  Firebase.begin(DATABASE_URL, DATABASE_SECRET, WIFI_SSID, WIFI_PASSWORD);
  Firebase.reconnectWiFi(true);

  if (!Firebase.beginStream(stream, path))
  {
    Serial.println("Can't connect stream, " + stream.errorReason());
    Serial.println();
  }

  if (!Firebase.beginStream(stream2, path2))
  {
    Serial.println("Can't connect stream, " + stream2.errorReason());
    Serial.println();
  }
}

void loop()
{

  if (millis() - sendDataPrevMillis > 15000 || sendDataPrevMillis == 0)
  {
    sendDataPrevMillis = millis();
    count++;

    Serial.print("Set string... ");

    if (Firebase.setString(fbdo, path + "/string", "Hello World! " + String(count)))
    {
      Serial.println("ok");
      Serial.println("path: " + fbdo.dataPath());
      Serial.println("type: " + fbdo.dataType());
      Serial.print("value: ");
      if (fbdo.dataType() == "int")
        Serial.println(fbdo.intData());
      if (fbdo.dataType() == "int64")
        Serial.println(fbdo.int64Data());
      if (fbdo.dataType() == "uint64")
        Serial.println(fbdo.uint64Data());
      else if (fbdo.dataType() == "double")
        Serial.println(fbdo.doubleData());
      else if (fbdo.dataType() == "float")
        Serial.println(fbdo.floatData());
      else if (fbdo.dataType() == "boolean")
        Serial.println(fbdo.boolData() == 1 ? "true" : "false");
      else if (fbdo.dataType() == "string")
        Serial.println(fbdo.stringData());
      else if (fbdo.dataType() == "json")
        Serial.println(fbdo.jsonData());
      else if (fbdo.dataType() == "array")
        Serial.println(fbdo.arrayData());
    }
    else
    {
      Serial.println("error, "+ fbdo.errorReason());
    }
    Serial.println();
  }

  if (!Firebase.readStream(stream))
  {
    Serial.println("Can't read stream, "+ stream.errorReason());
  }

  if (stream.streamTimeout())
  {
    //Serial.println("Stream timed out, resuming...");
  }

  if (stream.streamAvailable())
  {
    count++;
    if (stream.dataType() == "null")
      count = 0;

    Serial.println("Stream data received... ");
    Serial.println("stream path: " + stream.streamPath());
    Serial.println("event path: " + stream.dataPath());
    Serial.println("data type: " + stream.dataType());
    Serial.println("event type: " + stream.eventType());
    Serial.print("value: ");
    if (stream.dataType() == "int")
      Serial.println(stream.intData());
    if (stream.dataType() == "int64")
      Serial.println(stream.int64Data());
    if (stream.dataType() == "uint64")
      Serial.println(stream.uint64Data());
    else if (stream.dataType() == "double")
      Serial.println(stream.doubleData());
    else if (stream.dataType() == "float")
      Serial.println(stream.floatData());
    else if (stream.dataType() == "boolean")
      Serial.println(stream.boolData() == 1 ? "true" : "false");
    else if (stream.dataType() == "string")
      Serial.println(stream.stringData());
    else if (stream.dataType() == "json")
      Serial.println(stream.jsonData());
    else if (stream.dataType() == "array")
      Serial.println(stream.arrayData());
    Serial.println();
  }

  if (!Firebase.readStream(stream2))
  {
    Serial.println("Can't read stream, "+ stream2.errorReason());
  }

  if (stream2.streamTimeout())
  {
    //Serial.println("Stream timed out, resuming...");
  }

  if (stream2.streamAvailable())
  {
    count++;
    if (stream2.dataType() == "null")
      count = 0;

    Serial.println("Stream data received... ");
    Serial.println("stream path: " + stream2.streamPath());
    Serial.println("event path: " + stream2.dataPath());
    Serial.println("data type: " + stream2.dataType());
    Serial.println("event type: " + stream2.eventType());
    Serial.print("value: ");
    if (stream2.dataType() == "int")
      Serial.println(stream2.intData());
    if (stream2.dataType() == "int64")
      Serial.println(stream2.int64Data());
    if (stream2.dataType() == "uint64")
      Serial.println(stream2.uint64Data());
    else if (stream2.dataType() == "double")
      Serial.println(stream2.doubleData());
    else if (stream2.dataType() == "float")
      Serial.println(stream2.floatData());
    else if (stream2.dataType() == "boolean")
      Serial.println(stream2.boolData() == 1 ? "true" : "false");
    else if (stream2.dataType() == "string")
      Serial.println(stream2.stringData());
    else if (stream2.dataType() == "json")
      Serial.println(stream2.jsonData());
    else if (stream2.dataType() == "array")
      Serial.println(stream2.arrayData());
    Serial.println();
  }
}

image

Is this a bug, or are concurrent streams not supported?
Should I turn off the stream before writing and turn it on again after that? Is there another solution?

The library works fine as it should.

You don't mention which device you used.

The problem can be out of memory when you work with large payload.

Use SAMD21 device with U-blox NINA built-in.

I am using an Arduino Nano 33 IoT, which should not have a problem regarding memory there.
Is the example working for you, that I posted?

The device used ESP32 module (U-blox NINA) to handle WiFI.

On the WiFI module side, ESP32 runs the Arduino firmware that handle WiFI, SSL client, and SPI communication.

When you call Firebase functions to make a request, the connection required memory at least 80k on ESP32 to handle the secure connection.

The stream operation required the socket to be kept open which in case of two FirebaseData objects used, it required 160k on ESP32 side all the time which mostly used by mbedTLS SSL engine library.

If you call some function with another FirebaseData object, it required additional 80k or total > 240k.

If ESP32 does not have enough memory for the new request, the SSL handshake will fail as connection refused that you've have seen.

With ESP32 alone without Arduino firmware, using three FirebaseData objects for 240k reserved memory is ok.

Then in this library, you can't use more than two FirebaseData objects that open the connection at the same time.

Ok, thanks for clarifying this.
So would suggest ending the stream, then sending the data, and starting it again?
A follow up question would be if it would be possible to miss an event that way, or will then, when resuming the stream, the missed event be loaded?

You should keep the stream connection open by using the separate FirebaseData object for it.

The http protocol used in REST APIs does not support the quality of service like in MQTT.

In addition, Firebase RTDB server is a storage server instead of cloud server which it can't compute or make division and no query language supported.

It supports only data filtering and some conditional operation via Etag.

I think it is working. So just for clarification. You meant I should use one of the FirebaseData objects for writing and as well as the stream, is this right?
Or could you maybe just provide a short code snippet, that would be very kind.

Yes, I am aware that it's not as convenient as MQTT, but I think for integrating, it's far easier and faster, plus you don't need extra servers, just the RTDB and you're good to go.
For small projects this is I think a good way to go, if you want an exchange of data, between web client and Arduino.

Sorry I don't get it. Of course I am aware of this example. But it is only using one stream. I would prefer using two concurrently, and writing data in parallel. So how do I approach this with an Arduino Nano 33 IoT?
In the example for the stream and for writing, two different FirebaseData Objects are used ...
Or is there no possibility because of the memory usage?

As I ever replied you that you can't use more than two FirebaseData objects that open the connection simultaneously.

Two FirebaseData objects for stream in two different paths without CRUD operations.
Or one for stream and one for other CRUD operations.
Or use one FirebaseData object for stream and CRUD which stream will be stop during CRUD and resume with readStream.

CRUD - Create, read, update and delete

Ok, yes. Then I try resuming the stream after writing with readStream.
Thanks for your help.