A good supermasive headache.

Standard

Didn’t get the topic? Me neither. Drop it. Well, in this post (which, as a precursor, will be quite technical and ridiculously long) we will look at how to write a firmware for the Texas Instrument’s CC2540/41 Bluetooth Low Energy (BLE) SoC (System on Chip). The bad thing about this post is, I have very less resources that I can link my readers to for clarification but if you email me, I can send you stuff (but if you Google effectively, you can find ’em stuff anyway). As the final line of the opening paragraph, please note that this is not a guide or a stepwise how-to. I write this so that I can better understand what I’m doing right now. I just wanted someone to simply nod and say they understand and wordpress does that. Thats’ quite cool… .

I have assumed many things that a reader (and even I) maybe unaware of. I kindly ask, an experienced/knowledgeable reader to guide me if I go wrong somewhere (or guide me nonetheless, I have just started working on this and I do need someone from whom I can learn.).

So enough with my rambling *takes a drag of cigarette*, lets commence. This will be quite long, so if you have nothing better to do, do nothing instead of reading this. Defining a backdrop, I got this new project at work where I have to research on developing mobile accessories using the Bluetooth technology. Right now, the aims are modest. I simply used this development kit and interfaced it with an Arduino Uno (everyone knows what Arduino is). The good thing about the dev kit by RedBear labs is that it is ready to use out of the box. Install their iPhone/Android app on your respective phones, wire your circuit according to the instructions given in the above link and you’re happily on your way. It is fun for about 2 minutes (believe me, I know).

After having fun (for 2 mins) lets see what Bluetooth is. Not Bluetooth but Bluetooth. Bluetooth is technology, developed at Ericsson as a replacement for wires. Since it was conceived, it has seen 4 versions with Bluetooth 4.0 being the latest. Bluetooth 4.0 is what they call Bluetooth Low Energy or BLE for short. Low energy because a BLE device can run for decades on a single coin cell battery. I won’t go into hows and whats of Bluetooth, you can read about them in the Bluetooth SIG I linked above.

Directly jumping to the jargons now (we do have quite a few… sigh). Bluetooth (BLE hereon), is divided into 2 parts: the Host and the Controller. The host is a logical entity that executes upper half of BLE protocol stack. It consists of “profiles” (yes, that. I’ll define this in the next paragraph). Controller is another logical entity that executes lower half of the protocol stack. It includes the radio, the link layer, the baseband etc. (yes, and those.).

A profile is a definition of possible application specifying general behavior that BLE enabled devices use to communicate with other BLE devices. At a minimum, each profile contains info on the following:

  • dependencies on other profiles.
  • suggested UI formats.
  • specific parts of the Bluetooth protocol used by the profiles. To perform its task, each profile uses particular options and parameters at each layer of the stack. The above wiki link tells us that there are numerous profiles in existence and one can make vendor specific profile for themselves.

Before reading further, I strongly suggest that you read about GAP (Generic Access Profile) and GATT (Generic Attribute Profile) *scans through the notes I made*. A few more jargons… I promise! Then the coding.

Central and Peripherals and Services.

Peripherals, as the name suggests, are remote devices that interact with the surroundings (like a barometer or an accelerometer). They collect data and format them into “advertising packets”, i.e. small bundle of data that may contain useful information about what a peripheral has to offer.

A central on the other hand, scans and listens for any peripheral device that is advertising information that it may be interested in.

Peripherals have one or more services. A service is a collection of data and associated behavior for accomplishing a certain feature/function of a device. Each service are themselves made up of “characteristics” and/or included services.

A central device interacts with a peripheral by reading/writing the values of a service’s characteristics. Got it? Awesome… I did not, comment what you understood.

Now as promised, the juice. The SDK provided by Texas Instruments consists of five major sections: the OSAL, the HAL, the BLE stack, profiles and the application. Now, pause here for a moment. Note that the SDK is proprietary and you need IAR Workbench to compile and debug your code. The BLE protocol stack is provided NOT in it’s source form and only IAR Workbench gives support.  Having said that, unpause and lets start with this thing called OSAL.

OSAL is short for Operating System Abstraction Layer. It is not a traditional OS but rather a “control loop” that allows software to setup execution of events. It provides task management, message passing, memory management and timers. For each layer of the software that requires this type of control, a task’s ID must be created, a task processing routine must be defined and added to the OSAL initialization and an even processing routine must be defined.

