Arduino Nano - NodeMCU Serial Communication - Issues

Hello everyone,

TL;DR:
After WiFi connection loss, a NodeMCU unit connected to an Arduino NANO via TX-RX pins doesn't automatically reconnect to WiFi and hangs. I need to manually power on-and off the NodeMCU (and Arduino NANO as the power supply is common). The WiFi auto-reconnect feature on the NodeMCU was working fine before implementing Serial connection to the NANO.

Should I try to use Serial.end() in my code to stop serial communication in case of WiFi connection loss to avoid hanging?

Complete Post:
My project setup is as follows:

  • NodeMCU v1.0 connected to WiFi and MQTT
  • Arduino NANO connected to NodeMCU via Hardware Serial pins

The NodeMCU is connected to a remote WiFi AP that has a tendency to lose internet connection. I've setup my NodeMCU code so that, in case of MQTT or WiFi disconnection, the board either tries to re-connect (in case of MQTT disconnection) or reboots (in case of WiFi disconnection). This works fine.

The Arduino NANO has some analog sensors and their readings are transmitted to NodeMCU via Hardware Serial "on demand".

All works fine, but today the AP failed and the NodeMCU disconnected. After restarting the AP manually, the NodeMCU didn't connected back. Only cutting power to NodeMCU and Arduino NANO made the trick.

Is it possible that the Hardware Serial communication is preventing NodeMCU from properly reconnecting or rebooting?
Should I try to use the "Serial.end" function to stop serial communication in case of WiFi connection loss or MQTT disconnection?
I tried putting Serial.end() in my code below but I have yet to test it.

This is the NodeMCU code:

#include <FS.h>     
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ESP8266WiFi.h>
#include <Ticker.h>
#include <AsyncMqttClient.h>
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
//#include <ESP8266WebServer.h>
#include <SoftwareSerial.h>
#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson

String myString; // complete message from arduino, which consistors of snesors data
char rdata; // received charactors
float Irms1, Irms2, Irms3; // sensors 
String rms1, rms2, rms3;

// #define WIFI_SSID "TP-LINK_DOWN"
// #define WIFI_PASSWORD ""

// Raspberri Pi Mosquitto MQTT Broker
#define MQTT_HOST "192.168.5.20"
#define MQTT_PORT "1883"
#define MQTT_USER "Marco"
#define MQTT_PASS ""
#define MQTT_PUB_TEMP "centraletermica"
int mqtt_port_int;

char id[7];
String ids;

//flag for saving data
bool shouldSaveConfig = false;

void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

// GPIO where the DS18B20 is connected to
const int oneWireBus = 4;          
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(oneWireBus);
// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature sensors(&oneWire);
// Temperature value
float T1;
float T2;
float T3;
float T4;
float T5;

DeviceAddress sensor1 = { 0x28, 0xFF, 0x74, 0x84, 0x73, 0x15, 0x2, 0x49 };
DeviceAddress sensor2 = { 0x28, 0xFF, 0x22, 0x38, 0x74, 0x15, 0x3, 0xE7 };
DeviceAddress sensor3= { 0x28, 0xFF, 0x65, 0xA2, 0x73, 0x15, 0x1, 0x63 };
DeviceAddress sensor4= { 0x28, 0xFF, 0x9D, 0xA3, 0x73, 0x15, 0x1, 0x74 };
DeviceAddress sensor5= { 0x28, 0xFF, 0x2F, 0xC0, 0x73, 0x15, 0x2, 0x42 };

AsyncMqttClient mqttClient;
Ticker mqttReconnectTimer;

WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
Ticker wifiReconnectTimer;

unsigned long previousMillis = 0;   // Stores last time temperature was published
const long interval = 10000;        // Interval at which to publish sensor readings
boolean stringComplete = false;  // whether the string is complete


 void connectToMqtt() {
 //Serial.println("Connecting to MQTT...");
 mqttClient.connect();
 }

void onWifiConnect(const WiFiEventStationModeGotIP& event) {
  Serial.println("Connected to Wi-Fi.");
  connectToMqtt();
}

// void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
//   Serial.println("Disconnected from Wi-Fi.");
//   mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
//   wifiReconnectTimer.once(2, connectToWifi);
// }

void onMqttConnect(bool sessionPresent) {
  Serial.println("Connected to MQTT.");
  Serial.print("Session present: ");
  Serial.println(sessionPresent);
}

void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
  Serial.println("Disconnected from MQTT.");
  // Serial.end(); currently not used
  if (WiFi.isConnected()) {
    mqttReconnectTimer.once(2, connectToMqtt);
    // Serial.begin(9600); currently not used
  }
}

/*void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
  Serial.println("Subscribe acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
  Serial.print("  qos: ");
  Serial.println(qos);
}

void onMqttUnsubscribe(uint16_t packetId) {
  Serial.println("Unsubscribe acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}*/

