Nano-RK BMAC API

For more information about how bmac works, please refer to the basic_bmac project.

Global bmac defines located in nano-RK/src/net/bmac/bmac.h:

1#define BMAC_MAX_PKT_SIZE               116
2#define BMAC_STACK_SIZE                 128     
3#define BMAC_MIN_CHECK_RATE_MS          20 
4#define BMAC_DEFAULT_CHECK_RATE_MS      100 
5#define BMAC_TASK_PRIORITY              20 

  • void bmac_task_config ()
    • This function configures the BMAC task. This should be called before Nano-RK starts up.
  • int8_t bmac_init(uint8_t chan)
    • This function starts BMAC. chan refers to the radio channel that packets will be sent on. Returns 1 upon success, -1 otherwise.
  • int8_t bmac_started()
    • This function checks to see if BMAC has started. Use this when you use BMAC from multiple tasks to check for startup race conditions. Returns 1 upon success, -1 otherwise. For instance a task that uses BMAC, but does not initialize it might call this at startup:
      1  // Wait until the tx_task starts up bmac
      2  // This should be called by all tasks using bmac that
      3  // do not call bmac_init()...
      4  while(!bmac_started()) nrk_wait_until_next_period();
      
  • int8_t bmac_set_channel(uint8_t chan)
    • This function sets the radio channel the BMAC operates on.
  • int8_t bmac_rx_pkt_set_buffer(uint8_t *buf, uint8_t size);
    • Pass this function a buffer that BMAC can use to store the next incomming packet. Many users will not need to worry about changing buffers. This function is to allow advanced users to manage multiple buffers quickly without copying. Returns 1 upon success.
  • int8_t bmac_tx_pkt(uint8_t *buf, uint8_t len)
    • This function transmits a packet. BMAC will not queue packets, so calling this function from multiple tasks concurrently will return an error code. A value of 1 is returned if the packet was transmitted. -1 is returned upon failure. Upon successful queuing of the packet, the task calling this function will suspend until the packet is transmitted. To deal with multiple tasks transmitting, it is possible to queue packets based on task priority. If an error is returned, simply wait for the BMAC_TX_DONE_PACKET_EVENT. This will be sent by b-mac once the last packet is transmitted. Since tasks waiting on an event are woken up in order of priority, the highest priority tasks will get access to the medium first.
      1     // For blocking transmits, use the following function call.
      2    // For this there is no need to register  
      3     val=bmac_tx_pkt(tx_buf, strlen(tx_buf));
      4     if(val==NRK_OK) cnt++;
      5     else nrk_kprintf( PSTR( "NO ack or Reserve Violated!\r\n" ));
      
  • int8_t bmac_tx_pkt_nonblocking(uint8_t *buf, uint8_t len)
    • This is a non-blocking version of the standard bmac_tx_packet(). It returns NRK_OK if the packet was enqueued and NRK_ERROR if the packet could not be queued.
       1nrk_sig_t tx_done_signal;
       2 tx_done_signal=bmac_get_tx_done_signal();
       3
       4 ...
       5
       6 cnt = 0;
       7  while (1) {
       8    // Build a TX packet
       9    sprintf (tx_buf, "This is a test %d", cnt);
      10    cnt++;
      11    nrk_led_set (BLUE_LED);
      12
      13    // This function shows how to transmit packets in a
      14    // non-blocking manner  
      15    val = bmac_tx_pkt_nonblocking(tx_buf, strlen (tx_buf));
      16    nrk_kprintf (PSTR ("Tx packet enqueued\r\n"));
      17
      18    // This functions waits on the tx_done_signal
      19    ret = nrk_event_wait (SIG(tx_done_signal));
      20
      21    // Just check to be sure signal is okay
      22    if(ret & SIG(tx_done_signal) == 0 ) 
      23        nrk_kprintf (PSTR ("TX done signal error\r\n"));
      24
      25    // Task gets control again after TX complete
      26    nrk_kprintf (PSTR ("Tx task sent data!\r\n"));
      27    nrk_led_clr (BLUE_LED);
      28    nrk_wait_until_next_period ();
      29  }
      30
      
  • uint8_t *bmac_rx_pkt_get(uint8_t *len, int8_t *rssi)
    • This function returns a pointer to the buffer containing a newly received packet. len is set to the length of the returned packet. rssi is set to the raw RSSI value from the radio (add 45 for dB). On failure, this function returns NULL (if for example a packet has not been received since the last bmac_rx_packet_release() call). Typically you should check bmac_rx_pkt_ready() and bmac_wait_until_rx_pkt() before calling this function.
       1   uint8_t len,val;
       2   int8_t rssi; // don't forget rssi is signed!
       3   uint8_t *local_rx_buf;
       4   ...
       5
       6   // check if a packet is ready or wait for it
       7   if( bmac_rx_pkt_ready()==0)
       8       val=bmac_wait_until_rx_pkt();    
       9   // Get the RX packet 
      10   local_rx_buf=bmac_rx_pkt_get(&len,&rssi);
      11   // print it out as ascii text
      12   printf( "Got RX packet len=%d RSSI=%d [",len,rssi );
      13   for(i=0; i<len; i++ )
      14        printf( "%c", local_rx_buf[i]);
      15   printf( "]\r\n" );
      16   // Release the RX buffer so future packets can arrive 
      17   bmac_rx_pkt_release();
      
  • int8_t bmac_rx_pkt_ready(void)
    • This function returns 1 if there is a queued RX packet and 0 otherwise.
  • int8_t bmac_rx_pkt_release(void)
    • This function releases the RX buffer so that new packets can be received. This function should be called quickly after a packet is received to avoid dropping data. Returns 1 upon success.
  • int8_t bmac_wait_until_rx_pkt()
    • This function will wait until a packet is received. Timeouts can be implemented using the nrk_set_next_wakeup() function. Returns 1 upon success, -1 if another signal woke the task up.
  • int8_t bmac_set_rx_check_rate(nrk_time_t check_period)
    • This function changes the check rate of bmac down to BMAC_MIN_CHECK_RATE defined in bmac.h. It takes check_period which is of type nrk_time_t as a parameter which configures how often bmac checks the channel, and how long bmac must transmit to grab hold of the channel. NRK_OK is returned upon success and NRK_ERROR is returned if the check rate is too small.
      nrk_time_t check_period;
      ...
      check_period.secs=0;
      check_period.nano_secs=200*NANOS_PER_MS;
      val=bmac_set_rx_check_rate(check_period);
      
  • int8_t bmac_set_cca_thresh(int8_t threshold)
    • This function changes the low-level radio Clear Channel Assessment.. This controls how sensitive the radio is to overhearing communication in progress. This may need to be adjusted for different antenna configurations. The default value is -32. Lower values make the receiver more sensitive and likely to overhear noise by accident. Larger values make the receiver less sensitive, but will require a stronger signal in order for the packet to be received. If the value is set too high or too low, performance will suffer. This function returns NRK_OK on success and NRK_ERROR on failure.
  • void bmac_set_cca_active(int8_t active)
    • This function enables or disables low-level radio Clear Channel Assessment. Setting active to true enables CCA, while setting it to false disables CCA. By default CCA is active.
  • int8_t bmac_set_rf_power(uint8_t power)
    • This function sets the radio power level. It accepts a value between 1 and 32 with 32 being the max and default level.
  • nrk_sig_t bmac_get_tx_done_signal()
    • Returns the signal sent when a bmac packet is transmitted.
  • nrk_sig_t bmac_get_rx_pkt_signal()
    • Returns the signal sent when bmac receives a packet.
    • Below is an example of how to receive packet with a timeout
       1nrk_sig_t rx_signal;
       2nrk_time_t timeout;
       3
       4...  // Setup b-mac as usual
       5
       6  rx_signal = bmac_get_rx_pkt_signal ();
       7  nrk_signal_register (rx_signal);
       8
       9  // 5 second timeout on RX packets
      10  timeout.secs = 5;
      11  timeout.nano_secs = 0;
      12
      13while(1)
      14{
      15  // Wait until an RX packet is received or until the timeout expires
      16  nrk_set_next_wakeup (timeout);
      17  my_sigs = nrk_event_wait (SIG (rx_signal) | SIG (nrk_wakeup_signal));
      18
      19  if (my_sigs & SIG (rx_signal)) {
      20
      21        // Get the RX packet
      22        local_rx_buf = bmac_rx_pkt_get (&len, &rssi);
      23        // Process packet here...
      24
      25        bmac_rx_pkt_release ();
      26   } else
      27   {
      28   nrk_kprintf( PSTR("Packet RX timeout\r\n" ));
      29   }
      30
      31}
      32
      
  • void bmac_disable()
    • This function disables bmac. Use this to save power.
  • void bmac_enable()
    • This function enables bmac once it has been disabled. By default, bmac is enabled upon startup.

