Arduino Uno crashing

Hi all,

I am working on a project with two Arduino uno's they both have an dragino lora shield attached.
The main goal is to create a server and client.

On the client side I have an battery powerbooster attached where a Lipo battery of 3.7V is connected. On port A2 I measure the voltage of the Lipo battery
On port 3&4 I have a Ultrasonic distance sensor connected.

Readingout the value from the sensor is working only the cyle time of the loop needs to be short 100ms.

On the server side I have an 0.91" Oled Display connected this is working with I2c on port A4 and A5 with a powersupply of 3.3V.

Everything works like it should be, only the uno of the serverside is crashing everytime after a few
seconds. This happens randomly sometimes it works longer.
And I can't find the point where its going wrong.


Source Client

#include <SoftwareSerial.h>
#include <SPI.h>
#include <Wire.h>

#include <RH_RF95.h>
RH_RF95 rf95;


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

unsigned char data[4]={};
float sensorValue;
float rawValue;
int min_width = -6;
int num_digits_after_decimal = 2;
char databufSensor[10];//20 bytes
char databufBatvolt[10];


void setup() {
  mySerial.begin(9600); 
  Serial.begin(9600);

  if (!rf95.init())
  {
    Serial.println("init failed");
  }
  
  rf95.setFrequency(868.0);
  //rf95.setTxPower(23, false);
}

void loop() {
  do{
    for(int i=0;i<4;i++)
    {
      data[i]=mySerial.read();
    }
  }while(mySerial.read()==0xff);

  mySerial.flush();
  
  if(data[0]==0xff)
  {
    int sum;
    sum = 0;
    sum=(data[0]+data[1]+data[2])&0x00FF;
    if(sum==data[3])
    {  
      rawValue=(data[1]<<8)+data[2];
      
      if(rawValue>280)
      {
        sensorValue = (rawValue/10);
      }
      else
      {
        sensorValue = 0.0;       
      }
    }
  }

  memset(databufSensor, 0, sizeof(databufSensor));
  dtostrf(sensorValue,min_width,num_digits_after_decimal,databufSensor);
  int batValue = analogRead(A2);
  float batvolt= batValue * (5.0 / 1024.0);
  memset(databufBatvolt, 0, sizeof(databufBatvolt));
  dtostrf(batvolt,min_width,num_digits_after_decimal,databufBatvolt);
  
  uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
  uint8_t len = sizeof(buf);
  if (rf95.waitAvailableTimeout(100))
  {
    if (rf95.recv(buf, &len))
    { 
      int dataLength;
      String Request = (char*)buf;
      if (Request == "C1") {
        String data = String(databufSensor)+';'+String(databufBatvolt);
        int dataLength = data.length();dataLength++;
        uint8_t total[dataLength];
        memset(total, 0, sizeof(total));
        data.toCharArray(reinterpret_cast<char *>(total),dataLength);
        rf95.send(total, dataLength);
        rf95.waitPacketSent(); 
        Request = "";       
      }
    }
  }
}

Source Server

#include <SPI.h>
#include <RH_RF95.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

RH_RF95 rf95(10,2);

unsigned long millisBefore;
String voltage;
String sensorPosition;
int turn = 1;
String recievedData;
float batvolt;
float percentage;

// On an arduino UNO:       A4(SDA), A5(SCL)
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET     5 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup()
{
  Serial.begin(9600);
  while (!Serial); // Wait for serial port to be available
  if (!rf95.init())
    Serial.println("init failed");
  rf95.setFrequency(868.0); 
  //rf95.setTxPower(23, false);

  //SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
     Serial.println(F("SSD1306 allocation failed"));}

  
}

void loop()
{ 
  
  Communication();
}

void Communication()
{
    if (millis() - millisBefore < 500)
    {
        if (turn == 1) 
        {
        display.clearDisplay();
        display.setTextSize(1);             // Normal 1:1 pixel scale
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.setCursor(0,0);  

        sendRequest("C1");
        recievedData = waitForAnswer();

        int posDelimiter = recievedData.indexOf(';');
        sensorPosition = recievedData.substring(0,posDelimiter);
        voltage = recievedData.substring(posDelimiter + 1 ,recievedData.length());

        batvolt = voltage.toFloat();
        percentage = ((batvolt-3)/0.7)*100;
        
        display.println("   "+voltage + "V " + percentage + "%");
        display.setTextSize(2);// Draw 2X-scale text
        display.println(" "+sensorPosition + "cm");
        display.display();
        turn = 2;
        }
    }else if ((millis() - millisBefore > 500) && (millis() - millisBefore < 1000)) {
        if (turn == 2) 
        {
        millisBefore = millis();
        turn = 1;
        }
    }
}

