Read various types of data over serial

Hey All,
I've got a home automation project where I'm controlling lights, i.e. turning pins HIGH and LOW using various forms of input; IR signals, buttons and Serial. I use the serial connection to communicate to and from a Raspberry Pi running a web server.

I recently wrote some code for a thermostat to control a space heater.

I want to be able to set the desired temperature from a web application and then set that variable usr_temp over serial. I can make this work by using the following code

String a;

while(Serial1.available()) {

a = Serial1.readString();// read the incoming data as string

usr_temp = (a.toFloat());

Serial1.print("The new temperature setting is ");
Serial1.println(usr_temp);

}

I'm also listening to serial with a bunch of if statements to read ASCII characters to toggle LEDs which looks like this

  if (Serial1.available() > 0) {
    // read the oldest byte in the serial buffer:
    incomingByte = Serial1.read();
    // if it's a capital H (ASCII 72), turn on the LED:
    if (incomingByte == 'H') {
      digitalWrite(ledPin, HIGH);
      Serial1.println("ON - PIN 44");
    }
    // if it's an L (ASCII 76) turn off the LED:
    if (incomingByte == 'L') {
      digitalWrite(ledPin, LOW);
      Serial1.println("OFF - PIN 44");
    }   
    }

These things work on their own but confilct when together. For example when I send and ASCII character to toggle the LED pins it resets usr_temp to 0.0.

So my question is how can I send ASCII and integers that don't confuse one another over the same serial connection?

Thanks!
Rich

how can I send ASCII and integers that don't confuse one another over the same serial connection?

well seems you are sending everything in ASCII, right?

you need to parse what's coming and decide what's action to take based on what you receive

These things work on their own but confilct when together.

No surprise. You should only have a single Serial1.read(). Read Serial Input Basics - updated

Collect the serial data and next decide based on the data what must happen.

Look @how GPS serial works:

  • $ starts every line
  • Next 5 characters define comma separted data, CR terminated.

Parsing is now very easy!

SP8266 for use with Arduino IDE and ESP8266 plug-in

void GPSstuff(char c) {                                         // GPSbuffer[] is global
  static int i, j;                                              //   persistent within function scope
  static char q;
  static bool flag = false;
  static char GPSbuffer[120];                                   // GPS serial line input buffer
  q = c;

  if ( q == 0x24 )                                              // '

Full code here

Ray
  {
    i = 0;                                                      // brute force sync on '


[Full code here](https://www.hackster.io/rayburne/tardis-time-esp8266-ap-webserver-gps-6b5d2a)

Ray to GPSbuffer[0]
    // Serial << "Found $" << endl;
  }

  if ( i < 120) GPSbuffer[i++] = q;
  // Serial << "Index=" << (i -1) << "Input=" << q << endl;
  //if (i = 120) i = 119;                                       // array limit safety

  if (q == 0x0d) {
    flag = true;                                                // is the character a CR for eol
    i = 0;
  }

  if (flag) {                                                   // test for end of line and if the right GPSbuffer
    flag = false;                                               // reset for next time
    UDP.beginPacketMulticast(broadcastIP, localPort, apIP);
    // Serial << "We are in the flag routine..." << GPSbuffer[3] << GPSbuffer[4] << GPSbuffer[5] << endl;
    // Serial << "Analyzing " << GPSbuffer[3] << GPSbuffer[4] << GPSbuffer[5] << endl;
    if ( (GPSbuffer[3] == 0x52) && (GPSbuffer[4] == 0x4d) && (GPSbuffer[5] == 0x43)) // 'R' && 'M' && 'C'
    {
      for (j = 0; j < 120 ; j++) {
        UDP.write(GPSbuffer[j]);
      }
      UDP.write("\r\n");                                        // terminate the line
      UDP.endPacket();                                          // clear UDP buffer
    }
  }
}

Full code here

Ray

Thanks for you guidance everyone!

So I read through the Serial Input Basics page which was very helpful but I don't understand it fully. So I took one of the examples from there that reads a char, int and float using <> as start and end markers. This works fine and good but when I try to toggle a pin with commands I'm getting stuck.

I wrote a little function toggleLED(); to try to do this but I can't seem to get it toggle the LED.

// Example 5 - Receive with start- and end-markers combined with parsing
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

 // variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;

boolean newData = false;

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

void setup() {
    Serial.begin(115200);
    Serial.println("This demo expects 3 pieces of data - text, an integer and a floating point value");
    Serial.println("Enter data in this style <HelloWorld, 12, 24.7>  ");
    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();
        toggleLED();
        newData = false;
 }
}

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

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    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;
                newData = true;
            }
        }

        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
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, ",");
    floatFromPC = atof(strtokIndx);     // convert this part to a float
}

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