The application or network layer is required to allocate static buffers. These should typically be globally defined at the top of your application:

1   uint8_t tx_buf[RF_MAX_PAYLOAD_SIZE];
2   uint8_t rx_buf[RF_MAX_PAYLOAD_SIZE];

Here is a sample of how you initialize BMAC and receive data (found in the basic_bmac sample project):

 1void rx_task()
 2{
 3  uint8_t i,len;
 4  int8_t rssi,val;
 5  uint8_t *local_rx_buf;
 6
 7  printf( "rx_task PID=%d\r\n",nrk_get_pid());
 8
 9  // init bmac on channel 25 
10  bmac_init(25);
11  // This sets the next RX buffer.
12  // This can be called at anytime before releaseing the packet
13  // if you wish to do a zero-copy buffer switch
14  bmac_rx_pkt_set_buffer(rx_buf,RF_MAX_PAYLOAD_SIZE);
15
16  while(1)
17    {
18    // Wait until an RX packet is received
19    val=bmac_wait_until_rx_pkt();    
20    // Get the RX packet 
21      nrk_set_led(BLUE_LED); 
22    local_rx_buf=bmac_rx_pkt_get(&len,&rssi);
23    printf( "Got RX packet len=%d RSSI=%d [",len,rssi );
24        for(i=0; i<len; i++ )
25                   printf( "%c", local_rx_buf[i]);
26        printf( "]\r\n" );
27    // Release the RX buffer so future packets can arrive 
28      nrk_clr_led(BLUE_LED); 
29    bmac_rx_pkt_release();
30    }
31
32}

