Updates on iBeacons with the CC2540

 In 8051, Bluetooth Low Energy, embedded C, Embedded Systems, iBeacon

One of this year’s projects was taking a CC2540 and making an interactive iBeacon with it.  This turned out to be more difficult than expected… which may explain recent design trends I’ve noticed in embedded BLE devices.

The good news is that I was able to leverage the code I wrote for the CC2541 previously.  This project was a little more complicated.  It has to take commands over the asynchronous serial interface (generally known as a serial port, serial bus, or UART) and make changes to how the device is beaconing.  I also upgraded to version 1.4.1 of the BLE stack.

TI’s BLE Ecosystem

When working on the TI CC2540 or CC2541, there are five code bodies (see the Developer’s Guide for more details):

  1. BLE Stack – This is closed source and shipped as a binary with headers.
  2. OSAL – TI’s Operating System Abstraction Library
  3. HAL – Hardware Abstraction Layer
  4. Profiles – These are used in the different example projects
  5. Application – These are used in the different example projects

Since we are doing an iBeacon, #4 and $5 are already chosen for us.  We only have to worry about the OSAL and HAL layers.  The example programs have a round-robin event handler that uses the OSAL and HAL layers.

Becoming Interactive

The first thing we had to do was figure out how to handle the new event of updating the

iBeacon.  The best way we found to do that was to make a copy of the simpleBLEBroadcaster.c file to use as our template (found in Projects\ble\SimpleBLEBroadcaster\Source of the BLE stack download) .  After changing the function and variable names, we added the initialization function and handler to the main program loop.

.
const pTaskEventHandlerFn tasksArr[] =
{
LL_ProcessEvent, // task 0
Hal_ProcessEvent, // task 1
HCI_ProcessEvent, // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )
OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 3
#endif
GAP_ProcessEvent, // task 4
GAPRole_ProcessEvent, // task 5
SimpleBLEBroadcaster_ProcessEvent, // task 6
CommandProcessor_ProcessEvent // task 7 - We added this
};

void osalInitTasks( void )
{
uint8 taskID = 0;

tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

/* LL Task */
LL_Init( taskID++ );

/* Hal Task */
Hal_Init( taskID++ );

/* HCI Task */
HCI_Init( taskID++ );

#if defined ( OSAL_CBTIMER_NUM_TASKS )
/* Callback Timer Tasks */
osal_CbTimerInit( taskID );
taskID += OSAL_CBTIMER_NUM_TASKS;
#endif

/* GAP Task */
GAP_Init( taskID++ );

/* Profiles */
GAPRole_Init( taskID++ );

/* Application */
SimpleBLEBroadcaster_Init( taskID++ );
CommandProcessor_Init( taskID); //We added this
}

The low level code is already written in the HAL for handling the serial port.  We just have to enable it and tie in our new event to read the data from the serial port and act on it. Getting everything to this point was easier than I expected.  Now for testing.

Timing is Everything

During initial testing of the code, everything looked good on my end.  I kept receiving reports from my customer that the device stopped beaconing after receiving commands.

[alert type=”warning”] When troubleshooting an embedded system, you need to have a logic analyzer to see when and what is going across your serial port.[/alert]

I could never get the customer to put a logic analyzer on the serial bus to see what was actually going on.  After going back and forth for weeks, I was finally able to repeatably get the beacon to fail.  I wrote a python script that created a text file with hundreds of commands, some slightly mangled, to test the device and command processor with.  If I sent the data across the serial bus at full line speed, I could get the device to stop transmitting within a minute.  I could do that reliably.  As I slowed down the transmission speed (not the bus speed, but the number of characters per second) the device would last for a longer time.  What was interesting was that the device had stopped beaconing but would still respond to commands over the serial bus.

Putting Two and Two Together

So, we found that how long the device will beacon for is inversely related to how fast we are sending it commands.  We also have a cooperative (vs. preemptive) event handler.  This sounds like we are missing the required timing for the BLE stack and radio.  Do I know this? No.  Can I prove this? No.  I don’t have the source, so I’m not sure what is going on in there.  However, it is very plausible.  Looking around, there are several threads on TI’s e2e site talking about these issues.  I also didn’t find a way to tell if the radio had stopped transmitting.

Fixing It

We tried a few ways to keep the radio up.  To update some of the beacon parameters we were writing to non-volatile memory (NVM) and then rebooting to feed the new parameter to the BLE stack when it is brought up.  We tried doing this for all the parameters, but found that we were likely to hang things by shoving characters into the serial bus when the device rebooted.

We looked at using hardware handshaking on the serial bus.  This is more complicated than expected because the HAL driver does not support hardware handshaking for the CC2540.  I wasn’t convinced that this will solve all our problems because I think it is the time required to process the commands that is killing things.

My gut feeling is that the command processing routine needs to be broken down into several events.  These events need to be ordered so that going from the first to second event causes all other processes to be services before getting to the next step.  To do this we need to insert the events into the event queue in reverse order. Here’s an example of how the initialization functions would be aligned.

.
/* Application */
SimpleBLEBroadcaster_Init( taskID++ );
ChangeBleState_Init( taskID++);
CommandProcessor_Init( taskID++);
ParseCommand_Init( taskID++);
ServiceUART_Init( taskID++);

The other option is to throw out the BLE stack.  We just need software that will tell the radio to send out a series of bits every so often at certain frequencies with a certain modulation scheme.  We don’t have to worry about connecting or profiles.  If you strip it down, It can be pretty lean and robust.

How Vendors are Fixing It

One of the trends I’ve noticed in single “chip” solutions for BLE is that they are now multi-processor.  The TI CC2640 is an example of this.  It has an ARM Cortex M0 core that runs the BLE stack and is off-limits to you.  You, the developer, get a spacious ARM Cortex M3 to run your code on.  There are a few other vendors going the same route.  What’s the benefit of this compared to the Cypress dev kit which has a PSoC 4000S and a EZ-BLE PRoC tied together?  In my humble opinion, it’s the total hardware footprint, BoM cost and BLE stack functionality that say which is better… which has always been the case.

Leave a Comment

Contact Us

We're not around right now. But you can send us an email and we'll get back to you, asap.

Start typing and press Enter to search