1

I'm using an Arduino Nano for programing an automatic hand sanitizer that has a thermometer. But after running for some time the board freezes and only restarting can solve the issue. While checking the serial monitor it suddenly stops working.

The program includes reading distance from an ultrasonic sensor and temperature from an IR thermometer (distance and ambient temperature are read continuously and the serial monitor will constantly display values). At random when the temperature is read and is displayed on the OLED screen, the board freezes (board freezes only at this time) and restarting can make it work 1 more time and then freezes again.

Question 1 Is it something to do with the code or is it a hardware issue?

Question 2 Does a clone version of NANO cause such problem? (I'm using a clone version)

This is my code:

#include <Wire.h>
#include <Adafruit_MLX90614.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

Adafruit_MLX90614 mlx = Adafruit_MLX90614();
Adafruit_SSD1306 display(-1);
//Adafruit_SSD1306 display(128, 64, &Wire, -1);

#define trigPin 12 // Trigger pin
#define echoPin 11 // echo pin

float roomTemp; // ambient temperature
float objectTemp, stemp; // object temperature

int readcount = 0;
float threshold= 3.5 ;

int maximumRange = 15; // Maximum range needed
int minimumRange = 3; // Minimum range needed
long duration, distance; // Duration used to calculate distance
int dtime;
unsigned long rememTime;

// motor coding
int set_time;
float distance_cm;
unsigned long ultra_time;
int set_cm = 20;
int motor = 2; // Output for Motor drive
int flag = 0;

void setup() {
  Serial.begin(9600);// initialize serial communication at 9600 bits per second:

  pinMode (trigPin, OUTPUT);
  pinMode (echoPin, INPUT);
  pinMode(motor, OUTPUT);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C) ;
  delay(200);
  display.clearDisplay();
  display.setTextColor(WHITE);
}

void loop() {
  // temperature sensor coding
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
 
  duration = pulseIn(echoPin, HIGH);
  //Calculate the distance (in cm) based on the speed of sound.
  distance= duration * 0.034/2;

  // reading object and ambient  temperature
  objectTemp = threshold + mlx.readObjectTempC() ;  
  roomTemp = mlx.readAmbientTempC() ;  

  // print to Serial port
  Serial.println("Object:" + String(objectTemp) + ", Ambient:" + String(roomTemp));
  Serial.println(distance);
 
  // display on OLED
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 25);
  display.print("Dis:" + String(distance) + "cm");
  display.setCursor(65, 25);
  display.print("Room:" + String(roomTemp).substring(0, 4) + "C");
  display.display();
  display.setTextSize(2);
  display.setCursor(0, 0);
 
  if (distance < minimumRange) {
    display.print("TOO CLOSE!");
  }
  if ((distance >= minimumRange) && (distance <= maximumRange)) {
    if (readcount == 5) { // after reading 5 consecutive time
      disptemp();
    } else {
      display.print("HOLD ON"); // when in range, ask user to hold position
      stemp = stemp + objectTemp;
      readcount++; // until approx. 5 x 200 ms = 1 sec
    }
  } else { // if user is out of range, reset calculation
    dtime = 100;
    readcount = 0;
    stemp = 0;
  }
  display.display();
  delay(dtime);
  Serial.println("count  :"+String(readcount));
}

void disptemp() {
  objectTemp = stemp / 5; // get the average reading of temp
  display.setTextSize(1);
  display.print("YOUR TEMP:");
  display.setTextSize(2);
  display.setCursor(60,5);
  display.print(String(objectTemp).substring(0, 4) + "C");
  display.display();
  readcount = 0;
  stemp = 0;
  if (objectTemp >= 38) {
    play_alert();
  } else {
    play_ok();
  }
  ultrasonicRead();
  dtime = 5000; // wait for 5 seconds
}

void play_ok() {  // play three sequential notes when object temperature is below 37.5C
  tone(3, 600, 1000);  // pin,frequency,duration
  delay(200);
  tone(3, 750, 500);
  delay(100);
  tone(3, 1000, 500);
  delay(200);
  noTone(3);
}

void play_alert() { // beep 3x when object temperature is >= 37.5C
  tone(3, 2000, 1000);
  delay(1000);
  tone(3, 3000, 1000);
  delay(1000);
  tone(3, 4000, 1000);
  delay(1000);
  noTone(3);
}
 