void showParsedData() {
    Serial.print("Message ");
    Serial.println(messageFromPC);
    Serial.print("Integer ");
    Serial.println(integerFromPC);
    Serial.print("Float ");
    Serial.println(floatFromPC);
}

void toggleLED(){
    if(messageFromPC == "A"){
      digitalWrite(13, HIGH);
      Serial.println("ON");
    }
    if(messageFromPC == "a"){
      digitalWrite(13, LOW);
      Serial.println("OFF");
    }
  }

How can I tweak this code to toggle pins?

Thanks
Rich!

    if(messageFromPC == "A"){

You are comparing the address of the variable messageFromPC to the address of the string literal "A". Of course they are never going to match.

Use strcmp() to compare strings.

That was it! Thanks!

OK, next I want to set a variable from the float value. Here is my function

  void setTemp(){
     if ((floatFromPC != 0)  == 0){  // test to see if the two strings are equal
       float usr_temp = floatFromPC;
       Serial.println(usr_temp);
  }
  }

Seems pretty straight forward but it doesn't seem to fire. Here's the sketch in it's entirety. How can I set the usr_temp value from floatFromPC? Thanks again!

// Example 5 - Receive with start- and end-markers combined with parsing
const byte numChars = 32;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

 // variables to hold the parsed data
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;
float usr_temp = 0.0;
boolean newData = false;

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

void setup() {
    pinMode (13, OUTPUT);
    Serial.begin(115200);
    Serial.println("This demo expects 3 pieces of data - text, an integer and a floating point value");
    Serial.println("Enter data in this style <HelloWorld, 12, 24.7>  ");
    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();
        toggleLED();
        setTemp();
        newData = false;
 }
}

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

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    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;
                newData = true;
            }
        }

        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
 
    strtokIndx = strtok(NULL, ","); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer

    strtokIndx = strtok(NULL, ",");
    floatFromPC = atof(strtokIndx);     // convert this part to a float
}

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

void showParsedData() {
    Serial.print("Message ");
    Serial.println(messageFromPC);
    Serial.print("Integer ");
    Serial.println(integerFromPC);
    Serial.print("Float ");
    Serial.println(floatFromPC);
}

void toggleLED(){
     if (strcmp(messageFromPC, "A")  == 0){  // test to see if the two strings are equal
      digitalWrite(13, HIGH);
      Serial.println("ON");
    }
     if (strcmp(messageFromPC, "a")  == 0){  // test to see if the two strings are equal
      digitalWrite(13, LOW);
      Serial.println("OFF");
    }
  }
  void setTemp(){
     if ((floatFromPC != 0)  == 0){  // test to see if the two strings are equal
       float usr_temp = floatFromPC;
       Serial.println(usr_temp);
  }
  }
     if ((floatFromPC != 0)  == 0){  // test to see if the two strings are equal

That is NOT what that code does.

but it doesn't seem to fire.

If your code did catch fire, it would let all the magic smoke out of the Arduino. So, it's good that it didn't fire.

Asking programming questions require that you make some attempt to speak the language.

If your code did catch fire, it would let all the magic smoke out of the Arduino

:grinning:

Spewed coffee on keyboard ... clean-up on keyboard #3

Ray

richiep:
OK, next I want to set a variable from the float value. Here is my function

  void setTemp(){

if ((floatFromPC != 0)  == 0){  // test to see if the two strings are equal
      float usr_temp = floatFromPC;
      Serial.println(usr_temp);
 }
 }

First off, floatFromPC is not a string, it is a float.

Your IF line should just be

if (floatFromPC != 0) {

but comparing float variables to a precise number is risky. There might be a difference of 0.00001 which would cause the test to fail. It would be better to use

if (floatFromPC > 0) {

if all your values are positive or (I think)

if (abs(floatFromPC) > 0) {

if they could be negative or positive.

...R