Arduino Nano crashing when using nRF24L01 with button logic

Hello,

I am new to forums but I would appreciate any advice you have for my issue. I am using an Arduino Nano to read the analog signals from 5 flex sensors. I have a push button involved as well. (Each flex sensor and the push button is wired with a 10k ohm resistor).

In my program, I have a sequence that detects if the initial state of the button changes so the rest of the void loop is skipped until the button is toggled for the first time.

Once the button is initially toggled, the program will run through "mode 2" until it is toggled again where it will run through "mode 1" - Which you will continue to be able to toggle between until power is removed.

However, as soon as the button is toggled a second time (going from "mode 2" to "mode 1"), the program will radio.write() and print each value once but then crash.

I tried removing the "radio.write()" line and it will let you toggle between modes and continuously print the flex sensor values for as long as you want - so it must have something to do with the radio.

Another interesting thing I noticed is that when the button is toggled a second time (going from mode 2 to mode 1), and held down, the values will continue to print until it is released when the program crashes.

I also have a version of the code that doesn't involve the button logic and it will send values and be able to be received from another arduino just fine without crashing.

Here is the transmitter code with the button:

#include <RF24.h>
#include <RF24_config.h>
#include <nRF24L01.h>
#include <printf.h>


// Initialize Radio Data
RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";
// const int ARRAY_SIZE = 5;
int transmit[5];


// Initialize Flex Sensor Data
const int flexPin1 = A1; 
const int flexPin2 = A2;
const int flexPin3 = A3;
const int flexPin4 = A4;
const int flexPin5 = A5;

int value1, value2, value3, value4, value5;
int flex1, flex2, flex3, flex4, flex5;


// Initialize Button Data
const int buttonPin = 10;  // Pin connected to the push button
const int ledPin = 13;    // Pin connected to the LED

int buttonState;
int lastButtonState = LOW;
int modeSelect = 1;       // Current mode: 0 for mode 1, 1 for mode 2

bool debounceActive = false;  // Flag to track if button debouncing is active
bool initialButtonChange = false;  // Flag to track the initial button state change

unsigned long lastModeChange = 0;  // Timestamp of the last mode change
unsigned long debounceDelay = 100; // Debounce delay in milliseconds


void setup() {

  Serial.begin(9600);
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
  pinMode(buttonPin, INPUT); 
  pinMode(ledPin, OUTPUT);          
  Serial.println("Start Test");

}

void loop() {
  int buttonState = digitalRead(buttonPin);  // Read the current state of the button

  if (!initialButtonChange) {
    if (buttonState != lastButtonState) {
      lastButtonState = buttonState;
      if (buttonState == HIGH) {
        initialButtonChange = true;  // Button state changed for the first time, set initialButtonChange to true
      }
    }
    return; // Skip the rest of the loop until initial button change occurs
  }

  if (buttonState != lastButtonState) {
    lastButtonState = buttonState;
    if (buttonState == HIGH && debounceActive == false) {
      changeMode();                      // Button pressed, change the mode
      lastModeChange = millis();         // Record the timestamp of mode change
      debounceActive = true;             // Activate button debouncing
    }
  }

  if ((millis() - lastModeChange) > debounceDelay) {
    debounceActive = false;  // Disable button debouncing after debounceDelay
  }

  if (modeSelect == 0) {
    mode1();  // Execute mode 1 functionality
  } else {
    mode2();  // Execute mode 2 functionality
  }
}


void changeMode() {
  if (modeSelect == 0) {
    modeSelect = 1;  // Toggle mode from 0 to 1
  } else {
    modeSelect = 0;  // Toggle mode from 1 to 0
  }
}