void ultrasonicRead() {
  if (distance < set_cm) {
    digitalWrite(motor, HIGH);
    Serial.println("Motor On");
    delay(500);
    digitalWrite(motor, LOW);
    Serial.println("motor off");
  }
}
4
  • 6
    Remove all the String objects from your code.
    – Majenko
    Jan 3, 2021 at 18:43
  • 1
    You may want to consider using the watchdog timer to cause the board to automatically reset for this sort of thing. To be clear, I'm not proposing this as a solution; you should still address the actual problem. But the watchdog is often used to add a layer of protection against freezing up when deployed.
    – timemage
    Jan 3, 2021 at 18:48
  • It should be a fun project. Since you did not give us a schematic (not a frizzy thing) I can only guess. It appears to me you are using the I2C interface and may be week on the pull up resistors. Also the early 1 wire library would hang up if a device missed an ACK. There are many small programs that display the amount of free memory, include that in your code and see if it is stable or slowly leaking memory.
    – Gil
    Jan 4, 2021 at 20:15
  • You can put the following two lines at the top your sketch: void got_to_line(int n) {Serial.print(F("[Line:")); Serial.print(n); Serial.println(']'); Serial.flush();} and #define GOT_HERE() got_to_line(__LINE__) You can then litter your code with GOT_HERE(); moving them around until you find the exact line (or lines) where the freeze is happening. If you do that, let us know the exact line (or lines) that it is freezing on.
    – timemage
    Jan 4, 2021 at 21:22

2 Answers 2

4

Any allocation / de- or re-allocation of memory[1] during run time, will cause the heap (the pool of memory from which those allocations are made) to expand until the heap and the stack grow toward each other enough to collide, at which point one overwrites part of the other with unpredictable results.

Strings are allocated from the heap. Strings that are built up little by little, as with:
Serial.println("Object:" + String(objectTemp) + ", Ambient:" + String(roomTemp));
cause allocation several allocation / de-allocations.

As a simple-to-implement test, comment out the statements that use the String object and just print the raw data. Your program should run without crashes.

To recreate what you're now doing with Strings, I'd suggest using snprintf() to build up your output into a fixed-size output buffer, which can be a local character array (allocated on the stack and de-allocated when the containing function exits). This should be equally stable as the abbreviated experiment I proposed above.

[1] *Unless allocated memory is de-allocated most-recent-first. And even that assumes foreknowledge of the memory allocation algorithm in use, and so, is non-portable).

One way around this is to write one's own memory allocator/de-allocator, which has a pre-assigned pool of fixed-length blocks, one of which it supplies for any memory request of any size, so long as:

  1. there is a free block to allocate; and
  2. the requested size can fit in (the data-space of) a pre-assigned block.
2
  • 2
    why not a series of Serial.print()?
    – Juraj
    Jan 3, 2021 at 21:09
  • A series of Serial.print() is perfectly acceptable; I suggested snprintf() as being semantically closer to concatenation of Strings as the OP had written.
    – JRobert
    Jan 4, 2021 at 1:04
1

In addition to some of the above comments;

Your Nano may not support the watchdog. Please check This post on the Arduino forum

If you are looking for "There are many small programs..." I'll just post the functions I use for getting free memory, heap and stack information below.

int freeRam()
{
    extern int __heap_start, *__brkval;
    int v;
    return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
}

uint8_t * stack()
{
  uint8_t * ptr;
  ptr = (uint8_t *)malloc(4);
  free(ptr);
  return ((uint8_t *)(SP));
}

uint8_t * heap()
{
       uint8_t * ptr;
       ptr = (uint8_t *)malloc(4);
       free(ptr);
       return(ptr);
}
1
  • "may not support the watchdog" is perhaps not the best way to put it. Optiboot clears MCUSR and so WDRF, meaning you cannot tell that the watchdog timer was the cause of reset merely by inspecting MCUSR. As a reset mechanism it otherwise works perfectly fine. There are some ways to somewhat address the clearing of MCUSR also, beyond just fixing the bootloader that is.
    – timemage
    Jan 9, 2021 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.