Arduino State Machine using RTC as trigger for changing state

Hi All,

I'm trying to implement a state machine on Arduino using RTC to trigger state changes after specific hours. For this reason, I have implemented a small test state machine code for blinking LED.

In the actual state machine that I have , I intend to put hours as a time gap instead of seconds.

The code doesn't seem to work(Functionally). maybe I'm missing something. Please help.
Also, I'm very new to this. if there's a predefined way to use RTC with State machine please let me know.

I'm very thankful for all the help :smiley:

#include "RTClib.h"

RTC_DS3231 rtc;

DateTime now = rtc.now();
static uint8_t state;

void setup() {
  Serial.begin(57600);
  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }
  pinMode(LED_BUILTIN, OUTPUT);
  state = 0;
  Serial.println("Setup done");
}

void loop() {

  DateTime now = rtc.now();
  blink();

}
void blink(void)
{
  Serial.println("Entered fsm");
  DateTime timeLastTransition = rtc.now();
  DateTime LED_DELAY = now + TimeSpan(0, 0, 0, 5);
  switch (state)
  {
    case 0:   // toggle the LED
      Serial.println("LED Switch");
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      timeLastTransition = rtc.now();
      state = 1;
      break;

    case 1:   // wait
      Serial.println("Wait");

      if (rtc.now() > LED_DELAY) {
        LED_DELAY = rtc.now() + TimeSpan(0, 0, 0, 3);
        //Serial.println(now.second());
        state = 0;
      }
      break;
  }
}

Please describe exactly how it "doesn't work". What is it supposed to do? What does it not do? You did not add any comments to help decipher your code.

Hi,

Please excuse me for my cumbersome explanation.
here is the code with comments to help explain.

The Code stays at "Toggle state"(case 0) and doesn't go to wait state(case 1). As a result, the LED stays ON and doesn't blink.

I'm using DS3231 RTC with an Arduino Uno.

#include "RTClib.h"

RTC_DS3231 rtc;

static uint8_t state;

void setup() {
  Serial.begin(57600);
  if (! rtc.begin()) { // initialising RTC
    Serial.println("Couldn't find RTC");
    while (1);
  }
  pinMode(LED_BUILTIN, OUTPUT);// Setting LED Output
  state = 0;
  Serial.println("Setup done");
}

void loop() {

  blink_fsm(); // FSM function code for blinking LED every 5 seconds

}
void blink_fsm(void)
{
  DateTime now = rtc.now();

  switch (state)
  {
    case 0:   // toggle the LED
      DateTime LED_DELAY = now + TimeSpan(0, 0, 0, 5); // calculating time span every time it toggles the led.
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      Serial.println("LED Toggle");
      state = 1;
      break;

    case 1:   // wait
      Serial.println("Wait");

      if (now > LED_DELAY) { // If time exeeded then go to toggle state
        state = 0;
      }
      break;
  }
}

You are making things too complicated using the time span object. RTClib.h has the advantage of providing a "unix" time stamp with now.unixtime().

It is trivial to figure out the number of seconds for your future match.

#include "RTClib.h"

RTC_DS3231 rtc;

static uint8_t state;

void setup() {
  Serial.begin(57600);
  if (! rtc.begin()) { // initialising RTC
    Serial.println("Couldn't find RTC");
    while (1);
  }
  pinMode(LED_BUILTIN, OUTPUT);// Setting LED Output
  state = 0;
  Serial.println("Setup done");
}

void loop() {

  blink_fsm(); // FSM function code for blinking LED every 5 seconds

}
void blink_fsm(void)
{
  DateTime now = rtc.now();
  static unsigned long nextTime = 0;
  
  switch (state)
  {
    case 0:   // toggle the LED
      //DateTime LED_DELAY = now + TimeSpan(0, 0, 0, 5); // calculating time span every time it toggles the led.
      nextTime = now.unixtime() +5;
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      Serial.println("LED Toggle");
      state = 1;
      break;

    case 1:   // wait
      Serial.println("Wait");

      if (now.unixtime() > nextTime) { // If time exeeded then go to toggle state
        state = 0;
      }
      break;
  }
}

sometimes we use the wrong tool for the wrong purpose

i assume you're trying to figure out how to trigger different actions at various times using the RTC. at least for you example of toggling an LED, there's no need to track state. it would seem more appropriate to have a switch machine where each case is an event.

if (now - nowLst > Timeout)  {
    nowLst = now;
    // do something
}

this looks very similar to what your code it doing using the RTC

Hi All,

Thanks for all the help.
What Cattledog suggested worked.Using UnixTime and adding seconds.

What I originally wanted to do was @gcjr suggested. Yes, I wanted to do a series of steps after specific hours and then repeat the cycle all over.

if (now - nowLst > Timeout)  {
    nowLst = now;
    // do something
}

but I'm finding it hard to put in terms of RTC units.

?

if (now - nowLst > TimeSpan(0, 0, 0, 5))  {
    nowLst = now;
    // do something
}

I'm Getting the following error.

LED_RTC_FSM:42:25: error: no match for 'operator>' (operand types are 'TimeSpan' and 'TimeSpan')

       if (now - nowLast > TimeSpan(0, 0, 0, 5)) {

           ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~

Using library RTClib at version 1.5.0 in folder: G:\OneDrive\Documents\Arduino\libraries\RTClib 
Using library Wire at version 1.0 in folder: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire 
exit status 1
no match for 'operator>' (operand types are 'TimeSpan' and 'TimeSpan')[code]

[/code]

Post a link to the version of RTClib.h that you downloaded.

here is the link

I downloaded Version 1.8.0 from the library Manager.

Arduino Version: 1.8.12

LED_RTC_FSM:42:25: error: no match for 'operator>' (operand types are 'TimeSpan' and 'TimeSpan')

       if (now - nowLast > TimeSpan(0, 0, 0, 5)) {

now and TimeSpan objects are structs. The "greater than" operator does not apply.

not familiar with the library

would it word if nowLst were also type DataTime?

DateTime nowLst;