Ticker vs SimpleTimer strange behaviour

I am working with an ESP8266 board and am trying to retrieve data from firebase. The sketch is a test sketch to first get communication between Firebase and my ESP. I have a function called D1_GetData() which is basically retrieving the data and writing the data to Serial. I want to use the Ticker library to retrieve the data at a set interval to avoid ESP crashes. The problem appears to be the Ticker library. No data seems to be retrieved, however if I use the SimpleTimer library it works!

Code using Ticker library

//LIBRARIES//
//WIFI//
#include <ESP8266WiFi.h>
//FIREBASE//
#include <FirebaseArduino.h>
//OTA//
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>
//REMOTEME//
#include <RemoteMe.h>
#include <RemoteMeSocketConnector.h>
//RTC//
#include <SNTPtime.h> 
//OTHERS//
#include <EEPROM.h>
#include "Ticker.h"

Ticker T_D1_GetData;
Ticker T_ClockFunction;

//FIREBASE// 
#define FIREBASE_HOST ""
#define FIREBASE_AUTH ""
//REMOTEME//
#define DEVICE_ID 2
#define DEVICE_NAME ""
#define TOKEN ""
//WIFI GLOBALS//
//AquArt Setup//
#define WIFI_SSID "proietti office"
//#define WIFI_SSID "AquArt";
#define WIFI_PASSWORD ""

//SETUP//
AsyncWebServer serverOTA(80);

SNTPtime NTPch("uk.pool.ntp.org");
RemoteMe& remoteMe = RemoteMe::getInstance(TOKEN, DEVICE_ID);

//TIME//
strDateTime dateTime;
byte Hr;
byte Min;
byte Sec;
unsigned long TimeNow;

//FIREBASE GLOBALS//
String D1_BoostDosage;
String D1_CalibrationInput;
String D1_DailyDosage;
String D1_Function;
String D1_SolutionInput;
String D1_Boost;
String D1_ALKATecLink;
String D1_CalibrateButton;
String D1_HoseChange;
String D1_SaveButton;
String D1_ALKATec;
String D1_Checker;

void setup()
{
  Serial.begin(115200);
  EEPROM.begin(512);
  
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED)
  {
//    digitalWrite(LEDON, LOW);
    delay(500);
  }
  
  NTPch.setSNTPtime();
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);

  //OTA////////////////////////////////////////////////////////
  serverOTA.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
  {
    request->send(200, "text/plain", "AquArt DOSETec");
  });
  //Serial.println(WiFi.localIP());
  AsyncElegantOTA.begin(&serverOTA);
  serverOTA.begin();
  /////////////////////////////////////////////////////////////
  


  remoteMe.setConnector(new RemoteMeSocketConnector());
  remoteMe.sendRegisterDeviceMessage(DEVICE_NAME);

  T_D1_GetData.attach_ms(1000, D1_GetData);
  //T_ClockFunction.attach(1, ClockFunction);
}


void loop()
{
  ESP.wdtFeed();
}

void D1_GetData()
{

  D1_Function = Firebase.getString("DOSETec/Tag_D1_Function");    //FUNCTION SELECT DOSETec SCREEN             
  D1_CalibrateButton = Firebase.getString("DOSETec/Tag_D1_CalibrateButton");    //CALIBRATE BUTTON DOSETec SCREEN
  D1_BoostDosage = Firebase.getString("DOSETec/Tag_D1_BoostDosage");    //BOOST DOSAGE INPUT DOSETec SCREEN
  D1_CalibrationInput = Firebase.getString("DOSETec/Tag_D1_CalibrationInput");    //CALIBRATION INPUT DOSETec SCREEN
  D1_DailyDosage = Firebase.getString("DOSETec/Tag_D1_DailyDosage");    //DAILY DOSAGE INPUT DOSETec SCREEN
  D1_SolutionInput = Firebase.getString("DOSETec/Tag_D1_SolutionInput");    //SOLUTION INPUT DOSETec SCREEN
  D1_Boost = Firebase.getString("DOSETec/Tag_D1_Boost");


  Serial.print("Function:");
  Serial.println(D1_Function);
  Serial.print("CalibrateButton:");
  Serial.println(D1_CalibrateButton);
  Serial.print("BoostDosage:");
  Serial.println(D1_BoostDosage);
  Serial.print("CalibationInput:");
  Serial.println(D1_CalibrationInput);
  Serial.print("DailyDosage:");
  Serial.println(D1_DailyDosage);
  Serial.print("SolutionInput:");
  Serial.println(D1_SolutionInput);
  Serial.print("D1_Boost:");
  Serial.println(D1_Boost);  
}

