mobizt / Firebase-ESP8266

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

[Question] How to setJson from esp-now callback function or any registered callbacks as such and avoid wdt reset? #166

Closed yedhink closed 4 years ago

yedhink commented 4 years ago

When calling functions from this library within the registered esp callback, it leads to wdt reset. Using Arduino Uno with esp8266(nodemcu)

This is the failing code:-

#include <FirebaseESP8266.h>
#include <ESP8266WiFi.h>
#include <espnow.h>

#define FIREBASE_HOST "YOUR_URL"
#define API_KEY "YOUR_API_KEY"
#define PARENT_PATH "/YOUR_JSON_KEY/"

#define NO_OF_LIGHTS 2
#define NO_OF_FANS 0

const int totalApp = NO_OF_LIGHTS + NO_OF_FANS;
String appliances[totalApp];
String stat[2] = {"0", "0"};
volatile int receivedDataFromEspCallback = 0, dataSentToEsp = 0;

FirebaseData firebaseData, firebaseData2;

uint8_t receiverAddress[] = {0x40, 0xf5, 0x20, 0x17, 0x71, 0xba}; //add reciver mac ids

struct __attribute__((packed)) dataPacket
{
  int8_t index;
  int8_t status;
};

volatile dataPacket pac;
dataPacket packet;

void transmissionComplete(uint8_t *receiver_mac, uint8_t transmissionStatus)
{
  if (transmissionStatus == 0)
  {
    Serial.println("Data sent successfully from Master to Slave");
    dataSentToEsp = 1;
  }
  else
  {
    Serial.print("Error code in transmission complete: ");
    Serial.println(transmissionStatus);
  }
}

void sendDataToFriebase()
{
  FirebaseJson jsonDataForEachAppliance;
  const int pacIndexFromEsp = 0;
  const String stateOfAppliance = "1";
  jsonDataForEachAppliance.add("LED" + String(pacIndexFromEsp) + "_Status", stateOfAppliance);

  // TODO: move this to a separate func called printJson(FirebaseJson json);
  String jsonStr;
  jsonDataForEachAppliance.toString(jsonStr, true);
  Serial.println(jsonStr);

  if (Firebase.setJSON(firebaseData2, PARENT_PATH, jsonDataForEachAppliance))
  {
    Serial.println(firebaseData2.dataPath());
    stat[pac.index] = String(pac.status);
  }
  else
  {
    Serial.println("Failed in pushing json");
    Serial.println(firebaseData2.errorReason());
  }
  Firebase.reconnectWiFi(true);
}

void dataRecived(uint8_t *senderMac, uint8_t *data, uint8_t dataLength)
{
  unsigned long currentTime = millis();
  unsigned long previousTime = currentTime;
  while (previousTime - currentTime < 500)
  {
    currentTime = millis();
  }
  char macStr[18];
  // TODO: too confusing name. pack and pac are confusing. better name required
  dataPacket pack;

  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", senderMac[0], senderMac[1], senderMac[2], senderMac[3], senderMac[4], senderMac[5]);

  Serial.println();
  Serial.print("Received data from: ");
  Serial.println(macStr);

  memcpy(&pack, data, sizeof(pack));
  currentTime = millis();
  previousTime = currentTime;
  while (previousTime - currentTime < 500)
  {
    currentTime = millis();
  }
  pac.index = pack.index;
  pac.status = pack.status;
  Serial.print(pac.index);
  Serial.print(pac.status);

  // uncomment this and the if condition in loop(), to send data to firebase
  // hacky setting volatile variable
  //receivedDataFromEspCallback = 1;  // ---------------------------> This is the hack im using to make it work
  // doesn't work!!!!
  sendDataToFriebase(); // --------------------------------> Can't send to firebase. Comment this out to make it work
}

void wifiConnect()
{

  WiFi.begin("ssid", "password");
  Serial.println("Connecting..");
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.println('.');
    delay(1000);
  }
}