void onMqttPublish(uint16_t packetId) {
  Serial.print("Publish acknowledged.");
  Serial.print("  packetId: ");
  Serial.println(packetId);
}

String getValue(String data, char separator, int index)
{
    int found = 0;
    int strIndex[] = { 0, -1 };
    int maxIndex = data.length() - 1;
 
    for (int i = 0; i <= maxIndex && found <= index; i++) {
        if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
        }
    }
    return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

unsigned strToInt(const char *str, int base)
{
    const char digits[] = "01234567890ABCDEF";
    unsigned result = 0;

    while(*str)
    {
        result *= base;
        result += strchr(digits, *str++) - digits;
    }
    return result;
}

//per la ricezione delle letture da Nano
const byte numChars = 100;
char receivedChars[numChars];   // an array to store the received data

boolean 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();
        myString = myString + rc;
        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;
                //myString = rc;
            }
        }

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

void showNewData() {
    if (newData == true) {
        //Serial.print("This just in ... ");
        //Serial.println(receivedChars);
        newData = false;
    }
}


void setup(){

 WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP    
 sensors.begin();
  Serial.begin(9600);
  while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
  Serial.println();
WiFiManager wm;

  //clean FS for testing 
//  SPIFFS.format();

  //read configuration from FS json
  Serial.println("mounting FS...");

  if (SPIFFS.begin()) {
    Serial.println("mounted file system");
    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println("reading config file");
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println("\nparsed json");
          strcpy(MQTT_HOST, json["MQTT_HOST"]);
          strcpy(MQTT_PORT, json["MQTT_PORT"]);
          strcpy(MQTT_USER, json["MQTT_USER"]);
          strcpy(MQTT_PASS, json["MQTT_PASS"]);
          strcpy(MQTT_PUB_TEMP, json["MQTT_PUB_TEMP"]);

        } else {
          Serial.println("failed to load json config");
        }
      }
    }
  } else {
    Serial.println("failed to mount FS");
  }
  //end read

      //reset settings - wipe credentials for testing
    //wm.resetSettings();

    //wm.setConfigPortalBlocking(false);

  //set custom field(s)
  WiFiManagerParameter custom_text("<p>Insert mqtt server address, port, topic, user and pwd</p>");
  wm.addParameter(&custom_text);
  
  WiFiManagerParameter custom_mqtt_server("server", "mqtt server", "192.168.5.20", 40);
  wm.addParameter(&custom_mqtt_server);

  WiFiManagerParameter custom_mqtt_port("port", "mqtt port", "1883", 40);
  wm.addParameter(&custom_mqtt_port);

  WiFiManagerParameter custom_mqtt_topic("topic", "mqtt topic", (MQTT_PUB_TEMP + ids).c_str(), 40); //String(id)).c_str()
  wm.addParameter(&custom_mqtt_topic);    

  WiFiManagerParameter custom_mqtt_user("user", "mqtt user", "", 20);
  wm.addParameter(&custom_mqtt_user);

  WiFiManagerParameter custom_mqtt_pass("pass", "mqtt pass", "", 20);
  wm.addParameter(&custom_mqtt_pass);
 bool res;
    // res = wm.autoConnect(); // auto generated AP name from chipid
    // res = wm.autoConnect("AutoConnectAP"); // anonymous ap
    res = wm.autoConnect("CentraleTermica",""); // password protected ap

    if(!res) {
        Serial.println("Failed to connect");
         ESP.restart();
    } 
    else {
        //if you get here you have connected to the WiFi    
        Serial.println("connected...yeey :)");
    }
  
  
    //set config save notify callback
  wm.setSaveConfigCallback(saveConfigCallback);

  strcpy(MQTT_HOST, custom_mqtt_server.getValue());
  strcpy(MQTT_PORT, custom_mqtt_port.getValue());
  strcpy(MQTT_USER, custom_mqtt_user.getValue());
  strcpy(MQTT_PASS, custom_mqtt_pass.getValue());
  strcpy(MQTT_PUB_TEMP, custom_mqtt_topic.getValue());


  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["MQTT_HOST"] = MQTT_HOST;
    json["MQTT_PORT"] = MQTT_PORT;
    json["MQTT_USER"] = MQTT_USER;
    json["MQTT_PASS"] = MQTT_PASS;
    json["MQTT_PUB_TEMP"] = MQTT_PUB_TEMP;

    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println("failed to open config file for writing");
    }

    json.printTo(Serial);
    json.printTo(configFile);
    configFile.close();
    //end save
  }


  wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
  //wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);

 mqtt_port_int = strToInt(MQTT_PORT,10);
  // MQTT_USER = strtol(MQTT_USER);
  // MQTT_PORT = strtol(MQTT_PORT);
  // MQTT_PASS = strtol(MQTT_PASS);
  mqttClient.onConnect(onMqttConnect);
  mqttClient.onDisconnect(onMqttDisconnect);
  //mqttClient.onSubscribe(onMqttSubscribe);
  //mqttClient.onUnsubscribe(onMqttUnsubscribe);
  mqttClient.onPublish(onMqttPublish);
  mqttClient.setServer(MQTT_HOST, mqtt_port_int);
  // If your broker requires authentication (username and password), set them below
  mqttClient.setCredentials(MQTT_USER, MQTT_PASS);
  
  //connectToWifi();
  mqttClient.connect();
}

