Summary

In this article I will build the basic project and then add the scanner code to look for Tilt Hydrometers that are broadcasting the iBeacon.  It will decode the data and print out Gravity and Temperature.

This series is broken up into the following 12 articles with a few additional possible articles. 

Tilt Hydrometer (Part 1) Overview & Out-of-Box

Tilt Hydrometer (Part 2) Architecture

Tilt Hydrometer (Part 3) Advertising Scanner

Tilt Hydrometer (Part 4) Advertising Packet Error?

Tilt Hydrometer (Part 5) Tilt Simulator & Multi Advertising iBeacons

Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade

Tilt Hydrometer (Part 7) Advertising Database

Tilt Hydrometer (Part 8) Read the Database

Tilt Hydrometer (Part 9) An LCD Display

Tilt Hydrometer (Part 10) The Display State Machine

Tilt Hydrometer (Part 11) Draw the Display Screens

Tilt Hydrometer (Part 12) CapSense

Tilt Hydrometer: LittleFS & SPI Flash (Part ?)

Tilt Hydrometer: WiFi Introducer (Part ?)

Tilt Hydrometer: WebServer (Part ?)

Tilt Hydrometer: Amazon MQTT (Part ?)

Tilt Hydrometer: Printed Circuit Board (Part ?)

You can get the source code from git@github.com:iotexpert/Tilt2.git  This repository has tags for each of the articles which can be accessed with "git checkout part12"  You can find the Tilt Simulator at  git@github.com:iotexpert/TiltSimulator.git.

 

Build the Basic Project

Start by making a new project using the CY8CKIT-062S2-43012.  I chose this kit because that was the one sitting on my desk at the time.  Any of the kits with the Arduino headers where I can plug in the TFT will work.

I typically start from the IoT Expert FreeRTOS NTShell Template.  Ill do that here as well.  Notice that I call the project Tilt2.  You can find this project on GitHub.  git@github.com:iotexpert/Tilt2.git .  I will also be putting in tags for each article so you can look at the code for each article.  To see the tags do “git tags”

After the project is made run “make modlibs” so that you can add some libraries.  Specifically the bluetooth-freertos and btstack libraries.

Now that I have all of the libraries I need, run “make vscode” to build the project files for vscode.

If you remember from the architecture diagram, I will have a task to manage the bluetooth world.  Inside of the btutil library I have a template to start with.  It also includes the stuff you need in main.c to start the stack.

You need to make two changes to the Makefile.  Well you dont have to change the name of the App … but I typically do.  You do need to add the FREERTOS and WICED_BLE components to your project.

APPNAME=Tilt2


....


COMPONENTS=FREERTOS WICED_BLE

Now, edit main.c to include some stuff.

#include "bluetoothManager.h"
#include "cycfg_bt_settings.h"
#include "bt_platform_cfg_settings.h"

And start the bluetooth stack.

    cybt_platform_config_init(&bt_platform_cfg_settings);
    wiced_bt_stack_init (app_bt_management_callback, &wiced_bt_cfg_settings);

You need to get the bluetooth configuration files.  To do that run the Bluetooth Configurator by running “make config_bt”.  Click the paper to make a new configuration.

Pick out the PSoC6 MCU with 43xxxx Connectivity.

Go to the GAP Settings tab and add the Observer configuration

Then set the low duty scan parameters to 60ms and turn off the timeout.

Save your configuration.  Notice that I call it tilt2.

Make sure that everything works with a build it and program.  You will have a working shell, blinking LED and the Bluetooth Task started.  Here is what the output looks like.

iBeacon Packet Format

On the Tilt Hydrometer FAQ they give you a link to Karl Urdevics blog where he describes the way that the Tilt works.  Pretty simple, each Tilt Hydrometer is hardcoded to a color.  Each “color” sends out an iBeacon with the Temperature, Gravity and Transmit Power.  iBeacon is just an Apple specified format for a BLE advertising packet.  You can read about the format of the BLE advertising packet here.  But, in summary, a BLE device can advertise up to 31 bytes of data.  That data is divided into fields of the following form:

There are a bunch of specific format types.  However, the one that we are looking for is the iBeacon format.  This format was defined by Apple and is implemented as data in the “«Manufacturer Specific Data»” which has the field code 0xFF.  The Manufacturer Specific Data field type is divided into

  • 16-bit Manufacturers ID
  • Data

For the iBeacon the Manufacturer ID is 0X004C – which is Apple’s ID.  They further subdivided the data.  Here is the complete format of that data.

Bytes Data Name Comment
1 0xFF Manufacturer Data Bluetooth GAP Assigned Number
1 0x1A Length of Field
2 0×04 0x0C Manufacturers' UUID Apples Bluetooth Manufacturer ID
1 02 Apple defined 0x02=Subtype iBeacon
1 0×15 Length Apple defined length
16 ???? UUID The universally unique identifier for the data type of the iBeacon (these are defined by Tilt)
2 ???? Major Value Gravity in 1/1000 of SG
2 ???? Minor Value Temperature in degrees F (yup, Imperial!)
1 ?? Signal Power dBm Transmit Power as measured at 1m

It turns out that Tilt “hardcodes” the UUID during manufacturing for the color of the Tilt (remember each Tilt is one of 8 colors)

