Serial Communication between Arduino Nano and ESP32 using ArduinoJson

Hey all. I am having some trouble with a project of mine. It's a little weather station with Davis Instruments sensor connected to an Arduino Nano and a BME280 connected to an ESP32. I am using the ArduinoJSON Library to serialize all the Davis Instruments values, send them to the ESP32 and then the ESP32 deserializes the values and posts them to an MQTT server. I am getting an error in the serial console of the ESP32 and I have no idea what it means or how to solve it. I would really appreciate some help.

The error that shows in the serial console:

Stack smashing protect failure!

abort() was called at PC 0x400e1bd8 on core 1

Backtrace:0x40083e2d:0x3ffb25600x4008bcf1:0x3ffb2580 0x40090f39:0x3ffb25a0 0x400e1bd8:0x3ffb2620 0x400d2f43:0x3ffb2640 0x400d30ca:0x3ffb2690 0x400d3812:0x3ffb26b0 0x400d92fd:0x3ffb2820 

ELF file SHA256: 0000000000000000

After it displays this, it reboots, waits a few seconds and does it again. I've tried with

The code for the Arduino Nano:

#include "TimerOne.h"          // Timer Interrupt set to 2.5 second for read sensors
#include <math.h>
#include <util/atomic.h>
#include <SoftwareSerial.h>
#include <ArduinoJson.h>

SoftwareSerial nodemcu(5, 6);

#define WindSensorPin (3)     // The pin location of the anemometer sensor
#define WindVanePin (A4)      // The pin the wind vane sensor is connected to
#define VaneOffset 0         // define the anemometer offset from magnetic north
#define RainBucketPin (2)     // Pin of the bucket

int VaneValue;       // raw analog value from wind vane
int Direction;       // translated 0 - 360 direction

volatile long LastValue = 0;
volatile bool IsSampleRequired = false;       // this is set true every 2.5s. Get wind speed
volatile unsigned int  TimerCount = 0;    // used to determine 2.5sec timer count
volatile unsigned long Rotations = 0;     // cup rotation counter used in interrupt routine
volatile unsigned long ContactRotationTime;  // Timer to avoid contact bounce in interrupt routine
volatile unsigned long Tippings = 0;
volatile unsigned long ContactTippingTime;  // Timer to avoid contact bounce in interrupt routine
volatile unsigned long CalDirection = 0;    // converted value with offset applied
volatile uint32_t tl = 0;

String getHeading(int direction);
String getWindStrength(float speed);

void setup() {

  LastValue = 0;
  IsSampleRequired = false;
  TimerCount = 0;

  Rotations = 0; // Set Rotations to 0 ready for calculations
  ContactRotationTime = millis();

  Tippings  = 0;
  ContactTippingTime = millis();

  Serial.begin(9600);
  nodemcu.begin(9600);

  delay(1000);

  Serial.println("Program started");

  pinMode(WindSensorPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(WindSensorPin), isr_rotation, LOW);

  pinMode(RainBucketPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(RainBucketPin), isr_tipping, LOW);

  tl = millis();

  // Setup the timer intterupt
  Timer1.initialize(500000);
  Timer1.attachInterrupt(isr_timer);
}

void loop() {

  uint32_t tn = millis();
  uint32_t T = 0;

  getWindDirection();
  // Only update the display if change greater than 5 degrees.
  if (abs(CalDirection - LastValue) > 5)
  {
    LastValue = CalDirection;
  }

  if (IsSampleRequired)
  {

    float WindSpeed = 0.0;
    float TotalRain = 0.0;
    float WindDirection = 0.0;

    // Using formula V=P(2.25/T)
    tn = millis();
    T = abs(tn - tl) / 1000;
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
      WindSpeed = Rotations * (2.25 / T) * 1.609344;
      TotalRain  = Tippings * 0.2;
      WindDirection = CalDirection;
    }

    Serial.print("rain:");
    Serial.println(TotalRain);
    Serial.print("windspeed:");
    Serial.println(WindSpeed);
    Serial.print("winddir:");
    Serial.println(WindDirection);
    Serial.print("windheading:");
    Serial.println(getHeading(WindDirection));
    Serial.print("windstrength:");
    Serial.println(getWindStrength(WindSpeed));

    StaticJsonDocument<96> doc;

    doc["rain"] = TotalRain;
    doc["windspeed"] = WindSpeed;
    doc["winddir"] = WindDirection;
    doc["windheading"] = getHeading(WindDirection);
    doc["windstrength"] = getWindStrength(WindSpeed);

    serializeJson(doc, nodemcu);

    noInterrupts();
    Tippings  = 0;
    Rotations = 0;
    IsSampleRequired = false;
    tl = tn;
    interrupts();
  }
}