Serial Output

Function:
CalibrateButton:
BoostDosage:
CalibationInput:
DailyDosage:
SolutionInput:
D1_Boost:
Function:
CalibrateButton:
BoostDosage:
CalibationInput:
DailyDosage:
SolutionInput:
D1_Boost:

Code using the SimpleTimer library

//LIBRARIES//
//WIFI//
#include <ESP8266WiFi.h>
//FIREBASE//
#include <FirebaseArduino.h>
//OTA//
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncElegantOTA.h>
//REMOTEME//
#include <RemoteMe.h>
#include <RemoteMeSocketConnector.h>
//RTC//
#include <SNTPtime.h> 
//OTHERS//
#include <EEPROM.h>
#include <SimpleTimer.h>

SimpleTimer timer;


//FIREBASE// 
#define FIREBASE_HOST ""
#define FIREBASE_AUTH ""
//REMOTEME//
#define DEVICE_ID 2
#define DEVICE_NAME ""
#define TOKEN ""
//WIFI GLOBALS//
//AquArt Setup//
#define WIFI_SSID ""
//#define WIFI_SSID "AquArt";
#define WIFI_PASSWORD ""

//SETUP//
AsyncWebServer serverOTA(80);

SNTPtime NTPch("uk.pool.ntp.org");
RemoteMe& remoteMe = RemoteMe::getInstance(TOKEN, DEVICE_ID);



//TIME//
strDateTime dateTime;
byte Hr;
byte Min;
byte Sec;
unsigned long TimeNow;

//FIREBASE GLOBALS//
String D1_BoostDosage;
String D1_CalibrationInput;
String D1_DailyDosage;
String D1_Function;
String D1_SolutionInput;
String D1_Boost;
String D1_ALKATecLink;
String D1_CalibrateButton;
String D1_HoseChange;
String D1_SaveButton;
String D1_ALKATec;
String D1_Checker;

void setup()
{
  Serial.begin(115200);
  EEPROM.begin(512);
  
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED)
  {
//    digitalWrite(LEDON, LOW);
    delay(500);
  }
  
  NTPch.setSNTPtime();
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);

  //OTA////////////////////////////////////////////////////////
  serverOTA.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
  {
    request->send(200, "text/plain", "AquArt DOSETec");
  });
  //Serial.println(WiFi.localIP());
  AsyncElegantOTA.begin(&serverOTA);
  serverOTA.begin();
  /////////////////////////////////////////////////////////////
  
  remoteMe.setConnector(new RemoteMeSocketConnector());
  remoteMe.sendRegisterDeviceMessage(DEVICE_NAME);
  timer.setInterval(2000, D1_GetData);
  //timer.setInterval(1000, ClockFunction);
}

void loop()
{
  timer.run();
  ESP.wdtFeed();
}

void D1_GetData()
{

    D1_Function = Firebase.getString("DOSETec/Tag_D1_Function");    //FUNCTION SELECT DOSETec SCREEN             
    D1_CalibrateButton = Firebase.getString("DOSETec/Tag_D1_CalibrateButton");    //CALIBRATE BUTTON DOSETec SCREEN
    D1_BoostDosage = Firebase.getString("DOSETec/Tag_D1_BoostDosage");    //BOOST DOSAGE INPUT DOSETec SCREEN
    D1_CalibrationInput = Firebase.getString("DOSETec/Tag_D1_CalibrationInput");    //CALIBRATION INPUT DOSETec SCREEN
    D1_DailyDosage = Firebase.getString("DOSETec/Tag_D1_DailyDosage");    //DAILY DOSAGE INPUT DOSETec SCREEN
    D1_SolutionInput = Firebase.getString("DOSETec/Tag_D1_SolutionInput");    //SOLUTION INPUT DOSETec SCREEN
    D1_Boost = Firebase.getString("DOSETec/Tag_D1_Boost");


    Serial.print("Function:");
    Serial.println(D1_Function);
    Serial.print("CalibrateButton:");
    Serial.println(D1_CalibrateButton);
    Serial.print("BoostDosage:");
    Serial.println(D1_BoostDosage);
    Serial.print("CalibationInput:");
    Serial.println(D1_CalibrationInput);
    Serial.print("DailyDosage:");
    Serial.println(D1_DailyDosage);
    Serial.print("SolutionInput:");
    Serial.println(D1_SolutionInput);
    Serial.print("D1_Boost:");
    Serial.println(D1_Boost);  
}

