Summary

In this article I update the AnyCloud BLE advertising scanner to use the btutil library that was created in the previous post.  In addition, I add a command queue to the bluetoothManger and enable a new command to turn on and off scanning.

Story

If you have been following along until now, which I imagine that you have if you are reading this,  you will have gotten a vomit of device data blasting out onto your serial console.  This isn’t very helpful.  So now what?  I am going to divide this problem into two parts

  1. Creating a new user command to turn on and off scanning (this article)
  2. Creating a database to manage the data + a set of commands to dump it (next article)

There are

Article Topic
AnyCloud Bluetooth Advertising Scanner (Part 1) Introduction to AnyCloud Bluetooth Advertising
AnyCloud Bluetooth Advertising Scanner (Part 2) Creating an AnyCloud Bluetooth project
AnyCloud Bluetooth Advertising Scanner (Part 3) Adding Observing functionality to the project
AnyCloud Bluetooth Utilities Library A set of APIs for enhancement of the AnyCloud Library
AnyCloud Bluetooth Advertising Scanner (Part 4) Adding a command line to the scanner
AnyCloud Bluetooth Advertising Scanner (Part 5) Adding a history database to the scanner
AnyCloud Bluetooth Advertising Scanner (Part 6) Decoding advertising packets
AnyCloud Bluetooth Advertising Scanner (Part 7) Adding recording commands to the command line
AnyCloud Bluetooth Advertising Scanner (Part 8) Adding filtering to the scanner
AnyCloud Bluetooth Advertising Scanner (Part 9) Improve the print and add packet age
AnyCloud Bluetooth Advertising Scanner (Part 10) Sort the database

All of the code can be found at git@github.com:iotexpert/AnyCloudBLEScanner.git and https://github.com/iotexpert/AnyCloudBLEScanner.git

There are git tags in place starting at part 5 so that you can look at just that version of the code.  "git tag" to list the tags.  And "git checkout part6" to look at the part 6 version of the code.

You can also create a new project with this is a template if you have the IoT Expert Manifest Files installed

Add the IoT Expert “btutil” Library

Before we actually start all of the command queue stuff, lets move to the btutil library that I talked about in the previous post.  To do this, add the library using the library manager.

Then delete bt_platform_cfg_settings.h and bt_platform_cfg_settings.c from your project.  Finally Rebuild and make sure that everything still works.  That is it.

Multithreading

Id like to explain that there is now some danger.  That danger comes from the fact that we have multiple tasks which are all accessing data plus functions that are talking to each other ASYNCHRONOUSLY.  Specifically we have:

  1. The Bluetooth Stack task – running the Bluetooth stack and management callback
  2. The Bluetooth Stack APIs – e.g. wiced_bt_ble_observe
  3. The usrcmd task – which is interacting with the user on the serial port and talking to the other tasks
  4. A timer_svc task – which runs software timers
  5. The advertising data (which I will start saving in the next article)

When faced with this situation what I typically like to do is provide thread safe public functions for each of the tasks.  Then any other task can call these functions and know that things are not going to get corrupted by a race condition.

To make the design thread safe, I typically like to put an RTOS Queue between the tasks.  These queues are a safe place to send and receive data in a “thread safe” way.  There are two basic design patterns that can be used

  1. Define a message structure (that gets pushed into the queue) and make it global (via a dot-h).  Define a queue handle and make it global (via a dot-h).  Then let any task build messages and push them into the queue to be received in the task that owns the queue.
  2. Define the message structure and queue.  Then define functions which are global (via a dot-h) which know how to interact with the queue.

I typically think that the 2nd method is better, so that is what I am going to do here.

  1. In BluetoothManager.h I will provide a function called “btm_cmdScan”
  2. The usrcmd task will call the btm_cmdScan function which will
  3. Create a btm_cmdMsg_t with the “scan” command and data of true/false
  4. Then push it into the Bluetooth Manager Command Queue
  5. Where a timer callback in the Bluetooth Manager Task will take it out of the queue
  6. Figure out that it is a “scan” command
  7. Then will either turn on or off scanning

Add a Queue to the Bluetooth Manager Thread

So we need two things a message to push into a queue (just a structure) and we need a queue to push it into.  First the message which is just a structure with two elements.  The first element is a command and the second element is some data of type void.  The meaning of the void *data will be different based on the command.

typedef struct {
	btm_cmd_t cmd;
	void *data;
} btm_cmdMsg_t;

But how about the command?  The command is just an enumerate list of commands which will now start with just one command.

typedef enum {
	BTM_SCAN,
} btm_cmd_t;