void mode1() {  // Glove Control Mode

  digitalWrite(ledPin, HIGH);  // Turn on the LED
  //Serial.println("LED is on");

  // Transmitter Code
  value1 = analogRead(flexPin1);
  value2 = analogRead(flexPin2);
  value3 = analogRead(flexPin3);
  value4 = analogRead(flexPin4);
  value5 = analogRead(flexPin5);


  flex1 = map(value1, 759, 810, 0, 51); // PINKY **
  flex1 = constrain(flex1, 0, 180); 

  flex2 = map(value2, 759, 811, 0, 52); // RING **
  flex2 = constrain(flex2, 0, 180);

  flex3 = map(value3, 777, 829, 0, 52); // MIDDLE **
  flex3 = constrain(flex3, 0, 180);  

  flex4 = map(value4, 770, 880, 0, 110); // POINTER **
  flex4 = constrain(flex4, 0, 180);  

  flex5 = map(value5, 799, 890, 0, 91); // THUMB **
  flex5 = constrain(flex5, 0, 180);

  transmit[0] = flex1;
  transmit[1] = flex2;
  transmit[2] = flex3;
  transmit[3] = flex4;
  transmit[4] = flex5;



  radio.write(&transmit, sizeof(transmit));  // This line of code is casuing the crash!?


  Serial.println("PINKY: " + String(transmit[0]));
  Serial.println("RING: " + String(transmit[1]));
  Serial.println("MIDDLE: " + String(transmit[2]));
  Serial.println("INDEX: " + String(transmit[3]));
  Serial.println("THUMB: " + String(transmit[4]));

  delay(100);   
}


void mode2() {  // Preset Gesture Mode

  digitalWrite(ledPin, LOW);  // Turn off the LED
  Serial.println("LED is off");
  delay(100);
  //*** Set transmit[] variable to preset motions and send them -- receiver code will run normally

}

All the wiring is the same as when I tested it without the button logic and it worked fine so I don't believe that is the issue.

If there is anything else you need to know about my issue please let me know and I will attach it to this.

And if anyone can help me I would greatly appreciate it!

How do you know it has "crashed"? What happens?

On a PC or other larger computer, when an app or program "crashes", some unwanted criteria is detected, an error message is shown to the user and the app is deliberately stopped by the operating system in order to prevent damage to data, files or other apps/programs that are running.

On arduino, there is no operating system and nothing as sophisticated as that happening. Often the code simply behaves in some unexpected and undesirable way.

Please post a schematic showing how everything is connected. This should include all component values or model numbers and details of all power supplies used (which could be USB power for example).

If you don't understand what a schematic is, please Google to find out. It is not quite the same thing as a wiring diagram. Hand-drawn is ok.

Some bright, sharp photos of the circuit may also be useful.

1 Like

Sorry,

By "crash" I just mean that the code freezes and will no longer execute based on new inputs. So it gets stuck on printing the "THUMB" value once after its first iteration through mode 1.

I don't receive an error message or anything, the program just gets stuck and does not react to the button after this point.

I will create a schematic and attach it to this case as soon as I can. Thanks!


Please let me know if this is good enough... I put a 9V battery in the schematic which is what we plan to use, but I have been testing it with USB power supply so far.

Let me know if you need anything else or if you get an idea of what might be happening.

Thanks!

I would agree with that.

Is that also with the radio.write() line removed?

That seems to tell us that it is not radio.write() that causes the crash. Please post that code.

You mean THUMB is the last thing to appear on serial monitor?

Thanks for the schematic. I can suggest tips to improve it, but they will not fix the problem at hand.

Same for your code, lots of suggestions for improvements, but they also will not fix the problem, so we can leave those until later.

Your proposal to use 9V battery is problematic, but you are not using that yet, so it's also not relevant to the current problem.

No, it only "crashes" (lmk what the correct term is), when the radio.write() line is left in the code. When radio.write() is removed, the code functions exactly how I intend and will let you toggle between modes and continuously print flex sensor values while in mode 1.

Here is the transmitter code that doesn't involve the button:

#include <RF24.h>
#include <RF24_config.h>
#include <nRF24L01.h>
#include <printf.h>


// Initialize Radio Data
RF24 radio(7, 8); // CE, CSN