Serial output using the SimpleTimer library

D1_Boost:false
Function:4
CalibrateButton:false
BoostDosage:100
CalibationInput:150
DailyDosage:200
SolutionInput:10000
D1_Boost:false
Function:4
CalibrateButton:false
BoostDosage:100
CalibationInput:150
DailyDosage:200
SolutionInput:10000
D1_Boost:false
Function:4

The to sketches above are identical except for the timing library used. Can anybody shed some light on why Ticker does not work and SimpleTimer does? You might then say that use the SimpleTimer, the issue with the SimpleTimer library is that when you start to have 6 or more timing events it crashes the ESP, and also i would like to education sake know why the Ticker library does not work

Your topic has been moved to a more suitable location on the forum. Installation and Troubleshooting is not for problems with (nor for advice on) your project.

My guess is that Ticker runs your callback in interrupt context and SimpleTimer calls your callback in user context. Maybe Firebase.getString() won't work in interrupt context.

Try:

// Global
bool TimeToGetDate = false;

  // In setup()
  T_D1_GetData.attach_ms(1000, SetFlag);
}

void SetFlag()
{
  TimeToGetData = true;
}

void loop()
{
  if (TimeToGetData)
  {
    D1_GetData();
    TimeToGetData = false;
  }
  ESP.wdtFeed();
}

Or you could eliminate both libraries and just use Arduino timing:

void loop()
{
  unsigned long currentMillis = millis();

  static unsigned long lastReportTime = 0;
  if (currentMillis - lastReportTime >= 1000)
  {
    D1_GetData();
    lastReportTime = currentMillis;
  }

  ESP.wdtFeed();
}

Yep in fact I opted for arduino timing. I found this bit of code which really helps. I attach for reference

In Global definitions

//TimeOut SetUp//
#define TimeOut(timer, period)\
  static unsigned long timer = millis();\
  for (unsigned long now = millis();(now - timer) > (unsigned long)period; timer = now)

Example of use

if (WiFi.status() != WL_CONNECTED)
  {
    digitalWrite(LEDON, LOW);
    WiFi.disconnect();
    Connected = true;
    DataUpdate = false;
    TimeOut(timeout, 5000)
    {
      WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    }
  }
  else
  {
    if (Connected)
    {
      remoteMe.sendPushNotificationMessage(1,"DOSETec Device","DOSETec Connected","badge.png","doseteclogo.png","");
      Connected = false;
      DataUpdate = true;
    }
    digitalWrite(LEDON, HIGH);
  }
}

it acts like a lambda timer

That doesn't look right. It expands to:

  static unsigned long timeout = millis();
  for (unsigned long now = millis(); (now - timeout) > (unsigned long)5000; timeout = now)
    {
      WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    }

Which is equivalent to:

  static unsigned long timeout = millis();
  unsigned long now = millis();
  while ((now - timeout) > (unsigned long)5000)
    {
      WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
      timeout = now;
    }

The 'WiFi.begin()' will never execute because (now - timeout) is close to zero and therefore not > 5000.

I think the macro should be changed to:

//TimeOut SetUp//
#define TimeOut(timer, period)\
  static unsigned long timer = millis();\
  for (unsigned long now = millis(); (now - timer) < period; now = millis())

You might want to escape the loop if the WiFi.begin() is successful. Otherwise, you will always call 'WiFi.begin()' repeatedly for five full seconds.

    TimeOut(timeout, 5000)
    {
      if (WiFi.begin(WIFI_SSID, WIFI_PASSWORD))
        break;
    }

Yea, I'd advise not trying to get too tricky with #define(s) like that. Just do it the "regular" Arduino-millis() way.

Thanks for that

Indeed. If you look at the source code in Ticker.h, you'll see lots of comments like:

    // callback will be called in SYS ctx when ticker fires
    void once_ms(uint32_t milliseconds, callback_function_t callback)
    {
        _callback_function = std::move(callback);
        _attach_ms(milliseconds, false);
    }

They don't say exactly what "System Context" is, but it may very well be during an interrupt.

Yes i saw that

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.