void loop(){ 
  //wm.process();
  unsigned long currentMillis = millis();
  // Every X number of seconds (interval = 10 seconds) 
  // it publishes a new MQTT message

  if ((WiFi.status() != WL_CONNECTED) && (currentMillis - previousMillis >=interval)) {
    Serial.print(millis());
    Serial.println("Reconnecting to WiFi...");
    Serial.println(WiFi.localIP());
  //  Serial.end();  currently not used
    //Alternatively, you can restart your board
    ESP.restart();
    //Serial.println(WiFi.RSSI());
    previousMillis = currentMillis;
  }

  if (currentMillis - previousMillis >= interval) {
    // Save the last time a new reading was published
    previousMillis = currentMillis;

   Serial.println("<Nano read CTs>"); //ordinare a Nano di leggere i CT
    recvWithStartEndMarkers();
    if (newData == true) {

String rms1 = getValue(myString, ',', 1);
String rms2 = getValue(myString, ',', 2);
String rms3 = getValue(myString, ',', 3);
 
Irms1 = rms1.toFloat();
Irms2 = rms2.toFloat();
Irms3 = rms3.toFloat();

  myString = "";

// end new code
    }
 newData = false;


  sensors.requestTemperatures(); // Send the command to get temperatures
  
  T1 = sensors.getTempC(sensor1);
 
  T2 = sensors.getTempC(sensor2);
  
  T3 = sensors.getTempC(sensor3);
  
  T4 = sensors.getTempC(sensor4);
  
  T5 = sensors.getTempC(sensor5);
  

uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB_TEMP"/T1", 1, true, String(T1).c_str());                            
    // Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub1);
    // Serial.printf("Message: %.2f \n", T1);

uint16_t packetIdPub2 = mqttClient.publish(MQTT_PUB_TEMP"/T2", 1, true, String(T2).c_str());                            
    // Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub2);
    // Serial.printf("Message: %.2f \n", T2);    

uint16_t packetIdPub3 = mqttClient.publish(MQTT_PUB_TEMP"/T3", 1, true, String(T3).c_str());                            
    // Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub3);
    // Serial.printf("Message: %.2f \n", T3);

uint16_t packetIdPub4 = mqttClient.publish(MQTT_PUB_TEMP"/T4", 1, true, String(T4).c_str());                            
    // Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub4);
    // Serial.printf("Message: %.2f \n", T4);

uint16_t packetIdPub5 = mqttClient.publish(MQTT_PUB_TEMP"/T5", 1, true, String(T5).c_str());                            
    // Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub5);
    // Serial.printf("Message: %.2f \n", T5);

uint16_t packetIdPub6 = mqttClient.publish(MQTT_PUB_TEMP"/Irms1", 1, true, String(Irms1).c_str());                            
    // Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub6);
    // Serial.printf("Message: %.2f \n", Irms1);    

uint16_t packetIdPub7 = mqttClient.publish(MQTT_PUB_TEMP"/Irms2", 1, true, String(Irms2).c_str());                            
    // Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub7);
    // Serial.printf("Message: %.2f \n", Irms2);  

uint16_t packetIdPub8 = mqttClient.publish(MQTT_PUB_TEMP"/Irms3", 1, true, String(Irms3).c_str());                            
    // Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub8);
    // Serial.printf("Message: %.2f \n", Irms3);      

  }
}

This is the NANO code:

#include <Arduino.h>
//#include <SoftwareSerial.h>

#include "EmonLib.h"             // Include Emon Library
EnergyMonitor emon1;             // Create an instance
EnergyMonitor emon2;             // Create an instance
EnergyMonitor emon3;             // Create an instance
 
//SoftwareSerial nodemcuserial(10,11); //rx-tx pins --> connect to tx-rx

String cdata;

unsigned long interval = 10000;           //Time between readings
unsigned long previousMillis = 0; //will store last time LED was updated


// funzioni per ricevere la richiesta di lettura da NodeMCU
const byte numChars = 32;
char receivedChars[numChars];

boolean 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 showNewData() {
    if (newData == true) {
        //Serial.print("This just in ... ");
        //Serial.println(receivedChars);
        newData = false;
    }
}

