Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF52832 missing ADV_CONNECT_REQ BLE packets under most conditions

Hello,

I'm using a modified version of the examples->peripheral->radio examples from nRF5 SDK v17.0.0 to receive packets sent on Bluetooth Low Energy channel 37 using an nRF52832. I am not using any soft device. I have a Sparkfun module, a Laird module, and my own custom hardware using this transceiver and am using a J-Link and Segger Embedded Studio to debug. Please see my code below:

void radio_set_ch(uint8_t ch)
{
  if(ch<40)
  {
    NRF_RADIO->FREQUENCY = channel[ch];
    NRF_RADIO->DATAWHITEIV = 64+ch;
    NRF_LOG_INFO("Channel set to %d",ch);
  } else {
    NRF_LOG_ERROR("Error setting channel");
  }
}

void radio_init_adv(uint8_t ch, uint8_t power)
{
  //Power 0dbm, BLE 1Mbit mode
  NRF_RADIO->POWER = 1;
  NRF_RADIO->TXPOWER = (power << RADIO_TXPOWER_TXPOWER_Pos);
  NRF_RADIO->MODE = (RADIO_MODE_MODE_Ble_1Mbit << RADIO_MODE_MODE_Pos);
  
  //Channel
  radio_set_ch(ch);

  //Address configuration
  NRF_RADIO->PREFIX0 = 0x8e;
  NRF_RADIO->BASE0 = 0x89bed600;
  NRF_RADIO->TXADDRESS = 0x00;
  NRF_RADIO->RXADDRESSES = 0x01;

  //Packet configuration
  NRF_RADIO->PCNF0 = (
    (((1UL) << RADIO_PCNF0_S0LEN_Pos) & RADIO_PCNF0_S0LEN_Msk) |  //S0 length 1 byte
    (((2UL) << RADIO_PCNF0_S1LEN_Pos) & RADIO_PCNF0_S1LEN_Msk) |  //S1 length 2 bit
    (((6UL) << RADIO_PCNF0_LFLEN_Pos) & RADIO_PCNF0_LFLEN_Msk)    //Length field length 6 bit
  );
  NRF_RADIO->PCNF1 = (
    (((50UL) << RADIO_PCNF1_MAXLEN_Pos) & RADIO_PCNF1_MAXLEN_Msk)   |                      //Payload length max. 37 byte
    (((0UL) << RADIO_PCNF1_STATLEN_Pos) & RADIO_PCNF1_STATLEN_Msk)   |                      //Payload expansion 0 byte
    (((3UL) << RADIO_PCNF1_BALEN_Pos) & RADIO_PCNF1_BALEN_Msk)       |                      //Base address length 3 byte
    (((RADIO_PCNF1_ENDIAN_Little) << RADIO_PCNF1_ENDIAN_Pos) & RADIO_PCNF1_ENDIAN_Msk) |  //Endianess S0, Length, S1 and payload
    (((1UL) << RADIO_PCNF1_WHITEEN_Pos) & RADIO_PCNF1_WHITEEN_Msk)                         //whitening enable
  );

  //CRC
  NRF_RADIO->CRCCNF  = (RADIO_CRCCNF_LEN_Three << RADIO_CRCCNF_LEN_Pos) |
                       (RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos); //Exclude address from CRC
  NRF_RADIO->CRCINIT = 0x555555;                                                  //CRC inital value
  NRF_RADIO->CRCPOLY = 0x00065B;                                                  //CRC poly

  //Clear Events
  NRF_RADIO->EVENTS_DISABLED = 0;
  NRF_RADIO->EVENTS_END = 0;
  NRF_RADIO->EVENTS_READY = 0;
  NRF_RADIO->EVENTS_ADDRESS = 0;
  
  //Interrupt on event
  NRF_RADIO->INTENSET = RADIO_INTENSET_ADDRESS_Msk | RADIO_INTENSET_DISABLED_Msk;  
  
  NRF_LOG_INFO("Radio init (adv) complete");
}