Here is an example of how you would transmit data using BMAC:

 1void tx_task()
 2{
 3  uint8_t j, i,val,len,cnt;
 4
 5  printf( "tx_task PID=%d\r\n",nrk_get_pid());
 6
 7  // Wait until the tx_task starts up bmac
 8  // This should be called by all tasks using bmac that
 9  // do not call bmac_init()...
10  while(!bmac_started()) nrk_wait_until_next_period();
11  cnt=0;
12  while(1)
13    {
14    // Build a TX packet
15    sprintf( tx_buf, "This is a test %d",cnt );
16    cnt++;
17    // Transmit the packet 
18      nrk_set_led(ORANGE_LED); 
19    val=bmac_tx_packet(tx_buf, strlen(tx_buf));
20    // Task gets control again after TX complete
21      nrk_clr_led(ORANGE_LED); 
22    printf( "TX task sent data!\r\n" );
23    nrk_wait_until_next_period();
24    }
25
26}

Encryption

Make sure to call all of these functions after bmac_init() has been called.

  • int8_t bmac_encryption_set_key(uint8_t *key, uint8_t len);
    • This function sets the 128 bit AES encryption key. The length of the key is always 16 bytes, but the value is required anyways as a sanity check. If the node you are communicating with has the same key, it will be able to decrypt the packet. This function return NRK_OK upon success and NRK_ERROR on failure.
  • int8_t bmac_encryption_enable();
    • Turn on the hardware encryption for all outgoing packets. You must set the key using bmac_encryption_set_key(). This function return NRK_OK upon success and NRK_ERROR on failure.
  • int8_t bmac_encryption_disable();
    • This function will disable encryption on outgoing packets. Incoming encrypted packets will still be decrypted using the last key. This function return NRK_OK upon success and NRK_ERROR on failure.
  • int8_t bmac_rx_pkt_is_encrypted();
    • This function can be used to determine if the last packet was encrypted or in plain text. This should be used before you release the packet. This function returns 1 if the packet was encrypted and 0 otherwise.
  • int8_t bmac_encryption_set_ctr_counter(uint8_t *CTR_buf,uint8_t len);
    • This function sets the CTR frame counter to make sure that the counting stream cipher does not repeat. Make sure to change this for each packet. The MAC address of the node is also used to seed this value to help avoid repeats. The CTR register is 4 bytes long, so len should always be set as 4 for the cc2420 hardware. This function returns NRK_OK upon success and NRK_ERROR otherwise.

