Serial Comms between Nano & Micro

Good afternoon All!

I am a brand new member even though I have been reading tons of posts around here to find useful solutions to many issues during my first month of learning with Arduino.

I have a main project which is a Pro Micro handling several sensors which then pass the data via serial to a ESP8266-01 that handles the MQTT comms with my Home Assistant but…

…before doing that I understood I need to know how to pass data via serial as it’s not as easy as I thought!

Poking around all the pages here I have found tons of examples but somehow writing my code it doesn’t work also because I thought to use a Nano and the Pro Micro as test bench to try my comms.

Here below you can find the Sender code and Receiver code which I hope soon enough will become a function into my ESP8266 to forward all the data to HASSIO running on RPI4.

HELP!! :confused:

Sender:

#include<dht.h>
dht DHT;

#define DHT11_PIN 4

float humVal;
float tempVal;

void setup(){
  Serial.begin(115200);
 
}

void loop(){
  // data acquisition and output to PC 
  int chk = DHT.read11(DHT11_PIN);
  humVal = DHT.humidity;
  tempVal = DHT.temperature;
  
  char temperature[10];
  dtostrf(tempVal,4,1,temperature);
  char humidity[10];
  dtostrf(humVal,4,1,humidity);

  //Sending data
  Serial.write('T'); //Header
  Serial.write(temperature);
  Serial.write('T'); //Tail

  delay(150);

  Serial.write('H'); //Header
  Serial.write(humidity);
  Serial.write('H'); //Tail

  delay(5000);
}

Receiver:

byte header, tail =0;
float dataIn =0;
float tempVal, humVal;

void setup(){
  Serial.begin(115200);
  Serial1.begin(115200);
  while (!Serial){
    ;
  }
}

void loop(){
  if(Serial1.available() > 0){
    header = Serial1.read();
    dataIn = Serial1.read();
    tail = Serial1.read();
    lcd.setCursor(0,0);
    if ( header == 'T' && tail == 'T'){
       tempVal = dataIn;
       Serial.println("Temperature received: ");
       Serial.println(tempVal);
       Serial.println("C");
    }
    else if( header == 'H' && tail == 'H'){
       humVal = dataIn;
       Serial.println("Humidity received: ");
       Serial.println(tempVal);
       Serial.println("%");
    }
  delay(5000);
  }
}

Sender.ino (638 Bytes)

Receiver.ino (1.17 KB)

If you've been reading lots of posts, you should know how to properly post your code using code tags rather than just attaching it which forces people to download it and open it.

A very straight forward library is the SerialTransfer library. Install it through the library manager and give it a try.

blh64:
If you've been reading lots of posts, you should know how to properly post your code using code tags rather than just attaching it which forces people to download it and open it.

A very straight forward library is the SerialTransfer library. Install it through the library manager and give it a try.

My bad as I thought the upload procedure was generating the code tags...read the rules and ammended.

Thanks I will look into the SerialTransfer

Have a look at the examples in Serial Input Basics - simple reliable non-blocking ways to receive data. There is also a parse example to illustrate how to extract numbers from the received text.

The technique in the 3rd example will be the most reliable. It is what I use for Arduino to Arduino and Arduino to PC communication.

You can send data in a compatible format with code like this (or the equivalent in any other programming language)

Serial.print('<'); // start marker
Serial.print(value1);
Serial.print(','); // comma separator
Serial.print(value2);
Serial.println('>'); // end marker

…R

You can use SerialTransfer.h to automatically packetize and parse your data for inter-Arduino communication without the headace. The library is installable through the Arduino IDE and includes many examples.

Here are the library’s features:

This library:

  • can be downloaded via the Arduino IDE’s Libraries Manager (search “SerialTransfer.h”)
  • works with “software-serial” libraries
  • is non blocking
  • uses packet delimiters
  • uses consistent overhead byte stuffing
  • uses CRC-8 (Polynomial 0x9B with lookup table)
  • allows the use of dynamically sized packets (packets can have payload lengths anywhere from 1 to 254 bytes)
  • can transfer bytes, ints, floats, and even structs!!

Example TX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  char buff[] = "hi";

  myTransfer.txObj(buff, sizeof(buff));
  myTransfer.sendData(sizeof(buff));
  delay(100);
}