const byte address[6] = "00001";
const int ARRAY_SIZE = 5;
int transmit[ARRAY_SIZE];


// Initialize Flex Sensor Data
const int flexPin1 = A1; 
const int flexPin2 = A2;
const int flexPin3 = A3;
const int flexPin4 = A4;
const int flexPin5 = A5;

int value1, value2, value3, value4, value5;
int flex1, flex2, flex3, flex4, flex5;


void setup() {

  Serial.begin(9600);
  Serial.println("test");
  radio.begin();
  radio.openWritingPipe(address);
  radio.setPALevel(RF24_PA_MIN);
  radio.stopListening();
  
}

void loop() {

  value1 = analogRead(flexPin1);
  value2 = analogRead(flexPin2);
  value3 = analogRead(flexPin3);
  value4 = analogRead(flexPin4);
  value5 = analogRead(flexPin5);


  flex1 = map(value1, 759, 810, 0, 51); // PINKY **
  flex1 = constrain(flex1, 0, 180); 

  flex2 = map(value2, 759, 811, 0, 52); // RING **
  flex2 = constrain(flex2, 0, 180);

  flex3 = map(value3, 777, 829, 0, 52); // MIDDLE **
  flex3 = constrain(flex3, 0, 180);  

  flex4 = map(value4, 770, 880, 0, 110); // POINTER **
  flex4 = constrain(flex4, 0, 180);  

  flex5 = map(value5, 799, 890, 0, 91); // THUMB **
  flex5 = constrain(flex5, 0, 180);

  transmit[0] = flex1;
  transmit[1] = flex2;
  transmit[2] = flex3;
  transmit[3] = flex4;
  transmit[4] = flex5;


  radio.write(&transmit, sizeof(transmit));

  Serial.println("PINKY: " + String(transmit[0]));
  Serial.println("RING: " + String(transmit[1]));
  Serial.println("MIDDLE: " + String(transmit[2]));
  Serial.println("INDEX: " + String(transmit[3]));
  Serial.println("THUMB: " + String(transmit[4]));

  delay(500);
  
}

Yes, I was just trying to say that when running the "transmitter + button" code, with the radio.write() in the program, after switching from mode 2 to mode 1 for the first time, the program "crashes". - But it will always get through the block of 5 print statements, so THUMB is the last thing to print on the serial monitor. Would that mean something is going wrong through its second time in the loop?

And I'm open to suggestions once we can figure this out :slight_smile:

If you need anything else lmk.

There was no colon or value printed after the THUMB?

Sorry for being unclear, that entire line was executed properly - so it printed THUMB with the colon and the value but crashes after that point.

What I would suggest is... Put some extra Serial.println("doing XYZ now.") into your code, especially immediately before & after the radio.write(). After each Serial.println(), put Serial.flush(). This will force the Arduino to completely send the line to serial monitor before going into the next line of code.

Without the Serial.flush(), what can happen is that the text from Serial.println() gets put in an output buffer, but the "crash" prevents it from ever getting to serial monitor. This can make it look as though the crash happened earlier than it actually did.

Okay, I've already used some print statements to narrow it down to right before or after radio.write(). But I will try using Serial.flush() as well.

Another strange thing is that when I am switching from mode 2 to mode 1 with radio.write() in the code, when I hold down the push button to make the switch, it continues to print flex sensor values for as long as the button is held down. But as soon as I let go of the button it crashes after the THUMB statement. Do you know why this is the case, or would it have something to do with the button debounce logic?

Yes, you mentioned this in your first post, I can't think why that is happening. More Serial.println() might reveal a clue.