void receiver(void)
{
  uint8_t packet[50];
  radio_init_adv(37, RADIO_TXPOWER_TXPOWER_0dBm);
  NRF_RADIO->PACKETPTR = (uint32_t)&packet;
  NRF_LOG_INFO("Start!");

  //Radio enable & wait for ready
  NRF_RADIO->EVENTS_READY = 0U;
  NRF_RADIO->TASKS_RXEN = 1U;
  while (NRF_RADIO->EVENTS_READY == 0U);

  while(1)
  {
    //Listen & wait for address receive
    NRF_RADIO->EVENTS_END = 0U;
    NRF_RADIO->TASKS_START = 1U;
    while (NRF_RADIO->EVENTS_END == 0U);

    NRF_LOG_INFO("Packet was received");
    NRF_LOG_INFO("S0: 0x%02X", packet[0]);
    NRF_LOG_INFO("Length:%u Bytes (0x%02X)", packet[1], packet[1]);
    NRF_LOG_INFO("S1: 0x%02X", packet[2]);
    uint64_t adva=((uint64_t)packet[8]<<40)+((uint64_t)packet[7]<<32)+((uint64_t)packet[6]<<24)+((uint64_t)packet[5]<<16)+((uint64_t)packet[4]<<8)+(uint64_t)packet[3];
    NRF_LOG_INFO("AdvA: 0x%06X%06X", (adva>>24), (adva&0xFFFFFF));
    NRF_LOG_FLUSH();
  }

}

receiver(); is called from my main function and for most packets such as ADV_IND and ADV_NON_CONN_IND this works fine and gives me an output like this:

<info> app: Channel set to 37
<info> app: Radio init (adv) complete
<info> app: Start!

<info> app: Packet was received
<info> app: S0: 0x42
<info> app: Length:37 Bytes (0x25)
<info> app: S1: 0x00
<info> app: AdvA: 0x16544296A738

<info> app: Packet was received
<info> app: S0: 0x00
<info> app: Length:27 Bytes (0x1B)
<info> app: S1: 0x00
<info> app: AdvA: 0xA4C13824E4A6

The first packet received here is sent by my phones covid app, the second is an advertisment from a simple BLE thermometer. Of course this is just an excerpt, as the device would keep listening and the list would grow.

My issue however is that this setup seems to not receive or possibly miss ADV_CONNECT_REQ packets (for example sent from my phone to the BLE thermometer). They just don't show up. I simultaneously used a TI dongle and their Packet Sniffer software, so I know for a fact that a ADV_CONNECT_REQ packet was sent on channel 37.

For further debugging I added this code:

void send_packet()
{
  //Enable radio & wait for ready
  NRF_RADIO->EVENTS_READY = 0U;
  NRF_RADIO->TASKS_TXEN   = 1;
  while (NRF_RADIO->EVENTS_READY == 0U);

  //Send packet
  NRF_RADIO->EVENTS_END  = 0U;
  NRF_RADIO->TASKS_START = 1U;
  while (NRF_RADIO->EVENTS_END == 0U);
  
  //Disable radio
  NRF_RADIO->EVENTS_DISABLED = 0U;
  NRF_RADIO->TASKS_DISABLE = 1U;
  while (NRF_RADIO->EVENTS_DISABLED == 0U);
}

void transmitter(void)
{
  uint8_t packet[]={0x05,0x22,0x00,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xBB,0xBB,0xBB,0xBB,0xBB,0xBB,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC};
  radio_init_adv(37, RADIO_TXPOWER_TXPOWER_0dBm);
  NRF_RADIO->PACKETPTR = (uint32_t)&packet;
  while(1)
  {
    send_packet();
    nrf_delay_ms(200);
    NRF_LOG_FLUSH();
  }
}

This is used to periodically send a "dummy" ADV_CONNECT_REQ packet for test purposes. I used one nRF52832 as a transmitter, and one as a receiver, and the "dummy" ADV_CONNECT_REQ packets came through fine.

I am unable to find my mistake - why can I not receive ADV_CONNECT_REQ packets when they're being sent as a direct response to advertisements? I have tried different phones as well. The fact that I can receive the packets when they're not a direct response (above mentioned "dummy" packets) makes me think it may be a timing issue. Please advise - it must be possible somehow to receive these packets.

