How to send text messages on UART?

Hi! I have two arduino UNO connected to UART (RX-TX / TX-RX). I want to send text messages, for example, "LED on" or "LED off" via UART. I wrote the following code that does not want to work:

Uno #1

#include <SoftwareSerial.h>
//rx tx
#define rxPin 7
#define txPin 8

SoftwareSerial mySerial(rxPin, txPin);


void setup() {
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  mySerial.begin(115200);
}

void loop() {
  delay(500);
  mySerial.println("Led On");  
  delay(1000);
  mySerial.println("Led Off");
  delay(500);
}

Uno #2

#include <SoftwareSerial.h>

#define rxPin 7
#define txPin 8

//int message = 0;
//String message;
char message = 0;

SoftwareSerial mySerial(rxPin, txPin);

void setup() {
    pinMode(rxPin, INPUT);
    pinMode(txPin, OUTPUT);
    mySerial.begin(115200);
	pinMode(13, OUTPUT);
} 
 
void loop() {
    if(mySerial.available() > 0) {  //если есть доступные данные
        // считываем байт
        //message = mySerial.parseInt();
        message = mySerial.read();
        //message = mySerial.readString();
     }
if(message == "Led On") {
	digitalWrite(13, HIGH);
}

if(message == "Led Off") {
	digitalWrite(13, LOW);
}   
}

What is my error?

The message variable can only hold one character.

mySerial.read() only returns a single character.

You can't use == to compare strings (you would need to use strcmp() instead).

You can't use SoftwareSerial reliably at such high baud rates. You should avoid it altogether, as it's a complete waste of resources, especially since you have other options for inter-UNO communication, such as SPI and I²C.

PieterP, I set this speed because I will use esp8266 instead of 1 uno, where this speed is normal. Or am I wrong?

In my opinion, you shouldn't use the ESP8266 like that.

For serious projects, you shouldn't use AT commands, but program the ESP directly.

Even when programming the ESP8266 directly, the Arduino UNO is not a good choice for adding extra IO. In that case, you'll be much better off using a IO extender, multiplexer, shift register or external ADC. These devices are well supported and much easier to use than to write your own Serial protocol for turning the Arduino UNO into a smart IO extender.

You'll get much better recommendations if you post some more details about your project.

Do you have an ESP8266 dev board (with USB interface), or one of those ESP-01 boards?

I have an ESP8266 NodeMCU. The fact is that in my DIY device there is a separate board, which I developed in EAGLE, on this board there is Atmega328P (which is used by Arduino UNO). On this board everything is already collected, and I do not have the opportunity to transfer everything to
board ESP8266. Before that, I planned to connect my board to ESP using I2C, because the board had logic 5v and ESP 3.3V - I ordered a level converter on eBay, but unfortunately the terminal was lost by mail and I do not have time to order a new. Therefore, I also decided to connect to the UART via a voltage divider so that the ESP does not fail.

pert, PieterP

Do you think this is a good solution? It works, but I'm not sure of the correctness.

int const LED = 13;

// Serial Input Variables
int intLoopCounter = 0;
String strSerialInput = "";

// the setup routine runs once when you press reset:
void setup() 
{
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
}

void loop() 
{
  // Slow down a bit. 
  // Note: This may have to be increased for longer strings or increase the iteration in GetPossibleSerialData() function.
  delay(1);
  CheckAndExecuteSerialCommand();  
}

void CheckAndExecuteSerialCommand()
{
  //Get Data from Serial
  String serialData = GetPossibleSerialData();

  if (serialData.startsWith("Led.On"))
  {
    digitalWrite(LED, HIGH);   // turn the LED on (HIGH is the voltage level)
  }
  else if  (serialData.startsWith("Led.Off"))
  {
    digitalWrite(LED, LOW);    // turn the LED off by making the voltage LOW
  }
}

String GetPossibleSerialData()
{
  String retVal;
  int iteration = 10; // 10 times the time it takes to do the main loop
  if (strSerialInput.length() > 0)
  {
    // Print the retreived string after looping 10(iteration) ex times
    if (intLoopCounter > strSerialInput.length() + iteration)
    {
        retVal = strSerialInput;
        strSerialInput = "";
        intLoopCounter = 0;
    }
    intLoopCounter++;
  }

  return retVal;
}

void serialEvent()
{
  while (Serial.available())
  {
    strSerialInput.concat((char) Serial.read());
  }
}

I see.

How are you programming the ATmega? Do you have UART0 free?
What does the ATmega have to do? Just controlling some outputs?

On my board there is a MOSFET that controls the 12V load, there is an RTC to which the supercapacitor is connected, there is AMS 3.3V for ESP power supply and contacts for connecting buttons and sensors.

Suyano:
Do you think this is a good solution?

No. Don't use String. Use C strings (null terminated char array). String wastes memory and can lead to unreliable operation due to memory fragmentation.

See Robin2's Serial Input Basics.

PieterP, thanks! I learned a lot from this topic! :slight_smile: I chose example number 5, changed the code slightly and it works fine! I sent several messages: "<Hello World!>" / "<24y>" / "" / "", all of them were successfully delivered and displayed on the port monitor.

// 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() {
    pinMode(13, OUTPUT);
    Serial.begin(9600);
    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();
        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);
     if(messageFromPC == "24y") {
      digitalWrite(13, HIGH);
    }
    if(messageFromPC == "HelloWorld!") {
      digitalWrite(13, HIGH);
    }
    //Serial.print("Integer ");
    //Serial.println(integerFromPC);
    //Serial.print("Float ");
    //Serial.println(floatFromPC);
}

Now I'm trying to put a message in the condition. The output of messages to the monitor port is as follows:

Serial.print("Message ");
    Serial.println(messageFromPC);

The message is stored in "messageFromPC"
I'm trying to light the LED, but it does not respond to these messages:

     if(messageFromPC == "24y") {
      digitalWrite(13, HIGH);
    }
    if(messageFromPC == "HelloWorld!") {
      digitalWrite(13, HIGH);
    }

I believe that the problem in coding messages, is there an idea?

The == operator will just compare the pointer of messageFromPC to the pointer to the static string "24y", so they can't ever be the same.

To compare cstrings, you have to use the strcmp function. Note that it returns 0 when the strings are the same.

It works!

char led[] = "24y";
      if(strcmp(led, messageFromPC) == 0) {
      digitalWrite(13, HIGH);
    }

Thank you very much! :slight_smile:

You don't have to declare a separate variable for the string, you can just do:

    if(strcmp(messageFromPC, "24y") == 0) {
      digitalWrite(13, HIGH);
    }

Ok, thanks!