Example RX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  if(myTransfer.available())
  {
    char buff[40];
    
    myTransfer.rxObj(buff, sizeof(buff));
    
    Serial.println("New Data: ");
    Serial.write(buff, sizeof(buff));
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

For theory behind robust serial communication, check out the tutorials Serial Input Basics and Serial Input Advanced.

Robin2:
The technique in the 3rd example will be the most reliable. It is what I use for Arduino to Arduino and Arduino to PC communication.

Power_Broker:
For theory behind robust serial communication, check out the tutorials Serial Input Basics and Serial Input Advanced.

Thank you Robin2 and Power_Broker for your replies.

I am going to look into it straight away :slight_smile: just to confirm a doubt I have since the beginning, with these codes, can all the Arduinos be connected directly via TXRX pins? or should I generate a SoftwareSerial?

I have available a NANO, Pro Micro and Uno for testing

Many thanks again!

simobrs:
just to confirm a doubt I have since the beginning, with these codes, can all the Arduinos be connected directly via TXRX pins? or should I generate a SoftwareSerial?

I have available a NANO, Pro Micro and Uno for testing

You should probably use SoftwareSerial on the nano and Uno in order to leave HardwareSerial free for communicating with the PC and uploading programs. The Pro Micro has a spare HardwareSerial port (Serial1) on pins 0 and 1 so SoftwareSerial should not be necessary.

...R

Robin2:
You should probably use SoftwareSerial on the nano and Uno in order to leave HardwareSerial free for communicating with the PC and uploading programs. The Pro Micro has a spare HardwareSerial port (Serial1) on pins 0 and 1 so SoftwareSerial should not be necessary.

…R

Hi Robin2 it worked perfectly with SoftwareSerial so yeah my arduinos finally talked using the sample sketch shared by Power_Broker so I started editing and adding my bits to share Temperature and Humidity values but I believe I am not handling the conversions or something else is not working properly as the code gets stuck compiling :confused:

here is the sender code which converts the string into a char array:

#include <SerialTransfer.h>
#include <SoftwareSerial.h>
#include <dht.h>

dht DHT;
#define DHT11_PIN 4

float humVal,tempVal;
String str;

SerialTransfer myTransfer;

SoftwareSerial mySerial (2,3); //RX,TX

void setup()
{
  Serial.begin(115200);
  mySerial.begin(115200);
  myTransfer.begin(mySerial);
}

void loop()
{
  // data acquisition and output to PC 
  int chk = DHT.read11(DHT11_PIN);
  humVal = DHT.humidity;
  tempVal = DHT.temperature;
  str =String('H')+String(humVal)+String('T')+String(tempVal);
  Serial.println(str);
  
  transmission(str);

  delay(2000);
}

void transmission(String str){
  int str_len;
  str_len = str.length()+1;
  char buff[str_len];
  str.toCharArray(buff,str_len);
  myTransfer.txObj(buff,str_len);
  myTransfer.sendData(sizeof(buff));
  delay(100);
}

Receiver code:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  if(myTransfer.available())
  {
    Serial.println("New Data");
    for(byte i = 0; i < myTransfer.bytesRead; i++)
      Serial.write(myTransfer.rxBuff[i]);
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

I’ve attached also screenshot of the error while compiling and it does the same using myTransfer.txObj(buff,str_len); or myTransfer.txObj(buff,sizeof(buff)); as per original and working sketch.

Many thanks in advance for any help!

So now seems that the sender is working and sending properly data as I made it write pre and post transmission here is the adjustments:

void loop()
{
  // data acquisition and output to PC 
  int chk = DHT.read11(DHT11_PIN);
  humVal = DHT.humidity;
  tempVal = DHT.temperature;
  str =String('H')+String(humVal)+String('T')+String(tempVal);
  Serial.println(str);
  transmission(str);
  delay(2000);
}

void transmission(String str){
  int str_len = str.length()+1;
  char buff[str_len];
  str.toCharArray(buff,str_len);
  myTransfer.txObj(buff[str_len],str_len);
  myTransfer.sendData(sizeof(buff));
  Serial.println(buff);
  delay(100);
}

but this is what the receiver gets and I don't know how to fix it:

Do NOT send data in ASCII unless you have a really good reason to and never transfer "S"trings. It’s always best to transfer raw binary data instead:

TX:

#include <SerialTransfer.h>
#include <SoftwareSerial.h>
#include <dht.h>


const uint8_t DHT11_PIN 4


dht DHT;
SerialTransfer myTransfer;
SoftwareSerial mySerial (2,3); //RX,TX


float humVal, tempVal;


void setup()
{
  Serial.begin(115200);
  mySerial.begin(115200);
  myTransfer.begin(mySerial);
}


void loop()
{
  int chk = DHT.read11(DHT11_PIN);
  humVal = DHT.humidity;
  tempVal = DHT.temperature;
  
  Serial.print(F("Humidity: "));    Serial.println(humVal);
  Serial.print(F("Temperature: ")); Serial.println(tempVal);
  Serial.println();

  uint8_t sendSize = 0;
  myTransfer.txObj(humVal, sizeof(humVal), sendSize);
  sendSize += sizeof(humVal);
  
  myTransfer.txObj(tempVal, sizeof(tempVal), sendSize);
  sendSize += sizeof(tempVal);
  
  myTransfer.sendData(sendSize);
  delay(2000);
}

RX:

#include "SerialTransfer.h"


SerialTransfer myTransfer;


float humVal, tempVal;


void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}


void loop()
{
  if(myTransfer.available())
  {
    uint8_t recSize = 0;
    myTransfer.rxObj(humVal, sizeof(humVal), recSize);
    recSize += sizeof(humVal);
    
    myTransfer.rxObj(tempVal, sizeof(tempVal), recSize);
    recSize += sizeof(tempVal);
    
    Serial.println("New Data");
    Serial.print(F("Humidity: "));    Serial.println(humVal);
    Serial.print(F("Temperature: ")); Serial.println(tempVal);
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

Power_Broker:
Do NOT send data in ASCII unless you have a really good reason to and never transfer "S"trings. It's always best to transfer raw binary data instead:

That may be excellent advice when using your library - I presume it is sufficiently reliable that the user will never need to explore the raw data for debugging purposes.

However I take the opposite view when sending data where debugging may be required - it is much easier to debug human-readable text and I would never use binary data unless it was essential for performance purposes.

...R

Power_Broker:
Do NOT send data in ASCII unless you have a really good reason to and never transfer "S"trings. It's always best to transfer raw binary data instead:

Robin2:
That may be excellent advice when using your library - I presume it is sufficiently reliable that the user will never need to explore the raw data for debugging purposes.

Robin2 and Power_Broker thank you very much last night finally the two arduinos "talked" to eacheother perfectly! Such a relief and I understood now the concept behind (hopefully) but there's still a lot to learn for a newbi as me :slight_smile:

I have now adapted what I've learned and put it on the original project so the receiver is become an ESP8266 but seems that something is not working again...is it better to create a new post or share with you guys here? I just don't want to go off topic and be told off :slight_smile:

Many Thanks!

simobrs:
is it better to create a new post or share with you guys here?

IMHO it will be best to continue here so all the info is in one place.

...R

So after some researches online I’ve found what seemes a intresting way to pass data to Home Assistant using Json objects via MQTT so I put all together and after some painful debugging the software compiles but there’s no data coming into my broker. The Json part was in ArduinoJson 5 so I think I’ve migrated it to 6 correctly.

Now this is the ESP8266 code:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <SerialTransfer.h>

// Update these with values suitable for your network.
const char* ssid = "MYSSID";
const char* password = "MYPASSWORD";
const char* mqtt_server = "192.168.5.213";
#define mqtt_user "MQTTUSR" //enter your MQTT username
#define mqtt_password "MQTTPW" //enter your password

WiFiClient espClient;
PubSubClient client(espClient);

//Setup Serial Communication
SerialTransfer myTransfer;

//topic for the temperature
char* tempTopic = "ha/outside_temp";
//topic for the humidity
char* humTopic = "ha/outside_hum";
//topic for the ppm
char* ppmTopic = "ha/outside_ppm";

//Setup variables
String strTopic;
String strPayload;
String tempBuffer[11];
float humVal, tempVal, ppmVal;

//publishData function gets all the variables needed to be sent on MQTT
void publishData(float p_temperature){
  //create JSON object
  StaticJsonDocument<200> jsonBuffer;
  JsonObject root = jsonBuffer.as<JsonObject>();
  root["temperature"] = (String)p_temperature;
  //root.prettyPrintTo(Serial);
  serializeJsonPretty(root, Serial);
  Serial.println("");
  
  char data[200];
  serializeJson(root, data);
  //root.printTo(data, rootmeasureLenght()+1);
  client.publish(tempTopic, data, true);
}

void setup_wifi() {
 Serial.begin(115200);
  delay(100);
 
  // We start by connecting to a WiFi network
 
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
 
  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

// function called when a MQTT message arrived
void callback(char* p_topic, byte* p_payload, unsigned int p_length) {
}
 
 
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("arduinoClient", mqtt_user, mqtt_password)) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
 
void setup()
{
  setup_wifi(); 
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  Serial.begin(115200);
  Serial1.begin(115200); //Com to PC
  myTransfer.begin(Serial1);
  delay(150);
}
 
void loop(){
  
  //pooling data from serial
  if(myTransfer.available())
  {
    uint8_t recSize = 0;
    myTransfer.rxObj(humVal, sizeof(humVal), recSize);
    recSize += sizeof(humVal);
   
    myTransfer.rxObj(tempVal, sizeof(tempVal), recSize);
    recSize += sizeof(tempVal);

    myTransfer.rxObj(ppmVal, sizeof(ppmVal), recSize);
    recSize += sizeof(ppmVal);

    //call to publish data
    publishData(tempVal);
    
    
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
    
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  
}

This is the sender UNO hooked to DHT11 and MQ135:

#include <SerialTransfer.h>
#include <SoftwareSerial.h>
#include <dht.h>
#include <MQ135.h>

const uint8_t DHT11_PIN = 4;
const uint8_t ANALOGPIN = A0;

dht DHT;
MQ135 gasSensor = MQ135(ANALOGPIN);

SerialTransfer myTransfer;
SoftwareSerial mySerial (2,3); //RX,TX


float humVal, tempVal;


void setup()
{
  Serial.begin(115200);
  mySerial.begin(115200);
  myTransfer.begin(mySerial);
  float rzero = gasSensor.getRZero();
  Serial.print("MQ135 RZERO Calibration Value : ");
  Serial.println(rzero);
}


void loop()
{
  int chk = DHT.read11(DHT11_PIN);
  float ppm = gasSensor.getPPM();
  humVal = DHT.humidity;
  tempVal = DHT.temperature;
 
  Serial.print(F("Humidity: "));    Serial.println(humVal);
  Serial.print(F("Temperature: ")); Serial.println(tempVal);
  Serial.print(F("PPM: ")); Serial.println(ppm);
  Serial.println();

  uint8_t sendSize = 0;
  myTransfer.txObj(humVal, sizeof(humVal), sendSize);
  sendSize += sizeof(humVal);
 
  myTransfer.txObj(tempVal, sizeof(tempVal), sendSize);
  sendSize += sizeof(tempVal);

  myTransfer.txObj(ppm, sizeof(ppm), sendSize);
  sendSize += sizeof(ppm);
 
  myTransfer.sendData(sendSize);
  delay(5000);
}

And I believe it’s usuful to know how I am handling the topics into Home Assistant

#External Weather Unit
sensor 5:
  - platform: mqtt
    state_topic: "ha/outside_temp"
    unit_of_measurement: "°C"
    value_template: '{{ value_json.temperature }}'
    name: "Temp2 Outside"
  - platform: mqtt
    state_topic: "ha/humoutside"
    unit_of_measurement: "%"
    name: "Hum2 Outside"
  - platform: mqtt
    state_topic: "ha/ppmoutside"
    unit_of_measurement: "ppm"
    name: "PPM Outside"

In Home Assistant I’ve modified just the temperature value because I wanted to test with just one simple value otherwise debugging 3 different things could’ve become difficult.

I look forward to hear your thoughts!