void sendRequest(String request) 
{
  unsigned int dataLength = request.length(); dataLength ++;
  uint8_t total[dataLength];
  memset(total, 0, sizeof(total));
  request.toCharArray(reinterpret_cast<char *>(total), dataLength);
  rf95.send(total, dataLength);
  rf95.waitPacketSent();
}

String waitForAnswer() {
  //Now wait for a reply
  uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
  memset(buf, 0, sizeof(buf));
  uint8_t len = sizeof(buf);
  if (rf95.waitAvailableTimeout(500))
  {
    if (rf95.recv(buf, &len))
    {
      return (char*)buf;
    }
    else
    {
      Serial.println("recv failed");
    }
  }
}

Client

Server

The Adafruit display allocates a buffer at runtime. You have far less free memory than you think. Adding String objects to the mix likely makes things worse.

Even if I remove the code that has to do with the Oled display.

I still get the same problem.

If I google about the String class. I get exactly the same answers to not use String class.
instead use cstring whats the diffrence. and how to implement.

I removed the Oled display.

#include <SPI.h>
#include <RH_RF95.h>
//#include <Wire.h>
//#include <Adafruit_GFX.h>
//#include <Adafruit_SSD1306.h>

RH_RF95 rf95(10,2);

unsigned long millisBefore;
String voltage;
String sensorPosition;
int turn = 1;
String recievedData;
float batvolt;
float percentage;

//// On an arduino UNO:       A4(SDA), A5(SCL)
//#define SCREEN_WIDTH 128 // OLED display width, in pixels
//#define SCREEN_HEIGHT 32 // OLED display height, in pixels
//#define OLED_RESET     5 // Reset pin # (or -1 if sharing Arduino reset pin)
//#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
//
//Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup()
{
  Serial.begin(9600);
  while (!Serial); // Wait for serial port to be available
  if (!rf95.init())
    Serial.println("init failed");
  rf95.setFrequency(868.0); 
  //rf95.setTxPower(23, false);

//  //SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
//  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
//     Serial.println(F("SSD1306 allocation failed"));}

  
}

void loop()
{ 
  
  Communication();
}

void Communication()
{
    if (millis() - millisBefore < 499)
    {
        if (turn == 1) 
        {
//        display.clearDisplay();
//        display.setTextSize(1);             // Normal 1:1 pixel scale
//        display.setTextColor(SSD1306_WHITE);        // Draw white text
//        display.setCursor(0,0);  

        sendRequest("C1");
        recievedData = waitForAnswer();

        int posDelimiter = recievedData.indexOf(';');
        sensorPosition = recievedData.substring(0,posDelimiter);
        voltage = recievedData.substring(posDelimiter + 1 ,recievedData.length());
        Serial.println(sensorPosition);
        Serial.println(voltage);
        Serial.println(millisBefore);

//        batvolt = voltage.toFloat();
//        percentage = ((batvolt-3)/0.7)*100;
//        
//        display.println("   "+voltage + "V " + percentage + "%");
//        display.setTextSize(2);// Draw 2X-scale text
//        display.println(" "+sensorPosition + "cm");
//        display.display();
        turn = 2;
        }
    }else if ((millis() - millisBefore > 499) && (millis() - millisBefore < 999)) {
        if (turn == 2) 
        {
        millisBefore = millis();
        turn = 1;
        }
    }
}

void sendRequest(String request) 
{
  unsigned int dataLength = request.length(); dataLength ++;
  uint8_t total[dataLength];
  memset(total, 0, sizeof(total));
  request.toCharArray(reinterpret_cast<char *>(total), dataLength);
  rf95.send(total, dataLength);
  rf95.waitPacketSent();
}

String waitForAnswer() {
  //Now wait for a reply
  uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
  memset(buf, 0, sizeof(buf));
  uint8_t len = sizeof(buf);
  if (rf95.waitAvailableTimeout(499))
  {
    if (rf95.recv(buf, &len))
    {
      return (char*)buf;
    }
    else
    {
      Serial.println("recv failed");
    }
  }
}

On the Serial monitor I get this.

