nrf24l01+ can't transmit again after waking up (w/ atmega328p in Arduino nano)

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

I’m trying to setup a simple alarm with the atmega328p (within an arduino nano) and an nrf24l01+ (A pair needed for TX and another for an RX).

 

The scheme is simple. I press a push button on my TX and a buzzer goes off on the RX. If ~10 seconds pass without a button press, then the uC + nrf24l01 go to sleep, until I press that same button again to wake them up.

 

As nrf24l01 settings, I’m not using acknowledgments. I hardcode the RX and TX addresses in the receiver, and I use IRQ as interrupt to detect receive packets, or to detect that TX_DS is up.

 

I can successfully make the buzzer go off on the RX with a button press. The RX receives a message “ON” to turn on the buzzer.

However, my problems arise when I let the uC + nrf24l01 go to sleep. Once I press the button to wake up, I press the button again to send a message. Even though the my TX program recognizes the button is pressed and the message “ON” sent is printed from the TX (I monitor TX UART using cutecom), the RX doesn’t receive any message anymore. If I reset the TX (by pressing the nano reset button), then the RX sees my transmitted messages again, until I let the TX sleep and wake up again.

I guess the problem is on the TX transmission, but I can’t pin point exactly where the problem lies.

 

I have tried printing an status message after I re-initialize the nrf24l01 to double-check it has booted up correctly. I think it’s ok:

 

[21:37:22:253] nRF24L01+ configured as:␍␊

[21:37:22:253] -------------------------------------------␍␊

[21:37:22:253] CONFIG 0x3f␍␊

[21:37:22:253] EN_AA 0x0␍␊

[21:37:22:253] EN_RXADDR 0x1␍␊

[21:37:22:253] SETUP_RETR 0xf0␍␊

[21:37:22:253] RF_CH 0x7c␍␊

[21:37:22:253] RF_SETUP 0x6␍␊

[21:37:22:253] STATUS 0xe␍␊

[21:37:22:253] FEATURE 0x4␍␊

[21:37:22:253] -------------------------------------------␍␊

 

 

I checked that CE is effectively being lifted with the multimeter. There’s activity there.

 

I’m posting here the TX and RX code.
The libraries I’m using + TX + RX + Makefiles are on the zip files attached. Libraries are mostly a copy of the code here:
https://github.com/thehelvijs/nRF24L01-avr-bareminimum
 

I did some modifications as placing some headers and preprocessor declarations in the .h file.
 

TL;DR: TX sends message upon startup. But can’t transmit anything after waking-up from sleep. Even though the code says it’s sending messages, the RX doesn’t receive anything; it doesn’t even detect radio.

Thanks in advance for your help!

 

TX code:

//------- Custom Includes ----------//
#include "lib_nrf24l01_m328p.h"
#include "nrf24l01-mnemonics.h"
#include "lib_stdio_uart.h"


//------- AVR Includes ------------//
#include <avr/interrupt.h>
#include <string.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <util/delay.h>
#include <avr/cpufunc.h>
#include <stdio.h>
#include <stdbool.h>


//defines
#define SWITCH_SEND_BUTTON_MASK (1 << PIND3) 
#define BUTTON_PIN PIND
uint8_t timeOut = false;

volatile uint8_t buttonDown;
char ON[] = "ON";
//char OFF[] = "OFF";
char ack_payload[32];
char tx_message[32];

//Pin mapping
// PB0 = D8 = CSN
// PD7 = D7 = CE
// PB3 = D11 = MOSI
// PB4 = D12 = MISO
// PB5 = D13 = SCK
// Reset = PC6
// PD2 = D2 = IRQ
// PD3 = D3 = SEND BUTTON (PCINT19 or INT1)
//

// PD3 button: if system is powered down, then turn it on!
// else, just poll the button for trasmitting, no Interrupt Necessary
//

static inline void setExtInterrupts(void){

    //Setting External Interrupt Control Register A
    // Falling edge of INT1 (PD3, SEND BUTTON) generates an interrupt request
    //EICRA |= (1 << ISC11); 
    //EICRA &= ~(1 << ISC10);
    EICRA &= (0 << ISC10); 
    EICRA &= (0 << ISC11);
    //Setting External interrupt Mask register
    EIMSK |= (1 << INT1);
}

static inline void unsetExtInterrupts(void){
        
    EICRA = 0x00; 
    EIMSK = 0x00;
}