Like every C program there is a main function. If you have attempted your own OS before (I have! 😀 ), you’ll know that the main function never returns. Similarly, in this case, the OSAL is initialized at the end of the main function and it runs indefinitely. The code (yea, I almost forgot… sorry!).

/* Hal Drivers */
#include "hal_types.h"
#include "hal_key.h"
#include "hal_timer.h"
#include "hal_drivers.h"
#include "hal_led.h"

/* OSAL */
#include "OSAL.h"
#include "OSAL_Tasks.h"
#include "OSAL_PwrMgr.h"
#include "osal_snv.h"
#include "OnBoard.h"

#include "uart.h"
#include "i2c.h"
#include "eeprom.h"
#include "hal_uart.h"

/* This callback is triggered when a key is pressed */
void MSA_Main_KeyCallback(uint8 keys, uint8 state);

int main(void)
{ 
  HAL_BOARD_INIT();
  InitBoard( OB_COLD );
  HalDriverInit();
  osal_snv_init();
  osal_init_system();
  HAL_ENABLE_INTERRUPTS();
  InitBoard( OB_READY );
  #if defined ( POWER_SAVING )
  osal_pwrmgr_device( PWRMGR_BATTERY );
  #endif   
  osal_start_system(); // No Return from here
  #if defined FEATURE_UBL_MSD
  extern void appForceBoot(void);
  appForceBoot();
  #endif
  return 0;
}

So, well… you won’t find the header files anywhere. The good thing is, RedBear Lab has their own firmware called Biscuit which is open source. You can find the source here. I’ll be using their source as an example. But still, it doesn’t solve the problem of all those unknown header files. Well, if you download their source code and import the project into IAR workbench, it automatically links everything up nicely. TI has some docs describing some of those libraries. I’ll post the links as an update later on.

Inside the main function, notice the line called osal_start_system(). It is the function that sets up the OSAL and enters into an infinite loop polling for events and calling event handlers for specific events. Task events are declared as 16-bit flags where each bit corresponds to an unique event. The definition and usage of these event flags are completely up to the application.

I’ll now show the code for the OSAL task initialization routine called osalInitTasks(). It is long and seems like an entirely alien language:

#include "hal_types.h"
#include "OSAL.h"
#include "OSAL_Tasks.h"

/* HAL */
#include "hal_drivers.h"

/* LL */
#include "ll.h"

#if defined ( OSAL_CBTIMER_NUM_TASKS )
  #include "osal_cbTimer.h"
#endif

/* L2CAP */
#include "l2cap.h"

/* gap */
#include "gap.h"
#include "gapgattserver.h"
#include "gapbondmgr.h"

/* GATT */
#include "gatt.h"

#include "gattservapp.h"

/* Profiles */
#if defined ( PLUS_BROADCASTER )
  #include "peripheralBroadcaster.h"
#else
  #include "peripheral.h"
#endif

/* Application */
#include "hci.h"
#include "biscuit.h"

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
  L2CAP_ProcessEvent,                                               // task 4
  GAP_ProcessEvent,                                                 // task 5
  GATT_ProcessEvent,                                                // task 6
  SM_ProcessEvent,                                                  // task 7
  GAPRole_ProcessEvent,                                             // task 8
  GAPBondMgr_ProcessEvent,                                          // task 9
  GATTServApp_ProcessEvent,                                         // task 10
  BiscuitPeripheral_ProcessEvent                                  // task 11
};

const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
uint16 *tasksEvents;
void osalInitTasks( void )
{
  uint8 taskID = 0;
  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
  LL_Init( taskID++ );
  Hal_Init( taskID++ );
  HCI_Init( taskID++ );
  #if defined ( OSAL_CBTIMER_NUM_TASKS )
  osal_CbTimerInit( taskID );
  taskID += OSAL_CBTIMER_NUM_TASKS;
#endif
  L2CAP_Init( taskID++ );
  GAP_Init( taskID++ );
  GATT_Init( taskID++ );
  SM_Init( taskID++ );
  GAPRole_Init( taskID++ );
  GAPBondMgr_Init( taskID++ );
  GATTServApp_Init( taskID++ );  
  BiscuitPeripheral_Init( taskID );
}

Notice how for each layer of the software, an unique task ID is defined. The rule is simple, lower ID means higher priority.