23:31:15.329 -> 28000
23:31:15.845 -> 112.10
23:31:15.845 -> 4.14  
23:31:15.845 -> 28500
23:31:16.359 -> 112.10
23:31:16.359 -> 4.13  
23:31:16.359 -> 29000
23:31:16.823 -> 112.10
23:31:16.823 -> 4.14  
23:31:16.823 -> 29500
23:31:17.341 -> 112.40
23:31:17.341 -> 4.15  
23:31:17.341 -> 30000
23:31:17.855 -> 112.40
23:31:17.855 -> 4.12  
23:31:17.855 -> 30500
23:31:18.323 -> 112.10
23:31:18.323 -> 4.11  
23:31:18.323 -> 31000
23:31:19.261 -> 
23:31:19.261 -> 
23:31:19.261 -> 31500
23:31:19.356 -> 112.10
23:31:19.356 -> 4.11  
23:31:19.356 -> T
23:31:19.871 -> ⸮
23:31:19.871 -> 4.15  
23:31:19.871 -> H
23:31:20.385 -> ⸮

The millisBefore gets strange values

String objects can fragment your heap and odd behavior or crashes may result. Grab a copy of the freeMemory function from Adafruit and print that too. I suspect you'll find that the number gradually shrinks, which would be a sign that fragmentation is indeed taking place.

Perfect advice. :sunglasses:

A significant amount of Arduino code is involved in the creation of the String class. Were the Arduino team not confident that String performed as intended, it would be depreciated. Of course, this is my option and is not universally shared :broken_heart:

Many years ago, things were 'buggy' but I am an old-timer that believes if you need String then you should use it, but carefully. One of the careful things you could do is to set aside the maximum String length.

Reference: String.reserve()
https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/reserve/

Some things are wrong here

  1. Not all of your code returns something
  2. You return a pointer to a char array, you're not returning a String.

I have it working, thank you guys for the support.

its indeed :exploding_head: that stuppid String class.

#include <SPI.h>
#include <RH_RF95.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
//#include <MemoryFree.h>
//#include <pgmStrToRAM.h>

RH_RF95 rf95(10,2);

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 499;
float percentage;
float voltage;
float sensorValue;
int min_width = -6;
int num_digits_after_decimal = 2;
char databufSensor[10];
char databufBatvolt[10];
char databufPercentage[10];

//// On an arduino UNO:       A4(SDA), A5(SCL)
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET     5 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup()
{
Serial.begin(9600);
  while(!Serial);

  //Serial.println(getPSTR("Old way to force String to Flash")); // forced to be compiled into and read   
  //Serial.println(F("New way to force String to Flash")); // forced to be compiled into and read   
  //Serial.println(F("Free RAM = ")); //F function does the same and is now a built in library, in IDE > 1.0.0
  //Serial.println(freeMemory());  // print how much RAM is available in bytes.

  if (!rf95.init())
    Serial.println("init failed");
  rf95.setFrequency(868.0); 
  //rf95.setTxPower(23, false);

  //SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
     Serial.println(F("SSD1306 allocation failed"));}

  startMillis = millis();
}

void loop()
{ 
  currentMillis = millis(); 

  if (currentMillis - startMillis >= period)  //test whether the period has elapsed
  {

    uint8_t total[2];
    total[0] = 'C';
    total[1] = '1';
    rf95.send(total, 2);
    rf95.waitPacketSent();


    //Now wait for a reply
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);
    if (rf95.waitAvailableTimeout(499))
    {
      if (rf95.recv(buf, &len))
      {
        size_t bufflen = sizeof(buf);
        char str[bufflen];
        memcpy( str, buf, bufflen );
        str[bufflen];  // 'str' is now a string

        float recievedData[2];
        //Seperate recieved data 
        char* command = strtok(str, ";");
        for (int i = 0; i <= 1; i++)
        {
          recievedData[i] = atof(command);
          command = strtok(0, ";");
        }
        sensorValue = recievedData[0];
        voltage = recievedData[1];
        percentage = ((recievedData[1]-3)/0.7)*100;
      }
      else
      {
        Serial.println("recv failed");
      }
    }

    dtostrf(sensorValue,min_width,num_digits_after_decimal,databufSensor);
    dtostrf(voltage,min_width,num_digits_after_decimal,databufBatvolt);
    dtostrf(percentage,min_width,num_digits_after_decimal,databufPercentage);
    
    Serial.println(databufSensor);
    Serial.println(databufBatvolt);
    Serial.println(databufPercentage);

    
    display.clearDisplay();
    display.setTextSize(1);             // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.setCursor(30,0);  
    display.println(databufBatvolt);
    display.setTextSize(2);// Draw 2X-scale text
    display.println(databufSensor);
    display.display();
    
    startMillis = currentMillis;
  }
}

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