And know we need to define the queue.

#include "queue.h"
static QueueHandle_t btm_cmdQueue;

Before you can use the queue you need to initialize it.  The best place to initialize this queue is in the management callback right after the stack gets going.  You can see that I tell FreeRTOS that there is a queue which can hold up to 10 commands.  I also tell it that each command is the sizeof the command message.

    switch (event)
    {
        case BTM_ENABLED_EVT:
            printf("Started BT Stack Succesfully\n");
            btm_cmdQueue = xQueueCreate(10,sizeof(btm_cmdMsg_t));

Now we need to create a way for other tasks to create these command messages.  They will do this by calling a function which we will define in the bluetoothManager.h

void btm_cmdScan(bool enable);

This function will live in bluetoothManager.c and it simply

  1. Creates a command
  2. Set the actual command to scan
  3. Sets the void* data to be enable … in other words start or stop scanning.  Remember that a void * can be anything.  See I cast a bool to a void *
  4. Finally push the data into the command queue
void btm_cmdScan(bool enable)
{
    btm_cmdMsg_t msg;
    msg.cmd = BTM_SCAN;
    msg.data = (void *)enable;
  	xQueueSend(btm_cmdQueue, &msg,0);
}

Add a Timer to Process the Queue

So now we have a method to push items into the queue.  How do we get them out of the queue?  To do that I will use a Bluetooth Stack timer that will run every 50ms.

First, define the timer in bluetoothManager.c

#include "wiced_timer.h"
static wiced_timer_ext_t btm_mgmtQueueTimer;

Then define a function which the timer will call.  This function will

  1. Try to get a message out of the queue
  2. IF there is a message it will use a big switch to look at the possible messages
  3. If the message is a scan
  4. Then call the wiced function to either start “observing” or stop “observing”
static void btm_processBluetoothAppQueue()
{
	btm_cmdMsg_t msg;

	 BaseType_t rval;

	 rval = xQueueReceive( btm_cmdQueue,&msg,0);
	 if(rval == pdTRUE)
	 {
		 switch(msg.cmd)
		 {
		 case BTM_SCAN:
            wiced_bt_ble_observe((wiced_bool_t)msg.data,0,btm_advCallback);
			 break;
		 }
	 }
}

The last thing you need to do is start the timer.  The best place to start the timer is in the management callback where you need to

  1. Create the timer
  2. Tell it to start and run every 50ms
    switch (event)
    {
        case BTM_ENABLED_EVT:
            printf("Started BT Stack Succesfully\n");
            btm_cmdQueue = xQueueCreate(10,sizeof(btm_cmdMsg_t));
            wiced_init_timer_ext (&btm_mgmtQueueTimer, btm_processBluetoothAppQueue,0, WICED_TRUE);
            wiced_start_timer_ext (&btm_mgmtQueueTimer, 50);
        break;

A Potential Threading Bug

When I did the implementation originally I created what I thought was a threading bug.  Specifically I used the FreeRTOS timer to process the queue.  In other words instead of using a wiced_timer_ext_t I used a TimerHandle_t.  So what?

The wiced_timer_ext_t is run INSIDE of the BluetoothStack task where the TimerHandle_t is run inside of the Timer_SVC task.

So what?  I was afraid that the call to wiced_bt_ble_obsere was NOT thread safe and needed to be called inside of the same task as the stack.

After some digging I found out that the Bluetooth Stack is threadsafe, so I worried for no reason.  Well, actually, you can never worry enough about making these kinds of threading bugs because they are viscously difficult to debug.

Add a Scan Off & On Command

The last thing that you need to do is add an actual command to the usercmd task to call the bluetooth manager function to turn on and off scanning.

First, add a new prototype for your new command in usercmd.c.  Then add it to the list of legal commands.

static int usrcmd_scan(int argc, char **argv);


static const cmd_table_t cmdlist[] = {

.... deleted stuff

    { "scan","scan [on|off]", usrcmd_scan},

};

Then create the function to process the command line input and call the btm_scan function.

static int usrcmd_scan(int argc, char **argv)
{

    if(argc != 2)
        return 0;

    if(strcmp(argv[1],"on") == 0)
    {
        btm_cmdScan(true);
    }
    else if(strcmp(argv[1],"off") == 0)
    {
        btm_cmdScan(false);

    }
    return 0;

}

Now build it and run it.  You should still get adv packets barfing all over your screen.  But now you can turn on and off the scanning with “scan on” and “scan off”.  In the next article we will create a database to hold the scan packets.

Recommended Posts

No comment yet, add your voice below!


Add a Comment

Your email address will not be published.