Red:    A495BB10C5B14B44B5121370F02D74DE
Green:  A495BB20C5B14B44B5121370F02D74DE
Black:  A495BB30C5B14B44B5121370F02D74DE
Purple: A495BB40C5B14B44B5121370F02D74DE
Orange: A495BB50C5B14B44B5121370F02D74DE
Blue:   A495BB60C5B14B44B5121370F02D74DE
Yellow: A495BB70C5B14B44B5121370F02D74DE
Pink:   A495BB80C5B14B44B5121370F02D74DE

Add Advertising Observer

Now that we have a working project template, and we know what we are looking for in the BLE advertising land, I’ll setup some data structures to map of the colors and UUIDs.

#define TILT_IBEACON_HEADER_LEN 20
#define TILT_IBEACON_DATA_LEN 5
typedef struct  {
    char *colorName;
    uint8_t uuid[TILT_IBEACON_HEADER_LEN];
} tilt_t;

// Apple Bluetooth Company Code 0x004C
// iBeacon Subtype = 0x02
// Length = 0x15
#define IBEACON_HEADER 0x4C,0x00,0x02,0x15

static tilt_t tiltDB [] =
{
    {"Red",    {IBEACON_HEADER,0xA4,0x95,0xBB,0x10,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE}},
    {"Green" , {IBEACON_HEADER,0xA4,0x95,0xBB,0x20,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE}},
    {"Black" , {IBEACON_HEADER,0xA4,0x95,0xBB,0x30,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE}},
    {"Purple", {IBEACON_HEADER,0xA4,0x95,0xBB,0x40,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE}},
    {"Orange", {IBEACON_HEADER,0xA4,0x95,0xBB,0x50,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE}},
    {"Blue"  , {IBEACON_HEADER,0xA4,0x95,0xBB,0x60,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE}},
    {"Yellow", {IBEACON_HEADER,0xA4,0x95,0xBB,0x70,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE}},
    {"Pink"  , {IBEACON_HEADER,0xA4,0x95,0xBB,0x80,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE}},
};
#define NUM_TILT (sizeof(tiltDB)/sizeof(tilt_t))

Then I will create and advertising callback that will

  • Look for the manufacturer specific data field in the advertising packet
  • If the length of that field is 25 then we have found a possible iBeacon
  • Loop through the different possible Tilt’s
  • Compare the data in the advertising packet against the data + UUID in the packet
  • If it matches then decode the Gravity, txPower and Temperature and print it
static void btm_advScanResultCback(wiced_bt_ble_scan_results_t *p_scan_result, uint8_t *p_adv_data )
{
	if (p_scan_result == 0)
		return;

	uint8_t mfgFieldLen;
	uint8_t *mfgFieldData;
	mfgFieldData = wiced_bt_ble_check_advertising_data(p_adv_data,BTM_BLE_ADVERT_TYPE_MANUFACTURER,&mfgFieldLen);
    
	if(mfgFieldData && mfgFieldLen == TILT_IBEACON_HEADER_LEN + TILT_IBEACON_DATA_LEN)
    {
        for(int i=0;i<NUM_TILT;i++)
        {
            if(memcmp(mfgFieldData,tiltDB[i].uuid,TILT_IBEACON_HEADER_LEN)==0)
            {
                float gravity = ((float)((uint16_t)mfgFieldData[22] << 8 | (uint16_t)mfgFieldData[23]))/1000;
		        int temperature = mfgFieldData[20] << 8 | mfgFieldData[21];
                int8_t txPower = mfgFieldData[24];

                printf("Found Color=%s Gravity=%f Temperature = %d txPower=%d\n",tiltDB[i].colorName,gravity,temperature,txPower);
                break;
            }
        }
    }
}

In the Bluetooth Management callback I want to turn on scanning (actually observing).

        case BTM_ENABLED_EVT:
            if (WICED_BT_SUCCESS == p_event_data->enabled.status)
            {
				wiced_bt_ble_observe(WICED_TRUE, 0,btm_advScanResultCback);
            }
            else
            {
            	printf("Error enabling BTM_ENABLED_EVENT\n");
            }

To test this thing I brought a black tilt into my office.  As soon as the PSoC saw it, packets starting coming out on the screen about once per second.  The first thing to notice is that they broadcast some crazy data at the start.  That means I should be careful with the error checks (something which if you are a reader you know that I am not always perfect 🙂 .  It is also interesting to see that they broadcast 5 packets at 5dBm, then 5 at -59dBm.

Now that we have data coming out, in the next article Ill address a couple of funky things that I noticed.

Recommended Posts

2 Comments

  1. I am looking into lifting data from the Tilt in combination with also reading temperature data from XIAOMI thermometers (https://hackaday.com/2020/12/08/exploring-custom-firmware-on-xiaomi-thermometers/) into a local database on the Pi.
    Your project looks very interesting with lots of useful information but I found that you skipped a few important details in the beginning about what tools to use…
    Mentioning “CY8CKIT-062S2-43012” as the starting point is not enlightening for others with just a basic Raspberry Pi and a wish to monitor their brew 😉

    Anyways – good work and interesting project. I may be able to figure out what IDE and compiler to use by my own.

    • You are right.

      Use the Infineon Modus Toolbox and the PSoC6 + Wifi development platform.

      Sorry


Add a Comment

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