// the setup function runs once when you press reset or power the board
void setup() {
  //analogReference(EXTERNAL); // use AREF for reference voltage
    Serial.begin(9600);           //  setup serial
    //nodemcuserial.begin(9600);

  //emon1.voltage(3, 234.26, 1.7);  // Voltage: input pin, calibration, phase_shift
  emon1.voltage(1, 205.0, 1.68);  // Voltage: input pin, calibration, phase_shift
  emon1.current(3, 30);       // Current: input pin, calibration.
  emon2.voltage(1, 205.0, 1.68);  // Voltage: input pin, calibration, phase_shift
  emon2.current(4, 30);       // Current: input pin, calibration.
  emon3.voltage(1, 205.0, 1.68);  // Voltage: input pin, calibration, phase_shift
  emon3.current(5, 30);       // Current: input pin, calibration.
}

// the loop function runs over and over again forever
void loop() {

//  unsigned long currentMillis = millis();
//   if(currentMillis - previousMillis > interval) {
//     previousMillis = currentMillis; //save the last time you blinked the LED

recvWithStartEndMarkers();
if(newData == true){
showNewData();

  emon1.calcVI(20,2000);         // Calculate all. No.of wavelengths, time-out
  //emon1.serialprint();           // Print out all variables
  emon2.calcVI(20,2000);         // Calculate all. No.of wavelengths, time-out
 // emon2.serialprint();           // Print out all variables
  emon3.calcVI(20,2000);         // Calculate all. No.of wavelengths, time-out
  // emon3.serialprint();           // Print out all variables


  double P1 = emon1.realPower;// Calculate Irms only
  //if (Irms1 < 0.05) Irms1=0.0;
  //Serial.print(Irms1*230.0);           // Apparent power
  //Serial.print(" ");
  //Serial.println(Irms1);             // Irms

    double P2 = emon2.realPower;  // Calculate Irms only
   // if (Irms2 < 0.05) Irms2=0.0;
 //Serial.print(Irms2*230.0);           // Apparent power
  //Serial.print(" ");
  //Serial.println(Irms2);             // Irms

      double P3 = emon3.realPower;  // Calculate Irms only
     //     if (Irms3 < 0.05) Irms3=0.0;
  //Serial.print(Irms3*230.0);           // Apparent power
  //Serial.print(" ");
  //Serial.println(Irms3);             // Irms

  // emon1.calcVI(20,2000);         // Calculate all. No.of wavelengths, time-out
   // emon1.serialprint();           // Print out all variables
  //   emon2.calcVI(20,2000);         // Calculate all. No.of wavelengths, time-out
  // emon2.serialprint();           // Print out all variables
  //   emon3.calcVI(20,2000);         // Calculate all. No.of wavelengths, time-out
  // emon3.serialprint();           // Print out all variables

cdata = cdata + "<" + "," + P1 + "," + P2 + "," + P3 +"*" ;
Serial.print(cdata); 
Serial.println();
//nodemcuserial.println(cdata);
//nodemcuserial.println();
cdata = ""; 

}
}

Thanks for your help!

Your post was MOVED to its current location as it is more suitable.

Figured out the issue, I'll share it just in case.
Basically it had nothing to do with TX/RX and serial.

The NodeMCU sketch I am using makes us of the great WifiManager by Tzapu.
In case of lost WiFi connection, I use the "AutoConnect" feature so that the NodeMCU automatically reconnects to the network.

There's an optional parameter for WiFiManager, called setConnectTimeout(). This parameters sets a timeout before putting the NodeMCU back to AP mode.

You just need to set a timeout (in seconds) to make sure the NodeMCU has enough time to reconnect to the WiFi network, which means it waitis for WiFi to become available before going back to AP mode. In my case the "setConnectTimeout()" was set to default (I think it is 0 seconds), so the NodeMCU unit was going back to AP mode immediately, while the WiFi network became available a couple of minutes after a power loss.

Call the function before calling autoConnect.

    wm.setConnectTimeout(7200);
    res = wm.autoConnect("APnetwork","APpassword"); // password protected ap

This worked out for me

Hi, I see that you used the NodeMCU for your project. Did you find a way to send data from the nodemcu to other devices? I can only receive data with it and would like two way communication. Thanks in advance!

Hi,
yes, there is a two-way communication in my project.
In fact in the loop I have the NodeMCU printing out to serial the following code:

 Serial.println("<Nano read CTs>");

On the other hand, in the Arduino Nano I have implemented the receive with start and end markers function:

void recvWithStartEndMarkers() {
[....]
    char startMarker = '<';
    char endMarker = '>';

When the Arduino Nano gets a serial message that begins with "<" and ends with">", it will do something.

I recommend you to read this post about serial input basics: link

I spent a few hours in trying and testing, but two way "on demand" communication now works fine.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.