ISR(INT1_vect){
   buttonDown = true; 
}

ISR(BADISR_vect){

}

static inline void setIO(void){

    //Configure PD3 as input pin with pull-up resistor
    DDRD &= ~(1 << DDD3);
    PORTD |= (1 << PORTD3);
    _NOP();

    //Most of the other IO is set by the SPI library embedded into the NRF24 library
    
}

volatile uint16_t timeCount = 0;
static inline void setTimers(){
    // Normal mode operation
    TCCR0B = 0x00;
    TCCR0A = 0x00;
    // Increment and no counter clear
    // Using TCCR0B

    //Scale by 1/1024
    TCCR0B |= (1 << CS02) | (1 << CS00);
    TCCR0B &= ~(1 << CS01);
    TCNT0=0;

    //Enable global interrupt
    sei();
    // Enable interrupt associated with this timer (TOVO)
    TIMSK0 |= (1 << TOIE0);
    
}

ISR(TIMER0_OVF_vect){
    //clock period = 1/8MHz = 0.125us
    //with pre-scaler to 1024 = 0.125us*1024 = 0.128ms
    //8-bit counter = 255 counts => 0.128ms*255 = 32.64 ms
    // To achieve 5 seconds = 5/32.64ms = 153 counts
    // To achieve 10 seconds = 10/32.64ms = 306 counts
    if (timeCount == 306){
        timeOut = true;
        timeCount = 0;
    } else {timeCount++;}
}

static inline void resetTimer(){
    unsigned char oldReg=SREG;
    cli();
    TCNT0 = 0;
    SREG=oldReg;
    _delay_ms(1);
}


static inline void buttonDebounce(void){

    static uint8_t count = 0;
    static uint8_t buttonState = 0;

    uint8_t currentState = (~BUTTON_PIN & SWITCH_SEND_BUTTON_MASK) != 0;

    if (currentState != buttonState){
        count++;
        if (count >= 4){
            buttonState = currentState;

            if (currentState != 0){
                buttonDown=1;
            } else {
                //buttonDown=0;
            }
            count=0;
        }
    } else {
        count = 0;
    }
}

void print_config(void)
{
    uint8_t data;
    printf("Startup successful\n\n nRF24L01+ configured as:\n");
    printf("-------------------------------------------\n");
    nrf24_read(CONFIG,&data,1);
    printf("CONFIG      0x%x\n",data);
    nrf24_read(EN_AA,&data,1);
    printf("EN_AA           0x%x\n",data);
    nrf24_read(EN_RXADDR,&data,1);
    printf("EN_RXADDR       0x%x\n",data);
    nrf24_read(SETUP_RETR,&data,1);
    printf("SETUP_RETR      0x%x\n",data);
    nrf24_read(RF_CH,&data,1);
    printf("RF_CH           0x%x\n",data);
    nrf24_read(RF_SETUP,&data,1);
    printf("RF_SETUP        0x%x\n",data);
    nrf24_read(STATUS,&data,1);
    printf("STATUS      0x%x\n",data);
    nrf24_read(FEATURE,&data,1);
    printf("FEATURE     0x%x\n",data);
    printf("-------------------------------------------\n\n");
}
                                                    
void sleepNow(){                                    

    //Power down NRF24
    //nrf24_write(FLUSH_TX,0,0);
    nrf24_state(POWERDOWN);

    //attach interrupt for powerup button
    setExtInterrupts();

    //sleep uC                   
    ADCSRA &= ~(1<<ADEN);                           
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);            
    sleep_enable();                                 
    sei();                                          
    sleep_cpu();                                    
                                                    

    cli();                                          
    unsetExtInterrupts();
    ADCSRA |= (1<<ADEN);                           
    sleep_disable();                                
    sei();

    buttonDebounce();
    _delay_ms(5);
    printf("Awake!\n");
    _delay_ms(5);
    //print_config();
    //_delay_ms(5);

    nrf24_init();
    uart_init();
    setIO();


}


