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.

 

Recommended Posts

No comment yet, add your voice below!


Add a Comment

Your email address will not be published. Required fields are marked *