Arduino Nano - stops executing function after a while

Hi,
I want my Arduino Nano to record my voice (and light up a LED), while I press a button and save the output file (.wav) to the connected SD card.
At the beginning, the code works perfectly, but after a while (~ 30 min. - 1,5h) it just stops running my function. So I press the button and nothing happens. After a while it will work again without a reset of the code. How can I ensure to be able to record my voice at any time?

If I modify the code to print something in the loop(), I can see that the program is still running without a reset, but just doesn't register my button presses.

I tried different boards, different power supplies, the wiring should be correct, because it works - until it randomly decides to stop working. No changes eliminated the "breaks" and I get really frustrated troubleshooting, because the "break"s appear after quite some different times..

Maybe someone can find the problem in my code or the wiring setup? Or is the Arduino Nano not suitable for this project?
Any help to solve my problem is much appreciated, thank you in advance!

// add necessary libraries
#include <SD.h>
#include <SPI.h>
#include <TMRpcm.h>

// create global variables
#define SD_ChipSelectPin 10
long debouncing_time = 250;  // debouncing time in milliseconds
volatile unsigned long last_micros;
TMRpcm audio;
volatile int file_number = 0;
volatile bool recording_now = false;
const int button_pin = 2;
const int recording_led_pin = 3;
const int mic_pin = A0;
const int sample_rate = 16000;


void record() {
  // name the file
  char file_name[20] = "";
  itoa(file_number,file_name,10);
  strcat(file_name,".wav");

  if (!recording_now) {
    // not recording --> start recording and turn LED on
    recording_now = true;
    digitalWrite(recording_led_pin, HIGH);
    audio.startRecording(file_name, sample_rate, mic_pin);
  }

  else {
    // if recording --> stops recording & turns LED off
    recording_now = false;
    digitalWrite(recording_led_pin, LOW);
    audio.stopRecording(file_name);
    // Serial.println(file_name);
    file_number++;
  }
}


void button_pushed() {  // if button state changes
   if((long)(micros() - last_micros) >= debouncing_time * 1000) {  // if last change is out of debouncing time
    record();
    last_micros = micros();
  }  
}


void setup() {
  // initialise serial connection between arduino and serial device
  Serial.begin(9600);
  Serial.println("loading...");
  randomSeed(analogRead(0));

  // pin setup
  pinMode(mic_pin, INPUT);
  pinMode(recording_led_pin, OUTPUT);
  pinMode(button_pin, INPUT_PULLUP);

  // audio recording functionality
  attachInterrupt(digitalPinToInterrupt(button_pin), button_pushed, CHANGE);  // interrupt if button state changes
  SD.begin(SD_ChipSelectPin);
  audio.CSPin = SD_ChipSelectPin;
}


void loop() {
//
}

audio_recording_circuit

Is the number of files created greater than the SD card maximum number of files allowed?

No, the SD card (class 10, 64 GB) is empty and it appears even if I write just a handful of files.

Is the power supply sufficient?

Not sure, it happens with the USB3.0 port on my PC, with a 5V - 1.6A and a 5V - 2.0A supply. Would you rate them sufficient?

Power looks good. SD cards seem to be around only 80mA to 200mA, externally powered.

[edit]

I do not see any of your datatypes in danger of going past max-value (except this guy: if((long)(micros()...), but I read the following on docs.arduino.cc in reference to this line in your code:

volatile unsigned long last_micros;

... and I wondered if the SD library or TMRpcm library uses interrupts, because...

int or long volatiles

If the volatile variable is bigger than a byte (e.g. a 16 bit int or a 32 bit long), then the microcontroller can not read it in one step, because it is an 8 bit microcontroller. This means that while your main code section (e.g. your loop) reads the first 8 bits of the variable, the interrupt might already change the second 8 bits. This will produce random values for the variable.

Remedy:

While the variable is read, interrupts need to be disabled, so they can’t mess with the bits, while they are read. There are several ways to do this:

2^31-1 is the maximum long int value, (2,147,483,647) and when counting microseconds that works out to be roughly 35 minutes.

Lets say that last_micros is 0 and 36 minutes have passed. micros() will return 1,560,000,000, giving a difference of 2,160,000,000. When the programs casts it to a (signed) long, it is -2,134,967,296. This is obviously much less than debouncing_time*1000.

The easiest "fix" is to use millis instead of micros and do calculations in milliseconds which will be good for about 25 days before acting up. For a "real fix" you need to change your debouncing algorithm. Since you are doing nothing in loop(), you could add code there that reads the button every 250ms and calls record if the button is now pressed but was not pressed in the last check.

1 Like

I will confirm this tomorrow in practice, but I really think, this is the solution.
Thank you so much for your informative help!

Edit:
The microseconds in the long variable were the problem. Now everything works fine.
Thank you for your time!

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