Arduino hangs / freezes - can't figure out why

Hi! I am building a remote controlled boat. My remote project freezes after an arbitrary amount of time. The serial stops output, the OLED doesn't update anymore and instructions are not send / received anymore.

For the life of me I cannot figure out why my project freezes. Using an Arduino Nano.

I have tried the following:

  • Keep memory small
  • Disable Serial as not to overflood
  • Not use any risky variables; like String
  • Determine if a specific circumstance causes the hang (X amount of time, certain instructions, losing radio connection, etc)
  • Replace the Arduino
  • Replace the power supply
  • Replace the power supply cable
  • Put the radio into debug mode, it becomes slow and nothing interesting shows on serial

Please provide help to allow me to continue my two-year long project. My gratitude for any pointers or help is unending.

The Arduino is connected to a NRF24L01 with capacitor board, an OLED and two potentiometers.

This is my code:

#include <SPI.h>
#include "printf.h"
#include "RF24.h"
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"
#include <DifferentialSteering.h>

// Debugging mode:
int debugging_serial = 0;
int debugging_delay = 0;
int debugging_verbose = 0;

// Timers init;
unsigned long start_timer = 0;
unsigned long end_timer = 0;

// Oled state for clearing
int oledstate = 0;

// Create nRF24L01 transceiver using pins 9 and 10.
RF24 radio(9, 10);

// OLED init
#define I2C_ADDRESS 0x3C // 0X3C+SA0 - 0x3C or 0x3D
#define RST_PIN -1 // Define proper RST_PIN if required.
SSD1306AsciiAvrI2c oled;

// Set potentio meter pins
int pot_one_pin = A0;
int pot_two_pin = A1;

// Set Diff Steering
DifferentialSteering DiffSteer;

// Register two radio addresses to use for communication.
uint8_t address[][6] = { "1Node", "2Node" };


// Define sending and receiving payload structs. Note: these should be inversed on remote vs boat code
// DEFINE WHAT WE WILL BE SENDING
struct SendingPayloadObject 
{
  // We are sending two numbers
  uint8_t Motor1Speed; 
  uint8_t Motor2Speed;
};
// Also create the payload object to prepare and load with defaults in setup
SendingPayloadObject payload;

// DEFINE WHAT WE WILL BE RECEIVING
struct ReceivingPayloadObject 
{
  // We are sending a message and a number
  char message[7];  // only using 6 characters for TX & ACK payloads
  uint8_t counter; // struct counter variable as example
};

void setup() 
{
  if(debugging_serial) 
  {
    Serial.begin(115200);
    Serial.println("Remote Code Started!");
  }

  // Oled setup
  // Call oled.setI2cClock(frequency) to change from the default frequency.
  oled.begin(&Adafruit128x64, I2C_ADDRESS, RST_PIN);
  //oled.setFont(System5x7);
  oled.clear();
  oled.println("Boat Remote Started!");
  oled.clear();
  
  // Check if radio works
  if (!radio.begin()) 
  {
    if(debugging_serial) 
    {
      Serial.println(F("Radio hardware is not responding!"));
    }
      
    while (1) {}  // hold
  }

  // Set the PA Level low to try preventing power supply related problems
  // because these examples are likely run with nodes in close proximity to
  // each other. (RF24_PA_MAX is default)
  radio.setPALevel(RF24_PA_LOW);

  // To use ACK payloads, we need to enable dynamic payload lengths (for all nodes). ACK payloads are dynamically sized
  radio.enableDynamicPayloads();

  // Acknowledgement packets have no payloads by default. We need to enable this feature for all nodes (TX & RX) to use ACK payloads.
  radio.enableAckPayload();

  // Set the TX address of the RX node into the TX pipe and the opposite to the RX pipe. The remote radio address is 1!
  radio.openWritingPipe(address[1]);
  radio.openReadingPipe(1, address[0]);

  // Sender role code
  // Set payload defaults and put radio in send (TX) mode
  payload.Motor1Speed = 0;  // set the payload payloadMotor1Speed
  payload.Motor2Speed = 0;  // set the payload payloadMotor1Speed
  radio.stopListening();                 // put radio in TX mode
  
  if(debugging_verbose)
  {
    printf_begin();             // needed only once for printing details
    radio.printDetails();       // (smaller) function that prints raw register values
    radio.printPrettyDetails(); // (larger) function that prints human readable data
  }

  // DiffSteer init
  DiffSteer.begin(0);
}