Reviewing your schematic again, I suggest the following wiring changes:

  1. Use Pin 10 as an OUTPUT pin, For example, use it to connect to either the CE or CSN pin of the NRF24 (instead of Pin 7 or 8), or use it to connect an external LED (+resistor). The reason is that Pin 10 (=SS) needs to be an OUTPUT when using Nano (or any atmega328) as an SPI Controller. If you use it as an INPUT, and it becomes LOW at any time, the Nano flips to becoming an SPI Peripheral. That's exactly what is happening now, I think, because the 10K resistor is pulling Pin 10 LOW (except when you are pressing the button).
  2. Choose another unused pin for your button, not pin 10.
  3. Don't use Pin 13 for your LED. Pin 13 (=SCK) is being used for SPI. You will need to choose another unused pin to connect an external LED (+ resistor).

Awesome, I'll try these changes and let you know what happens.

Just a question - do the CE and CSN pins have to be connected to 7, 8 or is that just a suggestion.

I'll let you know what happens though

Pins 7 & 8 are ordinary digital input/output pins, they have no special purpose.

Wow! You are him - I just made the 3 changes you suggested and it started working. Mind you I didn't have a chance to run proper tests because a solder broke on the connection to the button but when I was running it the program didn't crash and was able to radio.write() the values. I'll re-solder it and test it properly but I believe it should be good!

Now if you don't mind explaining your suggestions to the code and schematic drawing I'd be happy to hear it.

Also what power supply should we use instead of the 9V battery??

1 Like

For the schematic, I would suggest using the "Ground" and "Vcc" symbols. Use as many as you need and put them wherever they are needed. If you do that, you can remove all the ground and power wires that snake all over the schematic and make it such a difficult mess to read. The schematic will become much more neat, simple and easy to understand.

For the code, I suggest to use more arrays. Wherever you have multiple variables which have the same name except for digits at the end or somewhere in them, or have "A", "B", "C"... you get the idea. For example

const int flexPin1 = A1; 
const int flexPin2 = A2;
const int flexPin3 = A3;
const int flexPin4 = A4;
const int flexPin5 = A5;

int value1, value2, value3, value4, value5;
int flex1, flex2, flex3, flex4, flex5;

can become

const int fingers = 5;
const int flexPin[fingers] = {A1, A2, A3, A4, A5};

int value[fingers];
int flex[fingers];

When you look at your code in loop() you see many code lines which are repeated 5 times over. The only differences in these code lines is that "value2" is used in place "value1" and some of the numbers are different, as an example. By using arrays and for-loops, you can reduce the size of your code by almost 80%.

For example this

void loop() {

  value1 = analogRead(flexPin1);
  value2 = analogRead(flexPin2);
  value3 = analogRead(flexPin3);
  value4 = analogRead(flexPin4);
  value5 = analogRead(flexPin5);

  flex1 = map(value1, 759, 810, 0, 51); // PINKY **
  flex1 = constrain(flex1, 0, 180);

becomes

void loop() {

  for(int f=0; f<fingers; f++) {
    value[f] = analogRead(flexPin[f]);
    flex[f] = map(value[f], minValue[f], max value[f], 0, maxFlex[f]);
    flex[f] = constrain(flex[f], 0, 180);

9V batteries don't have much capacity. And when you run a 5V circuit (actually, most of it is 3.3V) from a 9V battery, half of the battery's capacity is wasted as heat by the Nano's regulator.

One improvement you could make is to get a very small DC-DC converter module. This will reduce the 9V down to 5V in a much more efficient way compared to the Nano's regulator, extending the battery life by almost double.

Another alternative could be 4xAAA or 4xAA NiMH batteries in a holder. These will produce approximately 5V. This means you can connect it directly to the 5V pin of the Nano, instead of the Vin pin, and bypass that wasteful regulator.

Even more radical would be to swap the Nano for a 3.3V Arduino like a Pro Mini or Pro Micro. Then everything can run at 3.3V and you don't need to supply 5V at all. This opens up the opportunity to use a 3.7V Li-Po pack. These packs have much more capacity for their size and weight compared to 9V and AA/AAA batteries.

Ok, I'll make some of those changes :+1:

What did you mean by putting the GND and VCC symbols - Do I not need to show their connections as long as I have those symbols?