The second major section of the firmware is the HAL or Hardware Abstraction Layer. The Hardware Abstraction Layer (HAL) of the CC2540/41 software provides an interface of abstraction between the physical hardware to and the application and protocol stack. This allows for the development of new hardware (such as a new PCB) without making changes to the protocol stack or application source code. The HAL includes software for the SPI and UART communication interfaces, ADC, keys, and LED’s etc. TI’s software developer’s guide provides no more information about HAL (so, neither will I 😛 ). But you can refer to TI’s HAL API guide here. Appending to this paragraph itself, the third major section BLE protocol stack is also NOT provided in source form. As a matter of policy, TI does not disclose its implementation.

The fourth major section is the profiles section. I’ll be describing the GAP and the GATT profiles as they are mandatory for any application. Source is taken from RedBear Lab’s Biscuit firmware.The GAP layer of the BLE Protocol Stack is responsible for handling the device’s access modes and procedures, including device discovery, link establishment, link termination, initiation of security features, and device configuration. GAP layer is always in one of the following four states:

  • Broadcaster: just advertises data. The connection is not connectible.
  • Observer: scans for advertisements but cannot initiate a connection.
  • Peripheral: an advertiser that is connectible and operates as a slave in a single link layer connection.
  • Central: scans for advertisements and initiates connections. A central operates as a master in a single or a multi link-layer connections.

In a typical BLE system, the peripheral device advertises with specific data letting any central device know that it is a connectable device. This advertisement contains the device address, and can contain some additional data as well, such as the device name. The central device, upon receiving the advertisement, sends a “scan request” to the peripheral. The peripheral responds with a “scan response”. This is the process of device discovery, in that the central device is now aware of the peripheral device, and knows that it can form a connection with it.

The application and profiles call the GAP and GATT API functions to perform BLE related functions such as advertising, connecting and reading/writing characteristics. I’ll try and elucidate the GAP profile in the peripheral role (I’m actually pausing to look at my notes because it IS technical… and I’m a fantasy writer). GAP in the peripheral role provides means for the dev kit to advertise, connect with a central device, and request a set of connection parameters from a master device. The GAP API (GATT API comes later) defines certain GAP peripheral role parameters. The following has defines are taken from “peripheral.c” found in the TI’s CC2540 software development kit (do yourself a favor, get a hold of it somehow, I cannot recall the github link from where I got the whole package, I’ll surely post it later on).