Hardware Packet Control

This section talks about some advanced features enabled by 802.15.4 radios that can be used to optimize communication. Make sure to call all of these functions after bmac_init() has been called.

  • int8_t bmac_auto_ack_disable()
    • This function disables the automatic ACKs feature of the radio. This function return NRK_OK upon success.
  • int8_t bmac_auto_ack_enable()
    • This function enables the radio level automatic acknowledgment mechanism found in the CC2420 radio. When this mode is enabled, the radio will automatically send an ACK message in reply to an incoming message if the message correctly passed address decoding. This ACK happens immediately after packet reception at very little energy penalty compared to a normal packet transmission since it does not require an extended preamble. In a broadcast domain, the ACK's will typically collide. To avoid this, you must use automatic address decoding along with auto ACKs. Once this mode is enabled (along with address decoding) then bmac_tx_packet() will return NRK_ERROR if an ACK was not received or NRK_OK if the packet was sent and acknowledged. To enable auto acks do the following:
 1// set my MAC address to 1
 2#define MY_MAC_ADDR        0x0001
 3
 4    uint16_t dst_addr;
 5
 6    ...
 7    bmac_addr_decode_set_my_mac(MY_MAC_ADDR);   // MY_MAC_ADDR should be a uint16_t local address within your network
 8
 9    bmac_addr_decode_enable();
10    bmac_auto_ack_enable();
11    ...
12
13    dst_addr=0x0003;  // send packet to node address 3
14    bmac_addr_decode_dest_mac(dst_addr);  // 0xFFFF is broadcast (keep in mind broadcast ACKs will collide)
15                                          // This function can be called right before each bmac packet transmission 
16    val=bmac_tx_packet(tx_buf, strlen(tx_buf)+1);
17
  • int8_t bmac_addr_decode_disable()
    • This function disabled automatic address decoding. This function return NRK_OK upon success.
  • int8_t bmac_addr_decode_enable()
    • This function enables radio level packet decoding. This allows the radio to selectively process or disregard packets before signaling higher layers of the radio stack. This can either be used as a low-level filter to save energy, or to support automatic acknowledgments. This function return NRK_OK upon success.
  • int8_t bmac_addr_decode_set_my_mac(uint16_t my_mac)
    • Call this function once after bmac has been initialized to set a node's local MAC address. The address should be a 16 bit value unique to the current network subnet. This function return NRK_OK upon receiving a valid address.
  • int8_t bmac_addr_decode_dest_mac(uint16_t dest)
    • This function sets the destination address in the packet header that is read by nodes using automatic address decoding. Call this function immediately before transmitting a bmac packet if address decoding is desired. 0xffff indicates a broadcast that is accepted by all nodes. This function return NRK_OK upon receiving a valid address.

Debugging

  • uint16_t bmac_rx_failure_count_get()
    • This function internally keeps track of how many listen failures occur during operation. Once this value saturates, it will not roll over and begin to lose track of new failures. Make sure to periodically call bmac_rx_failure_count_reset(). Transmit attempts can be recorded at the layer above bmac.
  • uint8_t bmac_rx_failure_count_reset()
    • This function resets the value of the rx failure counter.

Power Optimizations

  • CC2420 Oscillator
    • When using the CC2420, it is possible to save on the idle energy of the radio by disabling the main oscillator. This will in turn increase the TX and RX delay by 1ms to allow the oscillator time to spin up. To enable this mode, add the following to nrk_cfg.h, or any networking code that uses basic_rf.c:
      #define CC2420_OSC_OPT