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");
}
}
2 Answers 2
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:
- there is a free block to allocate; and
- the requested size can fit in (the data-space of) a pre-assigned block.
-
2why not a series of Serial.print()?2021年01月03日 21:09:52 +00:00Commented 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– JRobert2021年01月04日 01:04:07 +00:00Commented Jan 4, 2021 at 1:04
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); }
-
"may not support the watchdog" is perhaps not the best way to put it. Optiboot clears
MCUSR
and soWDRF
, meaning you cannot tell that the watchdog timer was the cause of reset merely by inspectingMCUSR
. As a reset mechanism it otherwise works perfectly fine. There are some ways to somewhat address the clearing ofMCUSR
also, beyond just fixing the bootloader that is.timemage– timemage2021年01月09日 14:04:56 +00:00Commented Jan 9, 2021 at 14:04
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 withGOT_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.