#define GAPROLE_PROFILEROLE         0x300  //!< Reading this parameter will return GAP Role type. Read Only. Size is uint8.
#define GAPROLE_IRK                 0x301  //!< Identity Resolving Key. Read/Write. Size is uint8[KEYLEN]. Default is all 0, which means that the IRK will be randomly generated.
#define GAPROLE_SRK                 0x302  //!< Signature Resolving Key. Read/Write. Size is uint8[KEYLEN]. Default is all 0, which means that the SRK will be randomly generated.
#define GAPROLE_SIGNCOUNTER         0x303  //!< Sign Counter. Read/Write. Size is uint32. Default is 0.
#define GAPROLE_BD_ADDR             0x304  //!< Device's Address. Read Only. Size is uint8[B_ADDR_LEN]. This item is read from the controller.
#define GAPROLE_ADVERT_ENABLED      0x305  //!< Enable/Disable Advertising. Read/Write. Size is uint8. Default is TRUE=Enabled.
#define GAPROLE_ADVERT_OFF_TIME     0x306  //!< Advertising Off Time for Limited advertisements (in milliseconds). Read/Write. Size is uint16. Default is 30 seconds.
#define GAPROLE_ADVERT_DATA         0x307  //!< Advertisement Data. Read/Write. Max size is uint8[B_MAX_ADV_LEN].  Default is "02:01:01", which means that it is a Limited Discoverable Advertisement.
#define GAPROLE_SCAN_RSP_DATA       0x308  //!< Scan Response Data. Read/Write. Max size is uint8[B_MAX_ADV_LEN]. Defaults to all 0.
#define GAPROLE_ADV_EVENT_TYPE      0x309  //!< Advertisement Type. Read/Write. Size is uint8.  Default is GAP_ADTYPE_ADV_IND (defined in GAP.h).
#define GAPROLE_ADV_DIRECT_TYPE     0x30A  //!< Direct Advertisement Address Type. Read/Write. Size is uint8. Default is ADDRTYPE_PUBLIC (defined in GAP.h).
#define GAPROLE_ADV_DIRECT_ADDR     0x30B  //!< Direct Advertisement Address. Read/Write. Size is uint8[B_ADDR_LEN]. Default is NULL.
#define GAPROLE_ADV_CHANNEL_MAP     0x30C  //!< Which channels to advertise on. Read/Write Size is uint8. Default is GAP_ADVCHAN_ALL (defined in GAP.h)
#define GAPROLE_ADV_FILTER_POLICY   0x30D  //!< Filter Policy. Ignored when directed advertising is used. Read/Write. Size is uint8. Default is GAP_FILTER_POLICY_ALL (defined in GAP.h).
#define GAPROLE_CONNHANDLE          0x30E  //!< Connection Handle. Read Only. Size is uint16.
#define GAPROLE_RSSI_READ_RATE      0x30F  //!< How often to read the RSSI during a connection. Read/Write. Size is uint16. The value is in milliseconds. Default is 0 = OFF.
#define GAPROLE_PARAM_UPDATE_ENABLE 0x310  //!< Slave Connection Parameter Update Enable. Read/Write. Size is uint8. If TRUE then automatic connection parameter update request is sent. Default is FALSE.
#define GAPROLE_MIN_CONN_INTERVAL   0x311  //!< Minimum Connection Interval to allow (n * 1.25ms).  Range: 7.5 msec to 4 seconds (0x0006 to 0x0C80). Read/Write. Size is uint16. Default is 7.5 milliseconds (0x0006).
#define GAPROLE_MAX_CONN_INTERVAL   0x312  //!< Maximum Connection Interval to allow (n * 1.25ms).  Range: 7.5 msec to 4 seconds (0x0006 to 0x0C80). Read/Write. Size is uint16. Default is 4 seconds (0x0C80).
#define GAPROLE_SLAVE_LATENCY       0x313  //!< Update Parameter Slave Latency. Range: 0 - 499. Read/Write. Size is uint16. Default is 0.
#define GAPROLE_TIMEOUT_MULTIPLIER  0x314  //!< Update Parameter Timeout Multiplier (n * 10ms). Range: 100ms to 32 seconds (0x000a - 0x0c80). Read/Write. Size is uint16. Default is 1000.
#define GAPROLE_CONN_BD_ADDR        0x315  //!< Address of connected device. Read only. Size is uint8[B_MAX_ADV_LEN]. Set to all zeros when not connected.
#define GAPROLE_CONN_INTERVAL       0x316  //!< Current connection interval.  Read only. Size is uint16.  Range is 7.5ms to 4 seconds (0x0006 to 0x0C80).  Default is 0 (no connection).
#define GAPROLE_CONN_LATENCY        0x317  //!< Current slave latency.  Read only.  Size is uint16.  Range is 0 to 499. Default is 0 (no slave latency or no connection).
#define GAPROLE_CONN_TIMEOUT        0x318  //!< Current timeout value.  Read only.  size is uint16.  Range is 100ms to 32 seconds.  Default is 0 (no connection).
#define GAPROLE_PARAM_UPDATE_REQ    0x319  //!< Slave Connection Parameter Update Request. Write. Size is uint8. If TRUE then connection parameter update request is sent.

The comments alongside of the defines are quite self explanatory if you know the jargons well (Google helped me a lot with this.).

The GAP peripheral role uses callbacks to notify the application of events. The GAP API defines these callbacks as a typedef (gapRoleCBs_t) which is a structure containing two function pointers:

  • pfnStateChange – This function gets called every time the GAP changes states, with the new state of the GAP passed as a parameter.
  • pfnRssiRead – This function gets called every time the RSSI has been read, with the RSSI value passed as a parameter.

It is up to the application to provide the actual definitions of the callbacks (see? Hints of object oriented programming in C.).

The GATT layer of the BLE protocol stack is designed to be used by the application for data communication between two connected devices. From a GATT standpoint, when two devices are connected they are each in one of two roles:

  • GATT Client – This is the device that is reading/writing data from/to the GATT Server.
  • GATT Server – This is the device containing the data that is being read/written by the GATT Client.

It is important to note that the GATT roles of Client and Server are completely independent from the BLE link-layer roles of slave and master, or from the GAP peripheral and central roles. A slave can be either a GATT Client or a GATT Server, and a master can be either a GATT client or a GATT Server. The software developer’s guide demonstrates the GATT profile using an example application. I cannot possibly post a friggin’ 1000 line code and explain it line-wise.

Well, I know the post doesn’t come up to the mark but I wanted it to be a reflection of my understanding of last few days of intensive reading. The next step would be to implement my own profile. That post will be up to the mark as I will dissect every line of code.

May the force be with thee.

Ninja.

Advertisements

5 thoughts on “A good supermasive headache.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s