void loop() 
{
  //// REMOTE CONTROL STUFF:

  // NEW LOGIC FOR MANEURVERING
  int motorSpeed1 = 0;
  int motorSpeed2 = 0;

  // Steering using potentiometers
  int steering_pot_value = map(analogRead(pot_two_pin), 0, 1023, -127, 127);
  int power_pot_value = map(analogRead(pot_one_pin), 0, 1023, 0, 127);

  // Go Straight when steering almost straight
  if(steering_pot_value <= 15 && steering_pot_value >= -15)
  {
    steering_pot_value = 0;
  }

  // Compute differential steering
  DiffSteer.computeMotors(steering_pot_value, power_pot_value);
  
  // map motor outputs to needed range
  motorSpeed1 = map(DiffSteer.computedLeftMotor(), 0, 127, 0, 255);
  motorSpeed2 = map(DiffSteer.computedRightMotor(), 0, 127, 0, 255);

  // Corrective measures for smoother steering
  // Fix division by 0, full power when pot completley off
  if(power_pot_value <= 1) { motorSpeed1 = 0; motorSpeed2 = 0; };

  // OLED motor control code (for testing)
  //  Speeds directley from pot meters
  //  motorSpeed1 = map(analogRead(pot_one_pin), 0, 1023, 0, 255);
  //  motorSpeed2 = map(analogRead(pot_two_pin), 0, 1023, 0, 255);
  //
  
  // Set motor speeds in payload
  payload.Motor1Speed = motorSpeed1;
  payload.Motor2Speed = motorSpeed2;

  //// RADIO STUFF:
  
  // Let's time the tranmission for maagd / nerd purposes
  if(debugging_serial) 
  {
     start_timer = micros();                  // start the timer
  }

  // Send over radio TX;
  bool report = radio.write(&payload, sizeof(payload));  // transmit & save the report

  if(debugging_serial) 
  {
    end_timer = micros();                    // end the timer
  }

  if (report) 
  {
    if(debugging_serial) 
    {
      Serial.print(F("Time to transmit = "));
      Serial.print(end_timer - start_timer);  // print the timer result
      Serial.print(F(" us. Sent (Motor 1 Speed: "));
      Serial.print(payload.Motor1Speed);
      Serial.print(F(" , Motor 2 Speed: "));
      Serial.print(payload.Motor2Speed);
      Serial.print(F(")"));
    }

    // Define the pipe
    uint8_t pipe;
    
    if (radio.available(&pipe)) 
    {
      if (oledstate != 1) { oled.clear(); }; oledstate = 1;
      oled.set1X();
      oled.setFont(System5x7);
      oled.setCursor(0 , 0);
      oled.print("Transmission OK \n");
      oled.setCursor(0 , 30);
      oled.print("Motors L:");
      // Add leading zeroes to get true values (otherwise old number remains)
      if(payload.Motor1Speed < 100 && payload.Motor1Speed >= 10) { oled.print("0"); };
      if(payload.Motor1Speed < 10) { oled.print("00"); };
      oled.print(payload.Motor1Speed);
      oled.setCursor(80 , 30);
      oled.print("R:");
      if(payload.Motor2Speed < 100 && payload.Motor2Speed >= 10) { oled.print("0"); };
      if(payload.Motor2Speed < 10) { oled.print("00"); };
      oled.print(payload.Motor2Speed);
      
      // is there an ACK payload? grab the pipe number that received it
      ReceivingPayloadObject received;
      radio.read(&received, sizeof(received));  // get incoming ACK payload

      if(debugging_serial) 
      {
        Serial.print(F(" Recieved "));
        Serial.print(radio.getDynamicPayloadSize());  // print incoming payload size
        Serial.print(F(" bytes on pipe "));
        Serial.print(pipe);  // print pipe number that received the ACK
        Serial.print(F(": "));
        Serial.print(received.message);    // print incoming message
        Serial.println(received.counter);  // print incoming counter
      }
    } 
    else 
    {
      if (oledstate != 2) { oled.clear(); }; oledstate = 2;
      oled.set1X();
      oled.setCursor(0, 0);
      oled.setFont(System5x7);
      oled.print("Transmission: \n \n");
      oled.setCursor(0, 80);
      oled.set2X();
      oled.print("No Reply (empty ACK)");

      if(debugging_serial) 
      {
        Serial.println(F(" Recieved: an empty ACK packet"));
      }
    }
  } 
  else 
  {
    if (oledstate != 3) { oled.clear(); }; oledstate = 3;
    oled.set1X();
    oled.setCursor(0, 0);
    oled.setFont(System5x7);
    oled.print("Transmission: \n \n");
    oled.setCursor(0, 80);
    oled.set2X();
    oled.print("Failed");

    if(debugging_serial) 
    {    
      Serial.println(F("Transmission failed or timed out"));
    }
  }

  // Add a delay while programming for reading purposes
  if(debugging_verbose)
  {
    delay(10000); 
  }
  else
  {
    if(debugging_delay)
    {
      delay(1000); 
    }
  }
}

Check if there will be a hang on MEGA2560

I do not have a MEGA in my possession - unfortunately. Thanks for your reply!

Possibilities including running out of memory (the OLED takes half of RAM on an ATmega328) and electrical noise from the motors causing processor malfunctions.

a NRF24L01 with capacitor board

Capacitors are an iffy fix, too. Use a separate 3.3V regulator capable of handling the transmit current.

Try using the F macro here, as well. This wastes RAM.

  oled.println("Boat Remote Started!");

If the OLED module contains 4.7K pullups on SDA/SCL you can try strengthening them to 2.2K or 1K.

Thanks for your replies. The problem persists. I am in fact using an adapter board with a proper regulator for the radio - a very common piece. I will add the F mavros. The IDE says there is still plenty of RAM. Not sure about the resistors.

My guess goes to some buffer overflowing or garnage collection not being done? Any advice?

Doesn't matter. The compiler/linker only knows about static allocations of memory at compile time. There is dynamic memory that is allocated when you create instances of objects, e.g. radio, oled, and DiffSteer.

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