// isr routine fr timer interrupt
void isr_timer() {

  TimerCount++;

  if (TimerCount == 5)
  {
    IsSampleRequired = true;
    TimerCount = 0;
  }
}

// This is the function that the interrupt calls to increment the rotation count
void isr_rotation ()   {

  if (((millis() - ContactRotationTime) > 15)  && (digitalRead(WindSensorPin) == LOW)) {  // debounce the switch contact.
    Rotations++;
    ContactRotationTime = millis();
  }

}

void isr_tipping ()   {

  if (((millis() - ContactTippingTime) > 15) && (digitalRead(RainBucketPin) == LOW)) {  // debounce the switch contact
    Tippings++;
    ContactTippingTime = millis();
  }
}


// Get Wind Direction
void getWindDirection() {

  VaneValue = analogRead(WindVanePin);
  Direction = map(VaneValue, 0, 1023, 0, 360);
  CalDirection = Direction + VaneOffset;

  if (CalDirection > 360)
    CalDirection = CalDirection - 360;

  if (CalDirection < 0)
    CalDirection = CalDirection + 360;

}


// Converts compass direction to heading
String getHeading(int direction) {
  if (direction < 22)
    return "N";
  else if (direction < 67)
    return "NE";
  else if (direction < 112)
    return "E";
  else if (direction < 157)
    return "SE";
  else if (direction < 212)
    return "S";
  else if (direction < 247)
    return "SW";
  else if (direction < 292)
    return "W";
  else if (direction < 337)
    return "NW";
  else
    return "N";
}

// converts wind speed to wind strength https://windows2universe.org/earth/Atmosphere/wind_speeds.html
String getWindStrength(float speed) {
  if (speed < 1)
    return "Calm";
  else if (speed >= 1 && speed < 5)
    return "Light Air";
  else if (speed >= 5 && speed < 11)
    return "Light Breeze";
  else if (speed >= 11 && speed < 19)
    return "Gentle Breeze";
  else if (speed >= 19 && speed < 28)
    return "Moderate Breeze";
  else if (speed >= 28 && speed < 38)
    return "Fresh Breeze";
  else if (speed >= 38 && speed < 61)
    return "Strong Breeze";
  else if (speed >= 61 && speed < 74)
    return "Fresh Gale";
  else if (speed >= 74 && speed < 88)
    return "Fresh Gale";
  else
    return "RUN";
}

Finally, code for ESP32:

#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <SoftwareSerial.h>
#include <ArduinoJson.h>

SoftwareSerial arduino(26, 27);

double SEALEVELPRESSURE_HPA = 1013.25;

const char *host = "Home Weather Station";
const char *ssid = "xxx";
const char *password = "xxx";
const char *mqtt_broker = "xxx";

WiFiClient espClient;
PubSubClient client(espClient);

Adafruit_BME280 bme;

const int mqtt_port = 1883;

long lastMsg = 0;
long lastMsg2 = 0;

float a = 17.625;
float b = 243.04;

float bmeTemp;
float bmeHum;
float bmePres;
float bmeAlt;
float heatindex;

float rain;
float windspeed;
int winddir;
const char* windheading ;
const char* windstrength;

double dewpoint;

void setup()
{
  Serial.begin(9600);
  arduino.begin(9600);
  Wire.begin();
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED)
  {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  client.setServer(mqtt_broker, mqtt_port);
  while (!client.connected())
  {
    String client_id = "HomeWeatherStation";
    client_id += String(WiFi.macAddress());
    Serial.printf("The client %s connects to the MQTT broker\n", client_id.c_str());
    if (client.connect(client_id.c_str()))
    {
      Serial.println("MQTT broker connected");
    }
    else
    {
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
    }
  }
  if (!bme.begin(0x76))
  {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1)
      ;
  }
}

