How to enter time data to set a timer?

Hello,

I am using an Arduino UNO and an RTC module. I am trying to make non-blocking code to set a timer value via Serial Monitor that would then be compared with the time from the RTC module, and when the time is equal, then it should turn on the LED. I did research to find the best approach to this problem and i think reading each piece of data as a character and storing in a string is the best solution? I found the following thread quite useful: Receiving string from Serial Monitor, not getting all characters - Programming Questions - Arduino Forum and Serial Input Basics - updated - Introductory Tutorials - Arduino Forum
The program input is based on hour:minute format, so for example, 15:10

I am also trying to implement the following:

  1. an auto-fix method for the length of data input, such that if the data entered is more than 5 characters, then the rest is simply ignored.
    I was considering using the string trim() commands but i think maybe it will become a memory hog.
  2. check if the first two characters are integers and if the third character is a colon and the next 2 are integers.
    I did some research and i found that maybe it's better to use the String to Int Function and then compare if the hours input is valid and between 0 and 24 and if the minutes is between 0 and 59.
    So, if these conditions are not true, then an error is displayed.

Here is my current sketch:

/*
 * Serial input time in hour:minutes format 
 */

int ledPin = 13;

// test values from RTC module
int RTChours = 11;
int RTCmins = 45;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  Serial.println("LED OFF");
  Serial.println("serial on/off test");
}

void loop() {
  char input[7];
  int charsRead;
 
  if (Serial.available() > 0) {
    charsRead = Serial.readBytesUntil('\n', input, 6); // input format is hours:minutes
    //Serial.readBytesUntil(character, buffer, length) 
    //Serial.readBytesUntil() reads characters from the serial buffer into an array. 
    //The function terminates if the terminator character is detected, 
    //the determined length has been read, or it times out (see Serial.setTimeout()). 
    input[charsRead] = '\0';
  }

  if (strlen(input) > 0) {
    Serial.println(input);

    if(strcmp(input, (RTChours <= 24 && RTChours >= 0)) == 0)
    {
      if(strcmp(input, (RTCmins <= 59 && RTCmins >= 0)) == 0)
      {
        digitalWrite(ledPin, HIGH);
        Serial.println("LED ON");
      }
    }
  }
}

I get errors during compilation but before i dig deeper into strings, i'm not sure if this is the right approach anyway.

I suggest that you look at the serial input basics tutorial. It will show how to do serial reception into a null terminated character array (c_string). That avoids the use of the potentially troublesome String class and the examples are non-blocking.

Example #5 shows how to parse data using a delimiter (:).

Which RTC are you using? One thing to consider: the DS3231, for example, has built-in alarms that can be configured in a number of ways.

groundFungus:
I suggest that you look at the serial input basics tutorial. It will show how to do serial reception into a null terminated character array (c_string). That avoids the use of the potentially troublesome String class and the examples are non-blocking.

Example #5 shows how to parse data using a delimiter (:).

Thank you for the hint. I had a look at the code in Example #5 and it was very enlightening. I previously didn't know how to approach this problem. During my research, i read that there is an issue of String being a memory hog considering the limited resources of the UNO.

steve20016:
Which RTC are you using? One thing to consider: the DS3231, for example, has built-in alarms that can be configured in a number of ways.

I am using the DS1302. It's the only RTC that i have ever worked with, but i will have a look at the one that you suggested.

Here is my sketch:

// Receive integers with start- and end-markers combined with parsing
// non-destructive

const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars]; // temporary array for use when parsing

// integer variables to hold the parsed data
int integer1FromPC = 0;
int integer2FromPC = 0;

boolean newData = false;

//============

void setup() {
    Serial.begin(9600);
    Serial.println("Enter data in this style: <12, 24>");
    Serial.println();
}

//============

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) {
        strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0; // variable that defines where to store the next character in the array (an index, in other words).
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();
        
        if (recvInProgress == true) {
            
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0; // reset the ndx counter
                newData = true; // set new data status to available
            }
            
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempChars, ",");      // get the first part - the string
    //strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC
    integer1FromPC = atoi(strtokIndx);     // convert this part to an integer (ASCII to int)
    
    //strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    //integer1FromPC = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    integer2FromPC = atoi(strtokIndx);     // convert this part to an integer (ASCII to int)
    
}

//============

void showParsedData() {
    Serial.print("First Integer: ");
    Serial.println(integer1FromPC);
    Serial.print("Second Integer: ");
    Serial.println(integer2FromPC);
}

The code above compiles without errors and i did some basic testing and it seems to work. But i'm wondering if i missed anything that could cause the program to crash or maybe memory overflow??

And here is a different (and maybe improved?) version of this sketch whereby there is no copy of the original string since it is not needed in this project. I found the relevant code snippet for parseData() by sterretje from reply #7 in interpreting a string - Programming Questions - Arduino Forum

// Receive integers with start- and end-markers combined with parsing 
// destructive

const byte numChars = 32;
char receivedChars[numChars];
//char tempChars[numChars]; // temporary array for use when parsing

// variables to hold the parsed data
int integer1FromPC = 0;
int integer2FromPC = 0;

boolean newData = false;

//============

void setup() {
    Serial.begin(9600);
    Serial.println("Enter data in this style: <12, 24>");
    Serial.println();
}

//============

void loop() {
    recvWithStartEndMarkers();
    if (newData == true) {
        //strcpy(tempChars, receivedChars);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        showParsedData();
        newData = false;
    }
}

//============

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0; // variable that defines where to store the next character in the array (an index, in other words).
    char startMarker = '<';
    char endMarker = '>';
    char rc;

    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();
        
        if (recvInProgress == true) {
            
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0; // reset the ndx counter
                newData = true; // set new data status to available
            }
            
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

//============

void parseData()
{
  // find comma
  char *ptr = strchr(receivedChars, ',');

  // if no comma found
  if (ptr == NULL)
  {
    return;
  }
  // replace comma by null chracter
  *ptr = '\0';
  // convert
  integer1FromPC = atoi(receivedChars);

  // restore comma
  *ptr = ',';

  // point to position after comma
  ptr++;
  // convert
  integer2FromPC = atoi(ptr);
}

//============

void showParsedData() {
    Serial.print("First Integer: ");
    Serial.println(integer1FromPC);
    Serial.print("Second Integer: ");
    Serial.println(integer2FromPC);
}

Could i have used this method instead: string.toInt(); to convert string to int? Is it better?

Any recommendations on how/if i can make this code better (as in, more reliable and efficient)?

No such function string.toInt(). That function is the String class. When working with c-strings use the atoi() function.

                if (ndx >= numChars) {

Don't decrease ndx; you have not yet received an endMarker so the input is invalid. Ignore any following input until a startMarker is received, and start reading anew.