void setup()
{
  Serial.begin(74880);

  Serial.print("/n/nInitializing...");
  Serial.print("My MAC address is: ");
  Serial.println(WiFi.macAddress());

  WiFi.mode(WIFI_AP_STA);
  WiFi.disconnect();
  WiFi.softAP("ssid", "password", 1);
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);

  wifiConnect();

  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  // const int WIFI_CHANNEL = WiFi.channel();
  Serial.println(WiFi.channel());

  Firebase.begin(FIREBASE_HOST, API_KEY);
  Firebase.reconnectWiFi(true);

  if (esp_now_init() != 0)
  {
    Serial.println("ESP-NOW initialization failed");
    return;
  }

  // TODO: move into a sep function called initEspNow()
  esp_now_set_self_role(MY_ROLE);
  esp_now_register_send_cb(transmissionComplete);

  // REGISTER OUR CUSTOM CALLBACK FUNCTION-----------------------------------------> Register callback
  esp_now_register_recv_cb(dataRecived);
  esp_now_add_peer(receiverAddress, RECEIVER_ROLE, 1, NULL, 0);
  Serial.println("Initialized.");

  for (int i = 0; i < totalApp; ++i)
  {
    const String indexOfAppliance = String(i + 1);
    const String currentAppliance = i < NO_OF_LIGHTS ? "LED" : "FAN";
    appliances[i] = PARENT_PATH + currentAppliance + indexOfAppliance + "_Status";
    Serial.println(appliances[i]);
  }
}

void sendRealTimeDataToEspSlave(String realTimeValueReceived, int i)
{
  Serial.println("GPIO 4, on");
  dataPacket packet;
  int Status = realTimeValueReceived == "1" ? 1 : 0;
  packet.index = i;
  packet.status = Status;
  Serial.println(packet.index);
  Serial.println(packet.status);
  while (dataSentToEsp == 0)
  {
    esp_now_send(receiverAddress, (uint8_t *)&packet, sizeof(packet));
    delay(100);
  }
  dataSentToEsp = 0;
}

void loop()
{
  /*
  #TODO: When our esp sends back data, we set a global variable
  receivedDataFromEspCallback=1. This is a workaround to avoid
  wdt reset in microcontroller. Ideally this section should be
  handled within the registered callback of esp-now

  Uncomment this and also set global volatile variable receivedDataFromEspCallback=1 in dataRecived callback
  */
  //if (receivedDataFromEspCallback == 1) // ------------------->Uncomment this condition to make it work
  //{
  //  Serial.println("entered");
  //  delay(100);
  //  sendDataToFriebase();
  //  receivedDataFromEspCallback = 0;
  //}

  for (int i = 0; i < totalApp; ++i)
  {
    if (WiFi.status() != WL_CONNECTED)
    {
      wifiConnect();
    }
    Firebase.getString(firebaseData, appliances[i]);
    const String ledStatusFromFireBase = firebaseData.stringData();
    if (stat[i] != ledStatusFromFireBase)
    {
      Serial.println(i);
      stat[i] = ledStatusFromFireBase;
      Serial.println(ledStatusFromFireBase);
      sendRealTimeDataToEspSlave(ledStatusFromFireBase, i);
    }
  }
  delay(500);
}

@mobizt Any ideas how to use the REGISTERED_CALLBACK dataRecived to set firebase data and also why this error occurs in the first place? Thanks.

mobizt commented 4 years ago

The wdt reset error caused by the blocking code (http request/response) and delay used in your code which the wdt does not feed or clear in time.

The error still be happened even no Firebase library used.

The below code send https request to github and wait for response (the code is taken from Arduino example)


#include <ESP8266WiFi.h>
#include <espnow.h>

const char* host = "api.github.com";
const int httpsPort = 443;
const char fingerprint[] PROGMEM = "5F F1 60 31 09 04 3E F2 90 D2 B0 8A 50 38 04 E8 37 9F BC 76";

#define NO_OF_LIGHTS 2
#define NO_OF_FANS 0

const int totalApp = NO_OF_LIGHTS + NO_OF_FANS;
String appliances[totalApp];
String stat[2] = {"0", "0"};
volatile int receivedDataFromEspCallback = 0, dataSentToEsp = 0;

uint8_t receiverAddress[] = {0x40, 0xf5, 0x20, 0x17, 0x71, 0xba}; //add reciver mac ids

struct __attribute__((packed)) dataPacket
{
  int8_t index;
  int8_t status;
};