void loop()
{
  reconnect();
  client.loop();

  long now = millis();

  if (now - lastMsg > 20000)
  {
    lastMsg = now;
    readBME();
  }

  long now2 = millis();

  if ((now2 - lastMsg2 > 2500) && (arduino.available() > 0))
  {
    StaticJsonDocument<192> doc;

    DeserializationError error = deserializeJson(doc, arduino);

    if (error) {
      Serial.print("deserializeJson() failed: ");
      Serial.println(error.c_str());
      return;
    }

    float rain = doc["rain"];
    float windspeed = doc["windspeed"];
    int winddir = doc["winddir"];
    const char* windheading = doc["windheading"];
    const char* windstrength = doc["windstrength"];

    Serial.println("JSON Object Recieved");
    Serial.print("Recieved Rain:  ");
    Serial.println(rain);
    Serial.print("Recieved Wind Speed:  ");
    Serial.println(windspeed);
    Serial.print("Recieved Wind Direction:  ");
    Serial.println(winddir);
    Serial.print("Recieved Wind Heading:  ");
    Serial.println(windheading);
    Serial.print("Recieved Wind Strength:  ");
    Serial.println(windheading);

    publisharduino();
  }
}

void reconnect()
{
  while (!client.connected())
  {
    String client_id = "LanHomeWeatherStation";
    client_id += String(WiFi.macAddress());
    Serial.printf("The client %s connects to the MQTT broker\n", client_id.c_str());
    if (client.connect(client_id.c_str()))
    {
      Serial.println("MQTT broker connected");
    }
    else
    {
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
    }
  }
  while (WiFi.waitForConnectResult() != WL_CONNECTED)
  {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
}

void readBME()
{
  bmeTemp = bme.readTemperature();
  Serial.print("Temperature = ");
  Serial.print(bmeTemp);
  Serial.println("*C");

  bmePres = (bme.readPressure() / 100.0F);
  Serial.print("Pressure = ");
  Serial.print(bmePres);
  Serial.println("hPa");

  bmeAlt = bme.readAltitude(SEALEVELPRESSURE_HPA);
  Serial.print("Approx. Altitude = ");
  Serial.print(bmeAlt);
  Serial.println("m");

  bmeHum = bme.readHumidity();
  Serial.print("Humidity = ");
  Serial.print(bmeHum);
  Serial.println("%");

  publishbme();
  calcdewpoint();
  calcheatindex();

  Serial.println();
}

void calcdewpoint()
{
  double gamma = log(bmeHum / 100) + ((a * bmeTemp) / (b + bmeTemp));
  dewpoint = b * gamma / (a - gamma);

  char dpString[8];
  dtostrf(dewpoint, 1, 2, dpString);
  client.publish("home/weather/dewpoint", dpString);
}

void calcheatindex()
{
  double c1 = -42.379;
  double c2 = -2.04901523;
  double c3 = -10.14333127;
  double c4 = -0.22475541;
  double c5 = -6.83783 * (10 ^ 3);
  double c6 = -5.481717 * (10 ^ 2);
  double c7 = -1.22874 * (10 ^ 3);
  double c8 = 8.5282 * (10 ^ 4);
  double c9 = -1.99 * (10 ^ 6);

  float tempF = (bmeTemp * 1.8) + 32;
  float heatindexf = c1 + (c2 * tempF) + (c3 * bmeHum) + (c4 * tempF * bmeHum) + (c5 * (tempF * tempF)) + (c6 * (bmeHum * bmeHum)) + (c7 * (tempF * tempF) * bmeHum) + (c8 * tempF * (bmeHum * bmeHum)) + (c9 * (tempF * tempF) * (bmeHum * bmeHum));
  heatindex = (heatindexf - 32) * 0.5556;

  char hiString[8];
  dtostrf(heatindex, 1, 2, hiString);
  client.publish("home/weather/heatindex", hiString);
}

void publishbme()
{
  char tempString[8];
  dtostrf(bmeTemp, 1, 2, tempString);
  client.publish("home/weather/temp", tempString);

  char humString[8];
  dtostrf(bmeHum, 1, 2, humString);
  client.publish("home/weather/hum", humString);

  char presString[8];
  dtostrf(bmePres, 1, 2, presString);
  client.publish("home/weather/pres", presString);

  char altString[8];
  dtostrf(bmeAlt, 1, 2, altString);
  client.publish("home/weather/alt", altString);
}

void publisharduino()
{
  char rainString[8];
  dtostrf(rain, 1, 2, rainString);
  client.publish("home/weather/rain", rainString);

  char windspeedString[8];
  dtostrf(windspeed, 1, 2, windspeedString);
  client.publish("home/weather/windspeed", windspeedString);

  char winddirString[8];
  dtostrf(winddir, 1, 2, winddirString);
  client.publish("home/weather/winddir", winddirString);

  client.publish("home/weather/windheading", windheading);

  client.publish("home/weather/windstrength", windstrength);
}

I'd really appreciate some help. Also, this is my first time posting in this forum, so please let me know if there is anything I can do to get the most out of posting here.

Welcome to the Arduino help forum. Thank you for providing your code nicely formatted.

I did a search on your error:

ESP32 + Arduino +"Stack smashing protect failure!"

Within the results returned, I found this thread suggesting "S"tring could be the issue:

Personally, I would not use JSON library to move between two microcontrollers; just move a struct, more efficient.
How to send and receive struct data packet over serial - Using Arduino / Programming Questions - Arduino Forum

Thank you so much, I appreciate the response. I must be honest, I have never used structs before but I will take a look at it.

I was just thinking, it is possible that it could be a result of the different logic levels? I am not sure how that works with serial communications, but I mean they operate at different logic levels and giving a 5V signal to the 3v3 ESP might cause this? Anyone know?

You will, at a minimum, need resistors on the Arduino 5V Tx line:

How to Level Shift 5V to 3.3V | Random Nerd Tutorials

Did the OP put the backtrace info into the ESP Exception Decoder? Did the OP post the ESP Exception Decoders results so we can see them?

Softwareserial is known to NOT work with the ESP32. Why use software serial on a esp32 when the esp32 has 3 useable hardware serial ports?

I've ordered some logic converters. Thought I had, but I will see if that works.

Sorry, I was not familiar with this, but I have done it now. Here are the results:

0x40083e2d: esp_timer_start_periodic at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_timer/src/esp_timer.c line 173
0x4008bcf1: multi_heap_malloc at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/heap/multi_heap_poisoning.c line 220
0x400e1bd8: _svfprintf_r at /builds/idf/crosstool-NG/.build/HOST-i686-w64-mingw32/xtensa-esp32-elf/src/newlib/newlib/libc/stdio/vfprintf.c line 1752
0x400d2f43: nvs::Page::eraseEntryAndSpan(unsigned int) at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/nvs_flash/src/nvs_page.cpp line 414
0x400d30ca: nvs::Page::findItem(unsigned char, nvs::ItemType, char const*, unsigned int&, nvs::Item&, unsigned char, nvs::VerOffset) at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/nvs_flash/src/nvs_page.cpp line 908
0x400d3812: nvs::PageManager::activatePage() at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/nvs_flash/src/nvs_pagemanager.cpp line 208
0x400d92fd: uart_pattern_queue_update at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/driver/uart.c line 466

I completely forgot about the hardware serial. I will make use of the ESP32's hardware serial and see if it makes a difference.

I see

0x400d92fd: uart_pattern_queue_update at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/driver/uart.c line 466

something does not like the UART.

Alright so an update.

Making use of the ESP32's hardware serial fixed communication errors. I am actually successfully getting the JSON files and using them. For a while the ESP32 was still rebooting and popping out different backtraces. I made use of a different method of converting the floats to const chars for the MQTT publishing like this:
I replaced this:

char dpString[8];
dtostrf(dewpoint, 1, 2, dpString);
client.publish("home/weather/dewpoint", dpString);

with this:

client.publish("home/weather/dewpoint", String(dewpoint).c_str());

This fixed the rebooting issue. The only issue now: every now and then, the JSON file comes through without errors, but it is blank. I think this might be due to the 5V and 3V3 logic differences, but I'll only get the converters in the week and will update then.

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