Best regards

  • Hi,

    Have you tried removing the logging from your code? Processing the logs will normally take quite some time, which may prevent you from receiving next packet if it is transmitted close to the first packet.

    If you want to process the incoming messages and send it over a log interface, while still being able to receive most packets, you should run the RADIO event handling in an interrupt handler, where you simply clear the END event, set a new buffer for the radio, and trigger the task to restart radio RX. You can then process the previous buffer in main context. If a new packet is received, the interrupt will cause the CPU to jump to the interrupt handler to service the radio events, before continuting the processing of the buffers in main.

    Best regards,
    Jørgen

  • Hello,

    thanks for the reply. This is a possibility. I did try putting the logging behind an if statement, such that I would only get a log if S0 matched that of an ADV_CONNECT_REQ packet, but it didn't help. I abandoned that idea to follow your advice regarding the interrupt. This is my code now:

    uint8_t buffer[5][40];
    uint8_t current_buffer=0;
    
    void receiver(void)
    {
      uint8_t last_buffer=0;
      radio_init_adv(37, RADIO_TXPOWER_TXPOWER_0dBm);
      NRF_RADIO->INTENSET=RADIO_INTENSET_END_Enabled << RADIO_INTENSET_END_Pos; //Enable Interrupt
      NVIC_SetPriority(RADIO_IRQn, 1);
      NVIC_ClearPendingIRQ(RADIO_IRQn); 
      NVIC_EnableIRQ(RADIO_IRQn);
      NRF_LOG_INFO("Start!");
      NRF_LOG_FLUSH();
    
      NRF_RADIO->PACKETPTR = (uint32_t)&buffer[current_buffer];
    
      //Radio enable & wait for ready
      NRF_RADIO->EVENTS_READY = 0U;
      NRF_RADIO->TASKS_RXEN = 1U;
      while (NRF_RADIO->EVENTS_READY == 0U);
    
      //Start listening
      NRF_RADIO->EVENTS_END = 0U;
      NRF_RADIO->TASKS_START = 1U;
    
      while(1)
      {
        if(last_buffer!=current_buffer)
        {
          NRF_LOG_INFO("Packet was received");
          NRF_LOG_INFO("S0: 0x%02X", buffer[last_buffer][0]);
          NRF_LOG_INFO("Length:%u Bytes (0x%02X)", buffer[last_buffer][1], buffer[last_buffer][1]);
          NRF_LOG_INFO("S1: 0x%02X", buffer[last_buffer][2]);
          uint64_t adva=((uint64_t)buffer[last_buffer][8]<<40)+((uint64_t)buffer[last_buffer][7]<<32)+((uint64_t)buffer[last_buffer][6]<<24)+((uint64_t)buffer[last_buffer][5]<<16)+((uint64_t)buffer[last_buffer][4]<<8)+(uint64_t)buffer[last_buffer][3];
          NRF_LOG_INFO("AdvA: 0x%06X%06X", (adva>>24), (adva&0xFFFFFF));
          NRF_LOG_FLUSH();
          last_buffer=(last_buffer+1)%5;
        }
        //NRF_LOG_INFO("Loop");
        //NRF_LOG_FLUSH();
      }
    }
    
    void RADIO_IRQHandler(void)
    {
      if ((NRF_RADIO->EVENTS_END!=0) && ((NRF_RADIO->INTENSET & RADIO_INTENSET_END_Msk)!=0))
      {
        current_buffer=(current_buffer+1)%5;
        NRF_RADIO->PACKETPTR = (uint32_t)&buffer[current_buffer];
        NRF_RADIO->EVENTS_END = 0U;
        NRF_RADIO->TASKS_START = 1U;
      }
    }

    However it does not work properly. It seems once the interrupt is triggered, the program never jumps back into the while(1) loop. Using the debugger I can see the buffer fill up, though it is impossible to check for ADV_CONNECT_REQ this way. I must be missing something - is clearing the END event not enough? If I uncomment this part:

    NRF_LOG_INFO("Loop");
    NRF_LOG_FLUSH();

    In the while(1) loop, I get a matching ouput in the console only a few times after the device has started, even if there are very few packets on air. Once a packet has been received the program jumps into the interrupt handler and doesn't jump back, instead repeating the interrupt handler over and over. Can you please check over my code? Do you have any examples using the radio interrupts?

    Thanks again and best regards

  • Hello again,

    just to let you know, I think I have found my problem. Here is my updated code:

    uint8_t buffer[5][40];
    uint8_t current_buffer=0;
    
    void receiver(void)
    {
      uint8_t last_buffer=0;
      radio_init_adv(37, RADIO_TXPOWER_TXPOWER_0dBm);
      NRF_RADIO->INTENCLR=0xFFFF;
      NRF_RADIO->INTENSET=RADIO_INTENSET_END_Enabled << RADIO_INTENSET_END_Pos; //Enable Interrupt
      NVIC_SetPriority(RADIO_IRQn, 1);
      NVIC_ClearPendingIRQ(RADIO_IRQn); 
      NVIC_EnableIRQ(RADIO_IRQn);
      NRF_LOG_INFO("Start!");
      NRF_LOG_FLUSH();
    
      NRF_RADIO->PACKETPTR = (uint32_t)&buffer[current_buffer];
    
      //Radio enable & wait for ready
      NRF_RADIO->EVENTS_READY = 0U;
      NRF_RADIO->TASKS_RXEN = 1U;
      while (NRF_RADIO->EVENTS_READY == 0U);
    
      //Start listening
      NRF_RADIO->EVENTS_END = 0U;
      NRF_RADIO->TASKS_START = 1U;
    
      while(1)
      {
        if(last_buffer!=current_buffer)
        {
          NRF_LOG_INFO("Packet was received");
          NRF_LOG_INFO("S0: 0x%02X", buffer[last_buffer][0]);
          NRF_LOG_INFO("Length:%u Bytes (0x%02X)", buffer[last_buffer][1], buffer[last_buffer][1]);
          NRF_LOG_INFO("S1: 0x%02X", buffer[last_buffer][2]);
          uint64_t adva=((uint64_t)buffer[last_buffer][8]<<40)+((uint64_t)buffer[last_buffer][7]<<32)+((uint64_t)buffer[last_buffer][6]<<24)+((uint64_t)buffer[last_buffer][5]<<16)+((uint64_t)buffer[last_buffer][4]<<8)+(uint64_t)buffer[last_buffer][3];
          NRF_LOG_INFO("AdvA: 0x%06X%06X", (adva>>24), (adva&0xFFFFFF));
          NRF_LOG_FLUSH();
          last_buffer=(last_buffer+1)%5;
        }
        //NRF_LOG_INFO("Loop");
        NRF_LOG_FLUSH();
      }
    }
    
    void RADIO_IRQHandler(void)
    {
      if ((NRF_RADIO->EVENTS_END!=0) && ((NRF_RADIO->INTENSET & RADIO_INTENSET_END_Msk)!=0))
      {
        current_buffer=(current_buffer+1)%5;
        NRF_RADIO->PACKETPTR = (uint32_t)&buffer[current_buffer];
        NRF_RADIO->EVENTS_END = 0U;
        NRF_RADIO->TASKS_START = 1U;
      }
       //NRF_LOG_INFO("INTENSET: 0x%04X", NRF_RADIO->INTENSET);
       //NRF_LOG_FLUSH();
    }

    I figured out that even if I didn't set TASKS_START in my interrupt handler, I would still get stuck. Meaning the interrupt must be triggered some other way or not properly cleared. Logging INTENSET in my interrupt handler revealed that it was set to 0x001A. Not sure why or when the other Events were set to trigger an interrupt, but setting INTENCLR to 0xFFFF before using INTENSET to enable the END interrupt solved my issue regarding getting stuck in the handler.

    And you were correct about this being the superior approach: using interrupts rather than while loops like I did originally enabled me to receive those ADV_CONNECT_REQ packets I was interested in:

    <info> app: Packet was received
    <info> app: S0: 0x45
    <info> app: Length:34 Bytes (0x22)
    <info> app: S1: 0x00
    <info> app: AdvA: 0x471EEC3914E8

    Thanks again and best regards

Related