Summary
In part 6 of this series I will update the AnyCloud BLE Advertising Scanner to decode advertising packets into a more human readable textual output
Story
We are now 6 (or maybe 7 depending on how you count) articles into this series and we are still looking at raw bytes. I have gotten to where I am pretty good at understanding those bytes, but that is now way to roll. You might remember from the article on the IoT Expert Bluetooth Utility library that there were a some interesting functions defined in the header. Here it is:
wiced_bool_t btutil_isEddystone(uint8_t *data); wiced_bool_t btutil_is_iBeacon(uint8_t *data); wiced_bool_t btutil_isCypress(uint8_t *data); int btutil_adv_len(uint8_t *packet); void btutil_adv_printPacketDecode(uint8_t *packet); void btutil_adv_printPacketBytes(uint8_t *packet);
Lets transform our project from part 6 to use these functions. In this article I will
- Redo the print bytes (to be smarter) functionality and to use the built in function
- Rework the logic for the “print” command implementation
- Add a new command “decode” which will run the decode function
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
Replace two code blocks with function calls
Do you remember this block of code? It print’s out the 6-bytes of the Bluetooth Address.
for(int i=0;i<BD_ADDR_LEN;i++)
{
printf("%02X:",adb_database[entry].result->remote_bd_addr[i]);
}
You might have noticed that this function already exists in the BT Utility Library. Use it.
btutil_printBDaddress(adb_database[entry].result->remote_bd_addr);
Then you remember this block of code which iterates through and advertising packet and prints out the raw bytes?
// Print the RAW Data of the ADV Packet
printf(" Data: ");
int i=0;
while(adb_database[entry].data[i])
{
for(int j=0;j<adb_database[entry].data[i];j++)
{
printf("%02X ",adb_database[entry].data[i+1+j]);
}
i = i + adb_database[entry].data[i]+1;
}
Well, it exists in the library as well. Use it.
btutil_adv_printPacketBytes(adb_database[entry].data);
Fix the “print” Command
In the previous implementation I had two functions for “print”. The first one printed one entry and the second one printed the whole table. I decided that I didnt really like this logic, so I compressed those two functions into one function. Specifically, it take a number “entry”. If that number is -1 then it will print the whole table.
static void adb_db_printRawPacket(int entry)
{
int start,end;
if(entry <= -1)
{
start = 0;
end = adb_db_count;
}
else
{
start = entry;
end = entry;
}
if(end>adb_db_count)
end = adb_db_count;
for(int i=start;i<=end;i++)
{
printf("%02d MAC: ",i);
btutil_printBDaddress(adb_database[i].result->remote_bd_addr);
printf(" Data: ");
btutil_adv_printPacketBytes(adb_database[i].data);
printf("\n");
}
}
Add a new “decode” Command
The next thing to do is to add a function to print out decoded packets (or the whole table). So I wrote this:
static void adb_printDecodePacket(int entry)
{
int start,end;
if(entry == -1)
{
start = 0;
end = adb_db_count;
}
else
{
start = entry;
end = entry;
}
if(end>adb_db_count)
end = adb_db_count;
for(int i=start;i<=end;i++)
{
printf("%02d MAC: ",i);
btutil_printBDaddress(adb_database[i].result->remote_bd_addr);
printf("\n");
btutil_adv_printPacketDecode(adb_database[i].data);
printf("\n");
}
}
After finishing that block of code, I realized I had implemented almost exactly the same functionality which two different functions. So, I decided to redo this by doing this. Notice that it take in a adb_print_method, in other words raw bytes or decoded packet.
typedef enum {
ADB_PRINT_METHOD_BYTES,
ADB_PRINT_METHOD_DECODE,
} adb_print_method_t;
static void adb_db_print(adb_print_method_t method,int entry)
{
int start,end;
if(entry < 0)
{
start = 0;
end = adb_db_count;
}
else
{
start = entry;
end = entry;
}
if(end>adb_db_count)
end = adb_db_count;
for(int i=start;i<=end;i++)
{
printf("%02d MAC: ",i);
btutil_printBDaddress(adb_database[i].result->remote_bd_addr);
switch(method)
{
case ADB_PRINT_METHOD_BYTES:
printf(" Data: ");
btutil_adv_printPacketBytes(adb_database[i].data);
break;
case ADB_PRINT_METHOD_DECODE:
printf("\n");
btutil_adv_printPacketDecode(adb_database[i].data);
break;
}
printf("\n");
}
}
I don’t show it here, but after changing this I had to fix up the function calls in several places in the advDatabase.
Add a new command to print decode packets
Now that I have a new method to print packets, I add a command to the database to allow the user to call it:
typedef enum {
ADB_ADD,
ADB_PRINT_RAW,
ADB_PRINT_DECODE,
} adb_cmd_t;
Then in the queue loop:
switch(msg.cmd)
{
case ADB_ADD:
scan_result = (wiced_bt_ble_scan_results_t *)msg.data0;
data = (uint8_t *)msg.data1;
adb_db_add(scan_result,data);
break;
case ADB_PRINT_RAW:
adb_db_print(ADB_PRINT_METHOD_BYTES,(int)msg.data0);
break;
case ADB_PRINT_DECODE:
adb_db_print(ADB_PRINT_METHOD_DECODE,(int)msg.data0);
break;
}
Then the actual function
void adb_printDecode(int entry)
{
adb_cmdMsg_t msg;
msg.cmd = ADB_PRINT_DECODE;
msg.data0 = (void *)entry;
xQueueSend(adb_cmdQueue,&msg,0); // If the queue is full... oh well
}
Then add it to advDatabase.h
void adb_printDecode(int entry);
Finally to the usercmd.c
static int usrcmd_printDecode(int argc, char **argv)
{
if(argc == 1)
{
adb_printDecode(-1);
}
if(argc == 2)
{
int val;
sscanf(argv[1],"%d",&val);
adb_printDecode(val);
}
return 0;
}
Program and Test
When I actually program the scanner you can see that I can print out 1 item. OR I can decode one item. Notice that one contains 3 fields
- flags
- Tx Power Level
- Manufacturers data. Apparently an Apple something or the other
And I can print the whole table
Or decode the whole table.









No comment yet, add your voice below!