volatile dataPacket pac;
dataPacket packet;

void transmissionComplete(uint8_t *receiver_mac, uint8_t transmissionStatus)
{
  if (transmissionStatus == 0)
  {
    Serial.println("Data sent successfully from Master to Slave");
    dataSentToEsp = 1;
  }
  else
  {
    Serial.print("Error code in transmission complete: ");
    Serial.println(transmissionStatus);
  }
}

void dataRecived(uint8_t *senderMac, uint8_t *data, uint8_t dataLength)
{
  sendRequest();
}

void wifiConnect()
{

  WiFi.begin("ssid", "password");
  Serial.println("Connecting..");
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.println('.');
    delay(1000);
  }
}

void setup()
{
  Serial.begin(74880);

  Serial.print("/n/nInitializing...");
  Serial.print("My MAC address is: ");
  Serial.println(WiFi.macAddress());

  WiFi.mode(WIFI_AP_STA);
  WiFi.disconnect();
  WiFi.softAP("ssid", "password", 1);
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(myIP);

  wifiConnect();

  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  // const int WIFI_CHANNEL = WiFi.channel();
  Serial.println(WiFi.channel());

  if (esp_now_init() != 0)
  {
    Serial.println("ESP-NOW initialization failed");
    return;
  }

  // TODO: move into a sep function called initEspNow()
  esp_now_set_self_role(MY_ROLE);
  esp_now_register_send_cb(transmissionComplete);

  // REGISTER OUR CUSTOM CALLBACK FUNCTION-----------------------------------------> Register callback
  esp_now_register_recv_cb(dataRecived);
  esp_now_add_peer(receiverAddress, RECEIVER_ROLE, 1, NULL, 0);
  Serial.println("Initialized.");

  for (int i = 0; i < totalApp; ++i)
  {
    const String indexOfAppliance = String(i + 1);
    const String currentAppliance = i < NO_OF_LIGHTS ? "LED" : "FAN";
    appliances[i] = PARENT_PATH + currentAppliance + indexOfAppliance + "_Status";
    Serial.println(appliances[i]);
  }
}

void sendRealTimeDataToEspSlave(String realTimeValueReceived, int i)
{
  Serial.println("GPIO 4, on");
  dataPacket packet;
  int Status = realTimeValueReceived == "1" ? 1 : 0;
  packet.index = i;
  packet.status = Status;
  Serial.println(packet.index);
  Serial.println(packet.status);
  while (dataSentToEsp == 0)
  {
    esp_now_send(receiverAddress, (uint8_t *)&packet, sizeof(packet));
    delay(100);
  }
  dataSentToEsp = 0;
}

void loop()
{

  for (int i = 0; i < totalApp; ++i)
  {
    if (WiFi.status() != WL_CONNECTED)
    {
      wifiConnect();
    }
    sendRequest();
    sendRealTimeDataToEspSlave(stat[i], i);
  }
  delay(500);
}

void sendRequest()
{

  //The code was taken from Arduino example

  // Use WiFiClientSecure class to create TLS connection
  WiFiClientSecure client;
  Serial.print("connecting to ");
  Serial.println(host);

  Serial.printf("Using fingerprint '%s'\n", fingerprint);
  client.setFingerprint(fingerprint);

  if (!client.connect(host, httpsPort))
  {
    Serial.println("connection failed");
    return;
  }

  String url = "/repos/esp8266/Arduino/commits/master/status";
  Serial.print("requesting URL: ");
  Serial.println(url);

  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "User-Agent: BuildFailureDetectorESP8266\r\n" +
               "Connection: close\r\n\r\n");

  Serial.println("request sent");
  while (client.connected())
  {
    String line = client.readStringUntil('\n');
    if (line == "\r")
    {
      Serial.println("headers received");
      break;
    }
  }
  String line = client.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\""))
  {
    Serial.println("esp8266/Arduino CI successfull!");
  }
  else
  {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("==========");
  Serial.println(line);
  Serial.println("==========");
  Serial.println("closing connection");
}

The internet access is not available when making any http request in ESP-Now send/receive callback and the waiting time for server response was defined by the default tcp timeout in WiFiClient class.