Strange problem with if function/timer

Hi all,

Sorry to bother you with my problems, but i have something i can not explain. I working on a project that us uses a serial transmission between 2 PCBs. Im using SerialTransfer.h which is a great library. Thank you PowerBroker2!

So i'm controlling relays via serial. for safety reasons i want to disable the relays if i'm not receiving any new data for some time or if there is a error in the package. Everything is working fine, with one exception...

The timeout code in the IF function is randomly triggering. Once every 0 to 5 seconds or so. I'm using debug code to understand what te program is doing. The serial monitor output is posted below.

  • So i seem to be receiving a package every 100ms. The debug code tells me so.
  • If im reading this message, the "lastmessage" timestamp must have been updated
  • This means there is no way my timeout code should be activated.

What is happening here? This should not be possible!

Serial:

[19:52:23.412 -> message received
19:52:23.513 -> message received
19:52:23.513 -> Timeout!!! Switching all outputs off
19:52:23.616 -> message received
19:52:23.719 -> message received

RX test code:

#include "SerialTransfer.h"

#define AccRelay      A3     // Pin to enable Acc relay
#define InvRelay      13     // Pin to enable inverter relay

SerialTransfer MainSysTransfer;


// Main variables
unsigned long CurrentMillis;        // main timer for every loop

// Variables for relay control
unsigned long LastMessage = 0;
int MsgTimeOut = 10000;

struct STRUCT {
  bool AccRelStatus;
  bool InvRelStatus;
  //add more variables
} SharedVar;


void setup()
{
  Serial.begin(115200);
  MainSysTransfer.begin(Serial); 
  pinMode(AccRelay, OUTPUT);        // Set pin as output
  pinMode(InvRelay, OUTPUT);        // Set pin as output
}


void loop() {
  CurrentMillis = millis();                                         // Update main timer 
  
  if(MainSysTransfer.available()) {                                 // Check if new message was received
    MainSysTransfer.rxObj(SharedVar, sizeof(SharedVar));            // Transfer data from RX buffer into local struct
    LastMessage = millis();                                         // Set timestamp of last received message
    Serial.println ("message received");                          // Debug code. Disable for real application
    digitalWrite(AccRelay, SharedVar.AccRelStatus);                 // Write the current "AccRelStatus" boolean to the digital output switching the relay
    digitalWrite(InvRelay, SharedVar.InvRelStatus);                 // Write the current "InvRelStatus" boolean to the digital output switching the relay
  }
  else if(MainSysTransfer.status < 0) {                             // If the return of MainSysTransfer.status is lower then 0, there was a problem with the message
      digitalWrite(AccRelay, false);                                // Corrupt message! Disable relay for safety reasons
      digitalWrite(InvRelay, false);                                // Corrupt message! Disable relay for safety reasons
     Serial.println("Corrupt message!!! Switch off all outputs "); // Debug code. Disable for real application
  }
  if (CurrentMillis - LastMessage >= MsgTimeOut) {                  // Check if the last received message is within the set timeout
    digitalWrite(AccRelay, false);                                  // Timeout! Disable relay for safety reasons
    digitalWrite(InvRelay, false);                                  // Timeout! Disable relay for safety reasons
    Serial.println ("Timeout!!! Switching all outputs off");      // Debug code. Disable for real application
  }
}

TX testcode:

#include "SerialTransfer.h"

SerialTransfer MainSysTransfer;

struct STRUCT {
  bool AccRelStatus;
  bool InvRelStatus;
  //add more variables
} SharedVar;

void setup()
{
  Serial1.begin(115200);
  MainSysTransfer.begin(Serial1);
  pinMode(30, INPUT_PULLUP);    
  pinMode(31, INPUT_PULLUP);    
  pinMode(13, OUTPUT);    
}


void loop()
{
  SharedVar.AccRelStatus = !digitalRead(31); 
  SharedVar.InvRelStatus = !digitalRead(30); 
  digitalWrite(13, SharedVar.AccRelStatus);   
  MainSysTransfer.txObj(SharedVar, sizeof(SharedVar));
  MainSysTransfer.sendData (sizeof(SharedVar));
  delay(100);
}

change this:

LastMessage = millis() ;

to:

LastMessage = CurrentMillis ;

this test goes wrong (unsigned integer subtraction) :

if (CurrentMillis - LastMessage >= MsgTimeOut) { . . .  }

if millis() in the meantime increments such that LastMessage > CurrentMillis .

The problem is in the RX code:

...
  CurrentMillis = millis();
 
  if(MainSysTransfer.available()) {
    MainSysTransfer.rxObj(SharedVar, sizeof(SharedVar)); 
    LastMessage = millis();             // <<==== Problem is here

...

  if (CurrentMillis - LastMessage >= MsgTimeOut) {

CurrentMillis is set to millis
"Some time" later also LastMessage is set to millis

But what will happen sooner or later: millis will increase within this "some time later".
And when it comes to:
CurrentMillis - LastMessage
the result will be 4294967295

This is the result when you try to subtract a larger number from a smaller one and you life in "unsigned land". :slight_smile:

Solution: change line

LastMessage = millis();

to

LastMessage = CurrentMillis;

Wow, I totally missed that. I have no idea why i did it that way. I used this type of timer before, and always using CurrentMillis.

It works great now. Thank you so much!

Sorry for being such a noob!

Good to hear that it is working now!