int main(void){ 

    unsetExtInterrupts();
    nrf24_init();
    uart_init();
    setIO();
    strcpy(tx_message,ON);
    setTimers();

    while(1){

        if(timeOut){
            _delay_ms(5);
            printf("sleep\n");
            _delay_ms(5);
            sleepNow();
            resetTimer();
            timeOut=0;
        } else {
            buttonDebounce();
            
            //_delay_ms(5);
            //printf("After_PRESSING=0x%x\n",buttonDown);
            //_delay_ms(5);
            
            if(buttonDown){

                buttonDown = false;
                resetTimer();
                timeCount=0;
                _delay_ms(5);
                printf("button pressed!\n");
                _delay_ms(5);

                if(!nrf24_send_message(tx_message)){
                    _delay_ms(5);
                   printf("error sending message\n");
                    _delay_ms(5);
                }

                //print nrf24l01 status after button pressed
                _delay_ms(10);
                print_config();
                _delay_ms(10);

            }
        }
            _delay_ms(50);
    }
}

 

RX code (setTimers function isn't being used):

//------- Custom Includes ----------//
#include "lib_nrf24l01_m328p.h"
#include "lib_stdio_uart.h"


//------- AVR Includes ------------//
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <stdbool.h>
#include <string.h>

//defines
#define SWITCH_SEND_BUTTON_MASK (1 << PIND3) 
uint8_t timeOut;
volatile uint8_t messageAvailable = 0;
char rx_message[32];

//Functions
static inline void setIO(void);
static inline void buttonDebounce(void);
static inline void setExtInterrupts(void);
static inline void unsetExtInterrupts(void);
static inline void writeOCR1A(unsigned int i);
static inline void setPWMOutput(void);
void sleepNow();
static inline void resetTimer();
static inline void setTimers();
//Pin mapping (Arduino nano)
// PB0 = D8 = CSN
// PD7 = D7 = CE
// PB3 = D11 = MOSI
// PB4 = D12 = MISO
// PB5 = D13 = SCK
// Reset = PC6
// PD2 = D2 = IRQ
// PD9(OC1A) = D9 = Buzzer
//

#define ENTRY(a,b)  const uint16_t a PROGMEM = b;
#define MUSICALNOTES(ENTRY) \
  ENTRY( note_C5  ,425) \
  ENTRY( note_Cs5 ,401) \
  ENTRY( note_D5  ,379) \
  ENTRY( note_Ds5 ,357) \
  ENTRY( note_E5  ,337) \
  ENTRY( note_F5  ,318) \
  ENTRY( note_Fs5 ,300) \
  ENTRY( note_G5  ,284) \
  ENTRY( note_Gs5 ,268) \
  ENTRY( note_A5  ,253) \
  ENTRY( note_As5 ,238) \
  ENTRY( note_B5  ,225) \
  ENTRY( note_C6  ,212) \
  ENTRY( note_Cs6 ,200) \
  ENTRY( note_D6  ,189) \
  ENTRY( note_Ds6 ,178) \
  ENTRY( note_E6  ,168) \
  ENTRY( note_F6  ,159) \
  ENTRY( note_Fs6 ,150) \
  ENTRY( note_G6  ,142) \
  ENTRY( note_Gs6 ,134) \
  ENTRY( note_A6  ,126) \
  ENTRY( note_As6 ,119) \
  ENTRY( note_B6  ,112) \
  ENTRY( note_C7  ,106)

MUSICALNOTES(ENTRY)

/********************** IO  ************************************/
/***************************************************************/
static inline void setIO(void){

    //Configure PD3 as input pin with pull-up resistor
    DDRD &= ~(1 << DDD3);
    PORTD |= (1 << PORTD3);

    //Most of the other IO is set by the SPI library embedded into the NRF24 library
    
}

volatile uint8_t buttonDown;
static inline void buttonDebounce(void){

    static uint8_t count = 0;
    static uint8_t buttonState = 0;

    uint8_t currentState = (~PIND & SWITCH_SEND_BUTTON_MASK) != 0;

    if (currentState != buttonState){
        count++;
        if (count >= 4){
            buttonState = currentState;

            if (currentState != 0){
                buttonDown=1;
            }
        }
    } else {
        count = 0;
    }
}
/********************** Interrupts  ************************************/
/***************************************************************/
    
static inline void setExtInterrupts(void){

    //Setting External Interrupt Control Register A
    // Falling edge of INT1 (PD3, SEND BUTTON) generates an interrupt request
    EICRA |= (1 << ISC01); 
    EICRA &= ~(1 << ISC00);
    //Setting External interrupt Mask register
    EIMSK |= (1 << INT1);
}

static inline void unsetExtInterrupts(void){
        
    EICRA = 0x00; 
    EIMSK = 0x00;
}

ISR(INT1_vect){
    //Do nuthin
}
/********************** PWM  ************************************/



static inline void writeOCR1A(unsigned int i){
   unsigned char sreg; 
   sreg = SREG;
   cli();
   OCR1A = i;
   SREG = sreg;
} 

static inline void setPWMOutput(void){
    //TCCR1A
    //Toggle OC1A/OC1B on compare match
    //Fast PWM
    // In non-inverting compare output mode, output compare OC1A is cleared
    // on the compare match and cleared at BOTTOM.
    TCCR1A = (1 << COM1A0) | (0 << COM1A1);

    //WGM13:WGM10 = 15
    //15 = 0b1111
    //When 15 = TOP=OCR1A
    TCCR1A |= (1 << WGM11) | (1 << WGM10);
    TCCR1B = (1 << WGM13) | (1 << WGM12);
    
    // Play a Treble Do = 523.251Hz
    // fclk/Do = 30578
    // N*(1+TOP) = 30578
    // Set divider to 64
    // 30578/64 = (1+TOP)
    // 477.478 = (1+TOP)
    // TOP = 476 ~480

    //Set pre-scaler to CLK/64
    TCCR1B |= (1 << CS11) | (1 << CS10);

    //Set TOP 
    writeOCR1A(480);

    //Since we have set the counter to compare against OCR1A...
    //Set OCIE1A to enable the the Output Compare A Match Interrupt Enable

    TIMSK1 |= (1 << OCIE1A);
    //execute ISR for this interrupt vector

}

static inline void enablePWMOutput(void){
    //Buzzer connected to PB1 (OC1A)

    DDRB |= (1 << DDB1);

}
static inline void disablePWMOutput(void){

    //Buzzer connected to PB1 (OC1A)
    DDRB &= ~(1 << DDB1);
}

/************************* Timers **********************************/
/***************************************************************/

volatile uint8_t timeCount = 0;
static inline void setTimers(){
    // Normal mode operation
    TCCR0B = 0x00;
    // Increment and no counter clear
    // Using TCCR0B

    //Scale by 1/1024
    TCCR0B |= (1 << CS02) | (1 << CS00);
    TCCR0B &= ~(1 << CS01);

    //Enable global interrupt
    sei();
    // Enable interrupt associated with this timer (TOVO)
    TIMSK0 |= (1 << TOIE0);
}

ISR(TIMER0_OVF_vect){
    //clock period = 1/8MHz = 0.125us
    //with pre-scaler to 1024 = 0.125us*1024 = 0.128ms
    //8-bit counter = 255 counts => 0.128ms*255 = 32.64 ms
    // To achieve 5 seconds = 5/32.64ms = 153 counts
    if (timeCount == 153){
        timeOut = true;
    } else {timeCount++;}
}

static inline void resetTimer(){
    unsigned char oldReg=SREG;
    cli();
    TCNT0 = 0;
    SREG=oldReg;
}


                                                    
void sleepNow(){                                    
    //Power down NRF24
    nrf24_state(POWERDOWN);
    //attach interrupt for powerup button                   
    setExtInterrupts();
    //sleep uC                   
    ADCSRA &= ~(1<<ADEN);                           
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);            
    sleep_enable();                                 
    sei();                                          
    sleep_cpu();                                    
                                                    
    cli();                                          
    sleep_disable();                                
    timeOut = false;                                
    timeCount = 0;                                  
    sei();                                          
    unsetExtInterrupts();
    nrf24_init();
    nrf24_start_listening();
}

ISR(INT0_vect){
       messageAvailable = true; 
}

int main(void){ 

    setIO();
    setPWMOutput();
    uart_init();
    nrf24_init();

    nrf24_start_listening();

    while(1){
    
        if(messageAvailable || nrf24_available()){
            messageAvailable = false;        
            strcpy(rx_message, nrf24_read_message());
            if((strcmp(rx_message,"ON") == 0)){

                printf("Received Message: %s\n",rx_message);
                //emit 3 tones!
                writeOCR1A(pgm_read_word(&note_C5));
                for(uint8_t i = 0; i<3;i++){

                    _delay_ms(250);
                    enablePWMOutput();
                    _delay_ms(250);
                    disablePWMOutput();
                }
            }
        }
        _delay_ms(5);
    }
}

 

Attachment(s): 

Learning a little every day is the compound interest for my satisfaction

Last Edited: Fri. Oct 15, 2021 - 05:38 PM