<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tilt Hydrometer Data Collection &#8211; IoT Expert</title>
	<atom:link href="https://iotexpert.com/category/tilt-hydrometer-data-collection/feed/" rel="self" type="application/rss+xml" />
	<link>https://iotexpert.com</link>
	<description>Engineering for the Internet of Things</description>
	<lastBuildDate>Tue, 11 May 2021 15:28:34 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://iotexpert.com/wp-content/uploads/2017/01/cropped-Avatar-32x32.jpg</url>
	<title>Tilt Hydrometer Data Collection &#8211; IoT Expert</title>
	<link>https://iotexpert.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Tilt Hydrometer (Part 12) CapSense</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-12-capsense/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-12-capsense/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 26 Apr 2021 13:14:48 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10507</guid>

					<description><![CDATA[Summary This article updates the user interface to have input as well as output by adding a CapSense GUI. Story If you look at the development kit you will notice on the right side that there are two CapSense buttons and one slider.  I know that this is going to come as a great shock [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>This article updates the user interface to have input as well as output by adding a CapSense GUI.</p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/part12-2/" rel="attachment wp-att-10518"><img fetchpriority="high" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/part12-1-1024x514.png" alt="" width="1024" height="514" class="alignnone size-large wp-image-10518" srcset="https://iotexpert.com/wp-content/uploads/2020/12/part12-1-1024x514.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/part12-1-300x151.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/part12-1-768x385.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/part12-1-1536x771.png 1536w, https://iotexpert.com/wp-content/uploads/2020/12/part12-1-600x301.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/part12-1.png 1614w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Story</h1>
<p>If you look at the development kit you will notice on the right side that there are two CapSense buttons and one slider.  I know that this is going to come as a great shock to those of you who know me, but Im not very patient.  I don&#8217;t always want to wait for the system to page through screens every 5 seconds.  So let&#8217;s turn on those buttons to do something useful.  But what?  How about the left button will toggle &#8220;auto mode&#8221; and the right button will go to the next screen.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/img_0311/" rel="attachment wp-att-10434"><img decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-1024x768.jpg" alt="" width="1024" height="768" class="alignnone size-large wp-image-10434" srcset="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-1024x768.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-300x225.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-768x576.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-1536x1152.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-2048x1536.jpg 2048w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-600x450.jpg 600w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Board Support Package</h1>
<p>We (Cypress/Infineon) created all of the setup stuff you need to make CapSense work on all of our development kits.  If you run &#8220;make config&#8221; you can look at the configuration of the Board Support Package for this development kit.  Notice that</p>
<ol>
<li>The CapSense is enabled</li>
<li>The pins are setup for two buttons and a slider.</li>
</ol>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/screen-shot-2020-12-12-at-10-35-54-am/" rel="attachment wp-att-10519"><img decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.35.54-AM-1024x794.jpg" alt="" width="1024" height="794" class="alignnone size-large wp-image-10519" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.35.54-AM-1024x794.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.35.54-AM-300x232.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.35.54-AM-768x595.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.35.54-AM-1536x1190.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.35.54-AM-2048x1587.jpg 2048w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.35.54-AM-600x465.jpg 600w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></p>
<p>When you run the CapSense Configurator you can see that there is a slider and two buttons</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/screen-shot-2020-12-12-at-10-36-27-am/" rel="attachment wp-att-10521"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.27-AM-1024x283.png" alt="" width="1024" height="283" class="alignnone size-large wp-image-10521" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.27-AM-1024x283.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.27-AM-300x83.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.27-AM-768x212.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.27-AM-1536x425.png 1536w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.27-AM-600x166.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.27-AM.png 1990w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>And they are attached to these pins:</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/screen-shot-2020-12-12-at-10-36-18-am/" rel="attachment wp-att-10520"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.18-AM-1024x728.png" alt="" width="1024" height="728" class="alignnone size-large wp-image-10520" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.18-AM-1024x728.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.18-AM-300x213.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.18-AM-768x546.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.18-AM-1536x1092.png 1536w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.18-AM-600x427.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-12-at-10.36.18-AM.png 1992w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>When you run the Library Manager you can also see that the CapSense middleware is already loaded into your project (notice MCU Middleware)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-11-29-at-9-22-21-am/" rel="attachment wp-att-10427"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.22.21-AM-750x1024.jpg" alt="" width="750" height="1024" class="alignnone size-large wp-image-10427" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.22.21-AM-750x1024.jpg 750w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.22.21-AM-220x300.jpg 220w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.22.21-AM-768x1049.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.22.21-AM-1124x1536.jpg 1124w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.22.21-AM-600x820.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.22.21-AM.jpg 1136w" sizes="auto, (max-width: 750px) 100vw, 750px" /></a></p>
<h1>The Firmware</h1>
<p>Let&#8217;s add the new capsenseManager.h, capsenseManager.c and update main.c.  The capsenseManager.h will just define the task:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#pragma once
void cpm_task();</pre>
<p>main.c needs to start the task.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">    xTaskCreate(cpm_task, "CapSense Manager", configMINIMAL_STACK_SIZE*2,0 /* args */ ,0 /* priority */, 0);
</pre>
<p>All of the action takes place in capsenseManager.c.  In the file there are really only two things that are even mildly complicated.  The CapSense block is a combination of a hardware block plus some firmware plus some middleware.  The hardware block does the CapSense to digital conversion then triggers an interrupt.  The interrupt firmware is responsible for managing the hardware block including sequencing the measurements, running the baseline etc.  When you trigger a scan, there is a back and forth between the hardware block and the firmware that must happen in an ISR.  Finally when things are done, you can ask for a callback.</p>
<p>The flow looks like this</p>
<ol>
<li>Initialize the hardware</li>
<li>Initialize the Interrupt Service Routine</li>
<li>Ask for a Callback</li>
<li>Enable the CapSense block</li>
<li>Start a scan</li>
<li>Wait for the callback</li>
<li>Process the results</li>
<li>Start a scan (repeat)</li>
</ol>
<h1>Initialize the CapSense (steps 1-4)</h1>
<p>To get this going you need to:</p>
<ol>
<li>Define the task</li>
<li>Initialize a semaphore (which you will use in the callback)</li>
<li>Initialize the hardware block</li>
<li>Initialize the interrupt</li>
<li>Register the callback</li>
<li>Enable the CapSense block</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void cpm_task()
{
    cpm_semaphore = xSemaphoreCreateCounting(10,0);

    static const cy_stc_sysint_t CapSense_ISR_cfg =
    {
        .intrSrc = csd_interrupt_IRQn, /* Interrupt source is the CSD interrupt */
        .intrPriority = 7u,            /* Interrupt priority is 7 */
    };

    Cy_CapSense_Init(&amp;cy_capsense_context);
        
    Cy_SysInt_Init(&amp;CapSense_ISR_cfg, &amp;cpm_isr);
    NVIC_ClearPendingIRQ(CapSense_ISR_cfg.intrSrc);
    NVIC_EnableIRQ(CapSense_ISR_cfg.intrSrc);

    Cy_CapSense_RegisterCallback	(CY_CAPSENSE_END_OF_SCAN_E,cpm_callback, &amp;cy_capsense_context); 
    Cy_CapSense_Enable (&amp;cy_capsense_context);</pre>
<h1>The ISR &amp; CallBack</h1>
<p>The ISR is trivial.  All it does is run our interrupt handler.</p>
<p>The callback just gives a semaphore which has held the task in suspension while the CapSense is running.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static void cpm_isr(void)
{
    Cy_CapSense_InterruptHandler(CYBSP_CSD_HW, &amp;cy_capsense_context);
}

static void cpm_callback(cy_stc_active_scan_sns_t *ptrActiveScan)
{
    xSemaphoreGiveFromISR(cpm_semaphore,portMAX_DELAY);
}</pre>
<h1>Main Task Loop</h1>
<p>In the main loop I</p>
<ol>
<li>Scan all of the widgets&#8230; then wait until the scan is done by holding on the semaphore.</li>
<li>After the scan is done I need to run all of the Cypress code which manages the data.</li>
<li>Then I find out the state of the two buttons.</li>
<li>Based on the state I call either the toggle auto mode or next screen command</li>
</ol>
<p>Notice that I wait for 20ms after I get this done.  What this does is give me a GUI update rate of about 50hz.  Probably 10hz would be fine.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">  int button0Prev = 0;
    int button1Prev = 0;
    int button0Curr = 0;
    int button1Curr = 0;
    while(1)
    {
        Cy_CapSense_ScanAllWidgets (&amp;cy_capsense_context);
        xSemaphoreTake(cpm_semaphore,portMAX_DELAY);
        Cy_CapSense_ProcessAllWidgets(&amp;cy_capsense_context);
        button0Curr = Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON0_WDGT_ID,&amp;cy_capsense_context);
        button1Curr = Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON1_WDGT_ID,&amp;cy_capsense_context);

        if(button0Curr != button0Prev &amp;&amp; button0Curr == 1)
        {
            dm_submitAutoCmd();
        }
        if(button1Curr != button1Prev &amp;&amp; button1Curr == 1)
        {
            dm_submitNextScreenCmd();
        }

        button0Prev = button0Curr;
        button1Prev = button1Curr;

        vTaskDelay(20); // 50 Hz Update Rate

    }</pre>
<h1>capSenseManager.c</h1>
<p>Here is the whole file.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#include &lt;stdio.h&gt;

#include "cycfg_capsense.h"

#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

#include "displayManager.h"

static SemaphoreHandle_t cpm_semaphore;

static void cpm_isr(void)
{
    Cy_CapSense_InterruptHandler(CYBSP_CSD_HW, &amp;cy_capsense_context);
}

static void cpm_callback(cy_stc_active_scan_sns_t *ptrActiveScan)
{
    xSemaphoreGiveFromISR(cpm_semaphore,portMAX_DELAY);
}

void cpm_task()
{
    cpm_semaphore = xSemaphoreCreateCounting(10,0);

    static const cy_stc_sysint_t CapSense_ISR_cfg =
    {
        .intrSrc = csd_interrupt_IRQn, /* Interrupt source is the CSD interrupt */
        .intrPriority = 7u,            /* Interrupt priority is 7 */
    };

    Cy_CapSense_Init(&amp;cy_capsense_context);
        
    Cy_SysInt_Init(&amp;CapSense_ISR_cfg, &amp;cpm_isr);
    NVIC_ClearPendingIRQ(CapSense_ISR_cfg.intrSrc);
    NVIC_EnableIRQ(CapSense_ISR_cfg.intrSrc);

    Cy_CapSense_RegisterCallback	(CY_CAPSENSE_END_OF_SCAN_E,cpm_callback, &amp;cy_capsense_context); 
    Cy_CapSense_Enable (&amp;cy_capsense_context);

    int button0Prev = 0;
    int button1Prev = 0;
    int button0Curr = 0;
    int button1Curr = 0;
    while(1)
    {
        Cy_CapSense_ScanAllWidgets (&amp;cy_capsense_context);
        xSemaphoreTake(cpm_semaphore,portMAX_DELAY);
        Cy_CapSense_ProcessAllWidgets(&amp;cy_capsense_context);
        button0Curr = Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON0_WDGT_ID,&amp;cy_capsense_context);
        button1Curr = Cy_CapSense_IsWidgetActive(CY_CAPSENSE_BUTTON1_WDGT_ID,&amp;cy_capsense_context);

        if(button0Curr != button0Prev &amp;&amp; button0Curr == 1)
        {
            dm_submitAutoCmd();
        }
        if(button1Curr != button1Prev &amp;&amp; button1Curr == 1)
        {
            dm_submitNextScreenCmd();
        }

        button0Prev = button0Curr;
        button1Prev = button1Curr;

        vTaskDelay(20); // 50 Hz Update Rate

    }
}</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-12-capsense/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 11) Draw the Display Screens</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 19 Apr 2021 12:39:26 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10491</guid>

					<description><![CDATA[Summary This article updates the display system in my Tilt Hydrometer project to actually display useful information. This article will continue to edit the display manager task Story In the last article I built all of the infrastructure to make the state machine work.  Now it is time to draw some actual graphics and get [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>This article updates the display system in my Tilt Hydrometer project to actually display useful information.</p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
<p>This article will continue to edit the display manager task</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/part9/" rel="attachment wp-att-10514"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/part9-1024x514.png" alt="" width="1024" height="514" class="alignnone size-large wp-image-10514" srcset="https://iotexpert.com/wp-content/uploads/2020/12/part9-1024x514.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/part9-300x151.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/part9-768x385.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/part9-1536x771.png 1536w, https://iotexpert.com/wp-content/uploads/2020/12/part9-600x301.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/part9.png 1614w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Story</h1>
<p>In the last article I built all of the infrastructure to make the state machine work.  Now it is time to draw some actual graphics and get this puppy displaying some data.  Remember that I want three screens</p>
<ol>
<li>The Splash Screen with the IoT Expert Logo</li>
</ol>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/img_0327/" rel="attachment wp-att-10502"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0327-300x269.jpg" alt="" width="300" height="269" class="alignnone wp-image-10502 size-medium" srcset="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0327-300x269.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0327-1024x919.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0327-768x690.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0327-600x539.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0327.jpg 1390w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p>
<p>2. The Table Screen with a table of Gravity and Temperature for the 8 Tilts</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/img_0313/" rel="attachment wp-att-10498"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0313-scaled-e1607424769624-300x237.jpg" alt="" width="300" height="237" class="alignnone wp-image-10498 size-medium" srcset="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0313-scaled-e1607424769624-300x237.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0313-scaled-e1607424769624-1024x808.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0313-scaled-e1607424769624-768x606.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0313-scaled-e1607424769624-1536x1212.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0313-scaled-e1607424769624-2048x1616.jpg 2048w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0313-scaled-e1607424769624-600x473.jpg 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p>
<p>3. A detail screen.  One per Tilt with more information drawn in the same color as the Tilt.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/img_0315/" rel="attachment wp-att-10496"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0315-scaled-e1607424833134-300x233.jpg" alt="" width="300" height="233" class="alignnone wp-image-10496 size-medium" srcset="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0315-scaled-e1607424833134-300x233.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0315-scaled-e1607424833134-1024x794.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0315-scaled-e1607424833134-768x596.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0315-scaled-e1607424833134-1536x1192.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0315-scaled-e1607424833134-600x465.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0315-scaled-e1607424833134.jpg 1971w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p>
<p>From the last post you might remember that you need to write 4 functions to implement a new screen</p>
<ol>
<li>precheck &#8211; returns true if it is legal to come to that screen</li>
<li>init &#8211; draw the initial data (like the table outline)</li>
<li>update &#8211; to update the data on the screen</li>
<li>sequence &#8211; up move to the next subscreen e.g. purple &#8211;&gt; red</li>
</ol>
<h1>Splash Screen</h1>
<p>First the splash screen.  The pre-check function returns true because you are always allowed to &#8220;go&#8221; to this screen.  The initialization function draws all of the screen (as it is static).  Basically sets the background to white, clears the screen (which actually turns it white) then draws the bitmap.  There is nothing to do in the update or the sequence functions.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">////////////////////////////////////////////////////////////////////////////////
//
// Splash
// 
////////////////////////////////////////////////////////////////////////////////
static bool dm_displayScreenSplashPre()
{
    return true;
}

static void dm_displayScreenSplashInit()
{
    GUI_SetBkColor(GUI_WHITE);
    GUI_Clear();
    GUI_DrawBitmap(&amp;bmIOTexpert_Logo_Vertical320x240,0,17);

}
static void dm_displayScreenSplashUpdate()
{
}

static bool dm_displayScreenSplashSeq()
{
    return true;
}
</pre>
<h1>Table Screen</h1>
<p>The table screen precheck and sequence functions say &#8220;true&#8221;, meaning it is always OK to come to this screen and it is always OK to leave this screen.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static bool dm_displayScreenTablePre()
{
    return true;
}

static bool dm_displayScreenTableSeq()
{
    return true;
}

</pre>
<p>The initialization function draws the header and the two lines.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/img_0325/" rel="attachment wp-att-10501"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0325-300x258.jpg" alt="" width="300" height="258" class="alignnone wp-image-10501 size-medium" srcset="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0325-300x258.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0325-1024x880.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0325-768x660.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0325-600x516.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0325.jpg 1134w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a></p>
<p>First of all it sets the location of the graphics with #defines.  The actual function goes on to use that information and draw the picture.  Self explanatory.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#define TABLE_FONT (GUI_FONT_24B_ASCII)
#define TABLE_BGCOLOR (GUI_BLACK)
#define TABLE_HEAD_BGCOLOR (GUI_WHITE)

#define TABLE_NAME_LEFT_X (0)
#define TABLE_GRAV_LEFT_X (120)
#define TABLE_TEMP_LEFT_X (220)

#define TABLE_NAME_RIGHT_X (119)
#define TABLE_GRAV_RIGHT_X (219)
#define TABLE_TEMP_RIGHT_X (319)

#define TABLE_NAME_CENTER_X (TABLE_NAME_LEFT_X+(TABLE_NAME_RIGHT_X-TABLE_NAME_LEFT_X)/2)
#define TABLE_GRAV_CENTER_X (TABLE_GRAV_LEFT_X+(TABLE_GRAV_RIGHT_X-TABLE_GRAV_LEFT_X)/2)
#define TABLE_TEMP_CENTER_X (TABLE_TEMP_LEFT_X+(TABLE_TEMP_RIGHT_X-TABLE_TEMP_LEFT_X)/2)

static void dm_displayScreenTableInit()
{
    GUI_SetColor(TABLE_HEAD_BGCOLOR);
    GUI_SetBkColor(TABLE_BGCOLOR);
    GUI_SetFont(TABLE_FONT);
    GUI_Clear();

    GUI_FillRect(0,0,320,GUI_GetFontSizeY()+ TOP_MARGIN);

    GUI_SetTextMode(GUI_TM_REV);
    GUI_SetTextAlign(GUI_TA_CENTER);
    GUI_DispStringHCenterAt("Name",TABLE_NAME_CENTER_X,TOP_MARGIN);
    GUI_DispStringHCenterAt("Gravity",TABLE_GRAV_CENTER_X,TOP_MARGIN);
    GUI_DispStringHCenterAt("Temp",TABLE_TEMP_CENTER_X,TOP_MARGIN);

    GUI_DrawLine(TABLE_NAME_RIGHT_X,0,TABLE_NAME_RIGHT_X,240);
    GUI_DrawLine(TABLE_GRAV_RIGHT_X,0,TABLE_GRAV_RIGHT_X,240);
}</pre>
<p>The update will:</p>
<ol>
<li>set the graphics configuration</li>
<li>iterate through the list of Tilts</li>
<li>If it is active then it will get the data</li>
<li>sprintf it into a buffer than display it.</li>
<li>If it is not active then display a &#8220;&#8211;&#8220;</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static void dm_displayScreenTableUpdate()
{

    uint32_t activeTilts =tdm_getActiveTiltMask();

    char buff[64];
    
    GUI_SetColor(TABLE_HEAD_BGCOLOR);
    GUI_SetBkColor(TABLE_BGCOLOR);
    
    GUI_SetFont(TABLE_FONT);
    GUI_SetTextMode(GUI_TEXTMODE_NORMAL);
    GUI_SetTextAlign(GUI_TA_CENTER);

    int row;
    for(int i=0;i&lt;tdm_getNumTilt();i++)
    {
        row = i+1;

        GUI_SetColor(tdm_colorGUI(i));
        GUI_DispStringHCenterAt(tdm_getColorString(i), TABLE_NAME_CENTER_X, ROW_Y(row));

        if(1&lt;&lt;i &amp; activeTilts)
        {

            tdm_tiltData_t *response = tdm_getTiltData(i);

            sprintf(buff,"%1.3f",response-&gt;gravity);
            GUI_DispStringHCenterAt(buff, TABLE_GRAV_CENTER_X, ROW_Y(row));
            sprintf(buff,"%02d",response-&gt;temperature);
            GUI_DispStringHCenterAt(buff, TABLE_TEMP_CENTER_X, ROW_Y(row));
                        
            free(response);
        }
        else
        {
            GUI_DispStringHCenterAt("-----", TABLE_GRAV_CENTER_X, ROW_Y(row));
            GUI_DispStringHCenterAt("--", TABLE_TEMP_CENTER_X, ROW_Y(row));

        }
    }  
}</pre>
<p>The only little bit of trickiness is that I defined a macro to calculate the y position on the screen.  Specifically I divided the screen into &#8220;rows&#8221; starting at 0 based on the height of the font plus the margin between the rows plus whatever the offset at the top of the screen (the top margin)</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#define TOP_MARGIN (4)
#define LINE_MARGIN (2)
#define ROW_Y(row) (TOP_MARGIN + (row)*(LINE_MARGIN+GUI_GetFontSizeY()))</pre>
<h1>Single Screen</h1>
<p>The single screen is a bit more interesting.  First of all you are only allowed to go to this screen if there is at least one Tilt that is active.  The next thing is that I want the first time you go to this screen to go to the first active tilt.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static tdm_tiltHandle_t currentSingle = 0xFF;

static bool dm_displaySinglePre()
{
    uint32_t activeTilts =tdm_getActiveTiltMask();
    if(activeTilts == 0)
        return false;

    for(int i=0;i&lt;tdm_getNumTilt();i++)
    {
        if(1&lt;&lt;i &amp; activeTilts)
        {
            currentSingle = i;
            break;
        }
    }

    return true;
}</pre>
<p>The initialization function</p>
<ol>
<li>Clears the screen</li>
<li>Asks for the color of the current Tilt (so that all of the text is drawn using that color)</li>
<li>Draws the header</li>
<li>Draws the labels</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static void dm_displaySingleInit()
{
    GUI_SetBkColor(GUI_BLACK);
    GUI_SetFont(GUI_FONT_32B_ASCII);
    GUI_Clear();

    GUI_SetColor(tdm_colorGUI(currentSingle));

    GUI_FillRect(0,0,320,ROW_Y(1));
    
    GUI_SetTextAlign(GUI_TA_LEFT);
    GUI_SetTextMode(GUI_TM_REV);
    GUI_DispStringHCenterAt(tdm_getColorString(currentSingle), CENTER_X,ROW_Y(0) );

    GUI_SetTextMode(GUI_TM_NORMAL);
    GUI_SetTextAlign(GUI_TA_RIGHT | GUI_TA_VCENTER);

    GUI_DispStringAt("Gravity: ", SINGLE_LABEL_X,ROW_Y(SINGLE_GRAV_ROW) );
    GUI_SetTextAlign(GUI_TA_RIGHT | GUI_TA_VCENTER);
    GUI_DispStringAt("Temp: ",    SINGLE_LABEL_X,ROW_Y(SINGLE_TEMP_ROW) );
    GUI_SetTextAlign(GUI_TA_RIGHT | GUI_TA_VCENTER);
    GUI_DispStringAt("TxPower: ", SINGLE_LABEL_X,ROW_Y(SINGLE_TXPOWER_ROW) );
    GUI_SetTextAlign(GUI_TA_RIGHT | GUI_TA_VCENTER);
    GUI_DispStringAt("RSSI: ",    SINGLE_LABEL_X,ROW_Y(SINGLE_RSSI_ROW) );
    GUI_SetTextAlign(GUI_TA_RIGHT | GUI_TA_VCENTER);
    GUI_DispStringAt("Time: ",    SINGLE_LABEL_X,ROW_Y(SINGLE_TIME_ROW) );

}</pre>
<p>The update function basically sets the font and color (just to make sure) then it gets the data, formats it into a string and displays it.  When I wrote this function originally I wasn&#8217;t sure if this would ever be called in a  situation where there is no data, so I handled that with the &#8220;&#8212;-&#8220;, but I don&#8217;t think that branch is actually ever used.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static void dm_displaySingleUpdate()
{

    uint32_t activeTilts =tdm_getActiveTiltMask();

    char gravString[10];
    char tempString[10];
    char txPowerString[10];
    char rssiString[10];
    char timeString[64];
       
    GUI_SetBkColor(GUI_BLACK);
    GUI_SetFont(GUI_FONT_32B_ASCII);
    GUI_SetColor(tdm_colorGUI(currentSingle));
    
    if(1&lt;&lt;currentSingle &amp; activeTilts)
    {
        tdm_tiltData_t *response = tdm_getTiltData(currentSingle);
        sprintf(gravString,"%1.3f",response-&gt;gravity);
        sprintf(tempString,"%02d",response-&gt;temperature);
        sprintf(txPowerString,"%d",response-&gt;txPower);
        sprintf(rssiString,"%2d",response-&gt;rssi);

        int seconds = (xTaskGetTickCount()/1000- response-&gt;time);
        int days = seconds/(24*60*60);
        seconds = seconds - days*(24*60*60);
        int hours = seconds/(60*60);
        seconds = seconds - hours*(60*60);
        int minutes = seconds/60;
        seconds = seconds - (minutes * 60);
        
        sprintf(timeString,"%02d:%02d:%02d:%02d",days,hours,minutes,seconds);                       
        free(response);
    }

    else
    {
        sprintf(gravString,"-----");
        sprintf(tempString,"--");
        sprintf(txPowerString,"---");
        sprintf(rssiString,"--");
        sprintf(timeString,"---");                       
    }

    GUI_DispStringAtCEOL(gravString, SINGLE_VALUE_X,ROW_Y(SINGLE_GRAV_ROW) );
    GUI_SetTextAlign(GUI_TA_LEFT | GUI_TA_VCENTER);
    GUI_DispStringAtCEOL(tempString,    SINGLE_VALUE_X,ROW_Y(SINGLE_TEMP_ROW) );
    GUI_SetTextAlign(GUI_TA_LEFT | GUI_TA_VCENTER);
    GUI_DispStringAtCEOL(txPowerString, SINGLE_VALUE_X,ROW_Y(SINGLE_TXPOWER_ROW) );
    GUI_SetTextAlign(GUI_TA_LEFT | GUI_TA_VCENTER);
    GUI_DispStringAtCEOL(rssiString,    SINGLE_VALUE_X,ROW_Y(SINGLE_RSSI_ROW) );
    GUI_SetTextAlign(GUI_TA_LEFT | GUI_TA_VCENTER);
    GUI_DispStringAtCEOL(timeString,    SINGLE_VALUE_X,ROW_Y(SINGLE_TIME_ROW) );
}</pre>
<p>The last bit of code is the sequence which will either</p>
<ol>
<li>Go to the next screen by returning True (if there are no active Tilts)</li>
<li>Move to the next Tilt (if there are more active)</li>
<li>Go to the next screen by returning True</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static bool dm_displaySingleSeq()
{
    uint32_t activeTilts =tdm_getActiveTiltMask();
    if(activeTilts == 0)
        return true;

    for(int i=currentSingle+1;i&lt;tdm_getNumTilt();i++)
    {
        if(1&lt;&lt;i &amp; activeTilts)
        {
            currentSingle = i;
            dm_displaySingleInit();
            return false;
        }
    }
    return true;
}
</pre>
<p>That&#8217;s it for the display.  In the next article Ill add the User Input of the GUI by adding CapSense.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 10) The Display State Machine</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 12 Apr 2021 13:24:03 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10469</guid>

					<description><![CDATA[Summary This article updates the display system in my Tilt Hydrometer project to include the state machine apparatus to move between screens. We will continue to edit the Display Manager in this article: Story Things are a little unfair as I already know the end of this story because I wrote this code a few [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>This article updates the display system in my Tilt Hydrometer project to include the state machine apparatus to move between screens.</p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
<p>We will continue to edit the Display Manager in this article:</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/part9/" rel="attachment wp-att-10514"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/part9-1024x514.png" alt="" width="1024" height="514" class="alignnone size-large wp-image-10514" srcset="https://iotexpert.com/wp-content/uploads/2020/12/part9-1024x514.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/part9-300x151.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/part9-768x385.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/part9-1536x771.png 1536w, https://iotexpert.com/wp-content/uploads/2020/12/part9-600x301.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/part9.png 1614w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Story</h1>
<p>Things are a little unfair as I already know the end of this story because I wrote this code a few months ago.  Recently, as I came back to write these articles I looked at the code, actually Monday.  The code was pretty complicated and I have been really busy so I set it aside as I wasn&#8217;t sure how to explain it.  Then I looked again on Tuesday and contemplated re-writing it&#8230; then again on Wednesday then &#8230; and finally Saturday when I decided that what I had done originally was correct.  That means I just need to explain it.</p>
<p>My system is going to look work like this:</p>
<ol>
<li>A splash screen with the IoT Expert Logo</li>
<li>A table of data screen with one row per tilt</li>
<li>A series of details screens, one per tilt</li>
<li>The ability to &#8220;skip&#8221; detail screens if there is no data</li>
<li>An automated UI that moved through the screens every 5000ms</li>
<li>Support for a manual UI so that the ntshell and the CapSense interface could send it commands to move to specific screens</li>
</ol>
<p>Here is the picture:</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/display-state-machine/" rel="attachment wp-att-10437"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/display-state-machine-1024x657.png" alt="" width="1024" height="657" class="alignnone size-large wp-image-10437" srcset="https://iotexpert.com/wp-content/uploads/2020/12/display-state-machine-1024x657.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/display-state-machine-300x193.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/display-state-machine-768x493.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/display-state-machine-600x385.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/display-state-machine.png 1050w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Add Color to the Tilt Data Manager</h1>
<p>Before I jump into the GUI, I need to add some color information to the database of Tilts.  This is a little bit of a smearing the line between the database and the display systems, but I felt that having all of the information about the Tilts in one place was better than having a split.  In order for the display manager to get the color information I add a new function to the tiltDataManager.h to return the specific GUI_COLOR (which is an emWin thing) for that specific Tilt handle.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">GUI_COLOR tdm_colorGUI(tdm_tiltHandle_t handle)
</pre>
<p>Then I need to update the tiltDataManager. c to have the color in the database.  Notice that the emWin library doesn&#8217;t have purpose or pink so I create those colors.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef struct  {
    char *colorName;
    GUI_COLOR color;
    uint8_t uuid[20];
    tdm_tiltData_t *data;
    int numDataPoints;
    int numDataSeen;
} tilt_t;


#define IBEACON_HEADER 0x4C,0x00,0x02,0x15
#define GUI_PINK GUI_MAKE_COLOR(0x00CCCCFF)
#define GUI_PURPLE GUI_MAKE_COLOR(0x00800080)

static tilt_t tiltDB [] =
{
    {"Red",    GUI_RED,    {IBEACON_HEADER,0xA4,0x95,0xBB,0x10,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Green" , GUI_GREEN,  {IBEACON_HEADER,0xA4,0x95,0xBB,0x20,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Black" , GUI_GRAY,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x30,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Purple", GUI_PURPLE, {IBEACON_HEADER,0xA4,0x95,0xBB,0x40,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Orange", GUI_ORANGE, {IBEACON_HEADER,0xA4,0x95,0xBB,0x50,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Blue"  , GUI_BLUE,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x60,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Yellow", GUI_YELLOW, {IBEACON_HEADER,0xA4,0x95,0xBB,0x70,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Pink"  , GUI_PINK,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x80,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
};
#define NUM_TILT (sizeof(tiltDB)/sizeof(tilt_t))</pre>
<p>Then I add the actual function.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">GUI_COLOR tdm_colorGUI(tdm_tiltHandle_t handle)
{
    return tiltDB[handle].color;
}</pre>
<h1>Display Manager State Machine</h1>
<p>The picture above looks like a state machine, which I suppose makes sense as it is a state machine.  The parts of the state machine are</p>
<ol>
<li>Initialize the screen</li>
<li>Update the data on the screen</li>
<li>Go to the next screen</li>
<li>Sequence the &#8220;subscreen&#8221; i.e. from the purple screen to the red screen</li>
<li>Precheck (can you enter the screen)</li>
</ol>
<p>To support this I created a structure of function pointers:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef struct {
    bool (*precheck)(void);   // return true if you can come to this screen
    void (*init)(void);       // draw the initial stuff
    void (*update)(void);     // update the data
    bool (*sequence)(void);   // sequence the data .. return true if you should go to the next screen
    dm_screenName_t next;
} dm_screenMgmt_t;</pre>
<p>This means that each &#8220;screen&#8221; needs functions in a table that have:</p>
<ol>
<li>precheck &#8211; returns true if it is legal to come to that screen</li>
<li>init &#8211; draw the initial data (like the table outline)</li>
<li>update &#8211; to update the data on the screen</li>
<li>sequence &#8211; up move to the next subscreen e.g. purple &#8211;&gt; red</li>
</ol>
<p>But what is the &#8220;next&#8221; member in the structure?  This is just an index into the state machine table that tells what is the next entry to go too.  I wish that there was a better way to do this in C as the enumerated value is critically mapped to the place in the array of screens (see below)</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef enum {
    SPLASH,
    TABLE,
    SINGLE,
} dm_screenName_t;
</pre>
<p>With all of this setup I can now make a table to represent the states of my screens like this (more on the functions a bit later in this article).</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">dm_screenMgmt_t screenList[] = {
    {dm_displayScreenSplashPre, dm_displayScreenSplashInit, dm_displayScreenSplashUpdate, dm_displayScreenSplashSeq, TABLE},
    {dm_displayScreenTablePre , dm_displayScreenTableInit, dm_displayScreenTableUpdate, dm_displayScreenTableSeq, SINGLE},
    {dm_displaySinglePre, dm_displaySingleInit, dm_displaySingleUpdate, dm_displaySingleSeq, TABLE},
};
</pre>
<p>With that in place I can now create the code that actually runs the state machine, dm_nextScreen.  This function</p>
<ol>
<li>Runs the &#8220;sequence&#8221; function that will either move to the next subscreen OR it will return true (meaning go to the next screen)</li>
<li>If the precheck returns true then it is legal to jump to this screen</li>
<li>Jump to the next screen</li>
<li>Finally update the data on the current screen</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static void dm_nextScreen()
{
    if((*screenList[dm_currentScreen].sequence)())
    {
        if((*screenList[screenList[dm_currentScreen].next].precheck)())
        {
            dm_currentScreen = screenList[dm_currentScreen].next;
            (*screenList[dm_currentScreen].init)();
        }
    }
    (*screenList[dm_currentScreen].update)();
}
</pre>
<h1>The Display Manager Task</h1>
<p>The display manager task is now a bit tricky as well.  First of all I have a boolean variable called &#8220;autoRotate&#8221;.  When this variable is true it means that the screens should automatically switch to the next screen every 5 seconds.</p>
<p>The next part of the code initializes a command queue (so that other tasks can send a next screen or enable/disable of autorotate or a jump straight to the table.</p>
<p>The dm_currentScreen is global to this file and keep track of which screen you are currently on, which is what state the state machine is in.</p>
<p>The next part of the code waits for a message OR a timeout.  The timeout happens after 5000ms (5s) and tell the system to either go to the next screen, or update the data on the screen.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void dm_task(void *arg)
{
    dm_cmdMsg_t msg;
    bool autoRotate=true;
    dm_cmdQueue = xQueueCreate(10,sizeof(dm_cmdMsg_t));

    /* Initialize the display controller */
    mtb_st7789v_init8(&amp;tft_pins);
    GUI_Init();

    dm_currentScreen = SPLASH;

    dm_displayScreenSplashInit();
    dm_displayScreenSplashUpdate();

    for(;;)
    {
        if(xQueueReceive(dm_cmdQueue,&amp;msg,5000) == pdPASS) // Got a command
        {
            switch(msg.cmd)
            {
                case SCREEN_NEXT:
                dm_nextScreen();
                break;
                case SCREEN_AUTO:
                autoRotate = ! autoRotate;
                printf("AutoRotate =%s\n",autoRotate?"True":"False");
                break;
                case SCREEN_TABLE:
                dm_currentScreen = TABLE;
                (*screenList[dm_currentScreen].init)();
                (*screenList[dm_currentScreen].update)();
                break;

            }

        }
        else // otherwise just update the screen
        {
            if(autoRotate)
                dm_nextScreen();
            else
                (*screenList[dm_currentScreen].update)();
        }
    }
}</pre>
<p>With the infrastructure in place, in the next article I actually draw the screens.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 9) An LCD Display</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 22 Mar 2021 14:07:13 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10424</guid>

					<description><![CDATA[Summary This article updates the Tilt Hydrometer project to include an LCD display using the Segger emWin library.  This includes a discussion of a &#8220;new&#8221; library in the suite of IoT Expert awesome sauce.  Specifically the IoT Expert Graphics library. Here is the architecture with the blocks we are editing marked green. Story I knew [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>This article updates the Tilt Hydrometer project to include an LCD display using the Segger emWin library.  This includes a discussion of a &#8220;new&#8221; library in the suite of IoT Expert awesome sauce.  Specifically the IoT Expert Graphics library.</p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
<p>Here is the architecture with the blocks we are editing marked green.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/part9/" rel="attachment wp-att-10514"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/part9-1024x514.png" alt="" width="1024" height="514" class="alignnone size-large wp-image-10514" srcset="https://iotexpert.com/wp-content/uploads/2020/12/part9-1024x514.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/part9-300x151.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/part9-768x385.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/part9-1536x771.png 1536w, https://iotexpert.com/wp-content/uploads/2020/12/part9-600x301.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/part9.png 1614w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Story</h1>
<p>I knew that I wanted to use the TFT Display so that I could have this Tilt Hydrometer system sitting in my &#8220;brewery&#8221;.  Notice in the picture below you can see that the screen is currently displaying the data from the &#8220;black&#8221; tilt.  Also notice that you can see two fermenters in the background in the brewery.  Who needs a guest bathroom in the era of Covid <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/img_0321/" rel="attachment wp-att-10473"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0321-675x1024.jpg" alt="" width="675" height="1024" class="alignnone size-large wp-image-10473" srcset="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0321-675x1024.jpg 675w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0321-198x300.jpg 198w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0321-768x1164.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0321-1013x1536.jpg 1013w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0321-1351x2048.jpg 1351w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0321-600x910.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0321-scaled.jpg 1689w" sizes="auto, (max-width: 675px) 100vw, 675px" /></a></p>
<p>Also notice that there is a Raspberry Pi (more on that in a future article).  And for all of you married people out there, yes I have a very frustrated but forgiving wife.</p>
<h1>Add Display Libraries &amp; Fix the Makefile</h1>
<p>The first step in building this is to add the new libraries.  Specifically the display-tft-st7789v library which knows how to talk to the lcd and the emWin library.  AND the new IoT Expert Graphics Library.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-02-at-5-56-14-am/" rel="attachment wp-att-10432"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM-1019x1024.png" alt="" width="1019" height="1024" class="alignnone size-large wp-image-10432" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM-1019x1024.png 1019w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM-298x300.png 298w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM-150x150.png 150w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM-768x772.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM-300x300.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM-600x603.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM-100x100.png 100w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-02-at-5.56.14-AM.png 1116w" sizes="auto, (max-width: 1019px) 100vw, 1019px" /></a></p>
<p>In order to use the emWin library you need to fix the Makefile to include the emWin component.  Notice that I chose OS  meaning an RTOS and NTS meaning no touch screen.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">COMPONENTS=FREERTOS WICED_BLE EMWIN_OSNTS</pre>
<h1>IoT Expert Graphics Library</h1>
<p>I wanted to have a splash screen on this project with my Logo.  So I started digging around on my computer to find the graphics, which I found in the file <span>IOTexpert_Logo_Vertical.png.  Unfortunately this is a 1091&#215;739 portable network graphics file, not exactly what emWin wants to draw with.  To fix this I opened it in GIMP</span></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-06-at-7-32-57-am/" rel="attachment wp-att-10475"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.32.57-AM-1024x558.jpg" alt="" width="1024" height="558" class="alignnone size-large wp-image-10475" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.32.57-AM-1024x558.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.32.57-AM-300x163.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.32.57-AM-768x418.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.32.57-AM-1536x836.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.32.57-AM-600x327.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.32.57-AM.jpg 2046w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Then I resized it to my screen size (320&#215;240)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-06-at-7-34-40-am/" rel="attachment wp-att-10476"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.40-AM.png" alt="" width="768" height="596" class="alignnone size-full wp-image-10476" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.40-AM.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.40-AM-300x233.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.40-AM-600x466.png 600w" sizes="auto, (max-width: 768px) 100vw, 768px" /></a></p>
<p>Now it looks like this:</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-06-at-7-34-51-am/" rel="attachment wp-att-10477"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.51-AM-1024x561.png" alt="" width="1024" height="561" class="alignnone size-large wp-image-10477" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.51-AM-1024x561.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.51-AM-300x164.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.51-AM-768x420.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.51-AM-1536x841.png 1536w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.51-AM-600x328.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.34.51-AM.png 2046w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Then I saved it as a PNG.  Still doesn&#8217;t work with emWin.  But, if you look in the emWin library you will notice that there is a new directory called &#8220;Tool&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-06-at-7-36-40-am/" rel="attachment wp-att-10478"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.36.40-AM-472x1024.png" alt="" width="472" height="1024" class="alignnone size-large wp-image-10478" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.36.40-AM-472x1024.png 472w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.36.40-AM-138x300.png 138w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.36.40-AM.png 572w" sizes="auto, (max-width: 472px) 100vw, 472px" /></a></p>
<p>This directory contains the Segger tools for supporting their graphics library.  The one that I want is called &#8220;Bin2C.exe&#8221; (unfortunately it is a Windows only tool).  I run it and open the logo.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-06-at-7-39-58-am/" rel="attachment wp-att-10479"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.39.58-AM-1024x716.png" alt="" width="1024" height="716" class="alignnone size-large wp-image-10479" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.39.58-AM-1024x716.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.39.58-AM-300x210.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.39.58-AM-768x537.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.39.58-AM-600x420.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.39.58-AM.png 1124w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Then I click save and pick &#8220;C&#8221; bitmap file (*.c)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-06-at-7-40-44-am/" rel="attachment wp-att-10481"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.40.44-AM-1024x876.png" alt="" width="1024" height="876" class="alignnone size-large wp-image-10481" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.40.44-AM-1024x876.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.40.44-AM-300x257.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.40.44-AM-768x657.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.40.44-AM-600x513.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.40.44-AM.png 1122w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>For my display I am using 16-bit color, also known as High Color with Alpha 565.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-06-at-7-41-22-am/" rel="attachment wp-att-10482"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.41.22-AM.png" alt="" width="604" height="456" class="alignnone size-large wp-image-10482" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.41.22-AM.png 604w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.41.22-AM-300x226.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.41.22-AM-600x453.png 600w" sizes="auto, (max-width: 604px) 100vw, 604px" /></a></p>
<p>The tool nicely makes me a file with the right stuff in it.  First, the array of unsigned char (representing the bitmap) and the GUI_BITMAP structure.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/screen-shot-2020-12-06-at-7-44-30-am/" rel="attachment wp-att-10483"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.44.30-AM-1024x928.jpg" alt="" width="1024" height="928" class="alignnone size-large wp-image-10483" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.44.30-AM-1024x928.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.44.30-AM-300x272.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.44.30-AM-768x696.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.44.30-AM-1536x1392.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.44.30-AM-600x544.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-7.44.30-AM.jpg 1644w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Unfortunately it didn&#8217;t create a header file which I do like this (notice I renamed the data)</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#pragma once
#include &lt;stdlib.h&gt;

#include "GUI.h"

extern GUI_CONST_STORAGE GUI_BITMAP bmIOTexpert_Logo_Vertical320x240;</pre>
<p>And, if you have been paying attention you might have noticed that I created a new IoT Expert Library &#8211; Graphics that contains these new files. (I have explained this process several times in the past)</p>
<h1>TFT Pins Discussion</h1>
<p>I am planning on making a custom circuit board for this whole project.  So, I didn&#8217;t want to use the whole CY8CKIT-028-TFT library.  In order to start the TFT you need to call the function &#8220;mtb_st7789v()&#8221; with an argument of type &#8220;mtb_st7789v_pins_t&#8221;.  When Cypress created the BSP for the TFT we defined a function like this in the header file.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">/**
 * Initializes GPIOs for the software i8080 interface.
 * @return CY_RSLT_SUCCESS if successfully initialized, else an error about
 * what went wrong
 */
static inline cy_rslt_t cy_tft_io_init(void)
{
    static const mtb_st7789v_pins_t PINS =
    {
        .db08 = CY8CKIT_028_TFT_PIN_DISPLAY_DB8,
        .db09 = CY8CKIT_028_TFT_PIN_DISPLAY_DB9,
        .db10 = CY8CKIT_028_TFT_PIN_DISPLAY_DB10,
        .db11 = CY8CKIT_028_TFT_PIN_DISPLAY_DB11,
        .db12 = CY8CKIT_028_TFT_PIN_DISPLAY_DB12,
        .db13 = CY8CKIT_028_TFT_PIN_DISPLAY_DB13,
        .db14 = CY8CKIT_028_TFT_PIN_DISPLAY_DB14,
        .db15 = CY8CKIT_028_TFT_PIN_DISPLAY_DB15,
        .nrd = CY8CKIT_028_TFT_PIN_DISPLAY_NRD,
        .nwr = CY8CKIT_028_TFT_PIN_DISPLAY_NWR,
        .dc = CY8CKIT_028_TFT_PIN_DISPLAY_DC,
        .rst = CY8CKIT_028_TFT_PIN_DISPLAY_RST,
    };
    return mtb_st7789v_init8(&amp;PINS);
}
</pre>
<p>Curiously we also made a static definition inside of the .c file.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static const mtb_st7789v_pins_t tft_pins =
{
    .db08 = CY8CKIT_028_TFT_PIN_DISPLAY_DB8,
    .db09 = CY8CKIT_028_TFT_PIN_DISPLAY_DB9,
    .db10 = CY8CKIT_028_TFT_PIN_DISPLAY_DB10,
    .db11 = CY8CKIT_028_TFT_PIN_DISPLAY_DB11,
    .db12 = CY8CKIT_028_TFT_PIN_DISPLAY_DB12,
    .db13 = CY8CKIT_028_TFT_PIN_DISPLAY_DB13,
    .db14 = CY8CKIT_028_TFT_PIN_DISPLAY_DB14,
    .db15 = CY8CKIT_028_TFT_PIN_DISPLAY_DB15,
    .nrd = CY8CKIT_028_TFT_PIN_DISPLAY_NRD,
    .nwr = CY8CKIT_028_TFT_PIN_DISPLAY_NWR,
    .dc = CY8CKIT_028_TFT_PIN_DISPLAY_DC,
    .rst = CY8CKIT_028_TFT_PIN_DISPLAY_RST,
};
</pre>
<p>What I know is that I need those pin definitions, so I will copy them into my project (a bit later in this article)</p>
<h1>Make a New Display Task</h1>
<p>To get this going, make a file called displayManager.h to contain the public definition of the task.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#pragma once

void dm_task(void *arg);
</pre>
<p>Then add a file displayManager.c.  This file will be the focus of the rest of this article.  Before we get too far down the road building all of the functionality of the display, first lets setup a test task to verify the functionality of the libraries and the screen.  As I talked about earlier, define the pins for the driver.  These the pin names come out of the BSP and &#8220;should&#8221; work for all of the Cypress development kits (that have Arduino headers).</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#include "GUI.h"
#include "mtb_st7789v.h"
#include "cybsp.h"

#include "FreeRTOS.h"
#include "task.h"

#include "displayManager.h"

#include "IOTexpert_Logo_Vertical320x240.h"

const mtb_st7789v_pins_t tft_pins =
{
    .db08 = (CYBSP_J2_2),
    .db09 = (CYBSP_J2_4),
    .db10 = (CYBSP_J2_6),
    .db11 = (CYBSP_J2_10),
    .db12 = (CYBSP_J2_12),
    .db13 = (CYBSP_D7),
    .db14 = (CYBSP_D8),
    .db15 = (CYBSP_D9),
    .nrd  = (CYBSP_D10),
    .nwr  = (CYBSP_D11),
    .dc   = (CYBSP_D12),
    .rst  = (CYBSP_D13)
};
</pre>
<p>Then make a simple task that</p>
<ol>
<li>Initializes the driver</li>
<li>Initialize emWin</li>
<li>Sets the background color</li>
<li>Clear the screen</li>
<li>Draw the IoT Expert Logo</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void dm_task(void *arg)
{
    /* Initialize the display controller */
    mtb_st7789v_init8(&amp;tft_pins);
    GUI_Init();
    GUI_SetBkColor(GUI_WHITE);
    GUI_Clear();
    GUI_DrawBitmap(&amp;bmIOTexpert_Logo_Vertical320x240,0,11);

    while(1)
        vTaskDelay(portMAX_DELAY);
}
</pre>
<h1>Test</h1>
<p>When I program this project I get a nice white background screen with my beautiful logo on it. Sweet.  In the next article I&#8217;ll do some work on displaying useful information on the screen.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/img_0311/" rel="attachment wp-att-10434"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-1024x768.jpg" alt="" width="1024" height="768" class="alignnone wp-image-10434 size-large" srcset="https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-1024x768.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-300x225.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-768x576.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-1536x1152.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-2048x1536.jpg 2048w, https://iotexpert.com/wp-content/uploads/2020/12/IMG_0311-scaled-e1606907525730-600x450.jpg 600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 8) Read the Database</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 15 Mar 2021 13:17:09 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10440</guid>

					<description><![CDATA[Summary This article expands the functionality of the Tilt Database by adding functions to get the status of Tilts from other threads in the system.  It demonstrates the FreeRTOS queue&#8217;s to provide a generic mechanism for communication between threads. Story At this point the Tilt Data Manager has all of the knowledge of the status [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>This article expands the functionality of the Tilt Database by adding functions to get the status of Tilts from other threads in the system.  It demonstrates the FreeRTOS queue&#8217;s to provide a generic mechanism for communication between threads.</p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
<h1>Story</h1>
<p>At this point the Tilt Data Manager has all of the knowledge of the status of Tilts.  But, we have other threads which need access to that data.  How are they going to get it in a thread safe way?  Moreover, how am I going to provide access to this data without hardcoding specific information about the threads into the Tilt Data Manager?  In this article I add functionality to the Tilt Data Manager Thread to provide Tilt status.  Then I will test the functionality by adding test code into the ntshell.  The green boxes are the functional blocks I will touch in this update.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/part8-2/" rel="attachment wp-att-10442"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/part8-1024x514.png" alt="" width="1024" height="514" class="alignnone size-large wp-image-10442" srcset="https://iotexpert.com/wp-content/uploads/2020/12/part8-1024x514.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/part8-300x151.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/part8-768x385.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/part8-1536x771.png 1536w, https://iotexpert.com/wp-content/uploads/2020/12/part8-600x301.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/part8.png 1614w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>&nbsp;</p>
<h1>Update the Public Interface to tiltDataManager.h</h1>
<p>As I thought about what the other threads in the system would need I decided the best thing to do would be to write their public APIs.  Here it is:</p>
<ul>
<li>What Color is the &#8220;Tilt&#8221; i.e. &#8220;Pink&#8221;</li>
<li>How many Tilts are in the system (so that you can loop through them)</li>
<li>What Tilts are currently active &#8211; a bitmask</li>
<li>How many events have been seen for each Tilt</li>
<li>Please give me a copy of the current data.</li>
</ul>
<pre class="EnlighterJSRAW" data-enlighter-language="c">/////////////// Generally callable threadsafe - non blocking
char *tdm_colorString(tdm_tiltHandle_t handle);       // Return a char * to the color string for the tilt handle
int tdm_getNumTilt();                                 // Returns the number of possible tilts (probably always 8)
uint32_t tdm_getActiveTiltMask();                     // Return a bitmask of the active handles
uint32_t tdm_getNumDataSeen(tdm_tiltHandle_t handle); // Return number of data points seen
tdm_tiltData_t *tdm_getTiltData(tdm_tiltHandle_t handle);</pre>
<h1>Create the Thread Unsafe Functions</h1>
<p>Obviously you need to do things thread safely.  However, I decided to have a few functions which couldn&#8217;t hurt anything, but are unsafe.  (Sorry Butch&#8230; comment below if you don&#8217;t agree and Ill fix it).  To make this a bit safer, I set the data as volatile to make the compiler at least write it back into memory when things are done.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef struct  {
    char *colorName;
    uint8_t uuid[TILT_IBEACON_HEADER_LEN];
    volatile tdm_tiltData_t *data;
    volatile int numDataPoints;
    volatile int numDataSeen;
} tilt_t;</pre>
<p>The functions are very straight forward.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">char *tdm_colorString(tdm_tiltHandle_t handle)
{
    return tiltDB[handle].colorName;
}

int tdm_getNumTilt()
{
    return NUM_TILT;
}

uint32_t tdm_getActiveTiltMask()
{
    uint32_t mask=0;
    for(int i=0;i&lt;NUM_TILT;i++)
    {
        if(tiltDB[i].data)
            mask |= 1&lt;&lt;i;
    }
    return mask;
}

uint32_t tdm_getNumDataSeen(tdm_tiltHandle_t handle)
{
    return tiltDB[handle].numDataSeen;
}</pre>
<h1>Thread Safe Communication</h1>
<p>Now, a much better answer for thread safety.  If you have multiple tasks that need access to the same data it is very tempting to just communicate between the threads with global variables (see above).  You are just asking for trouble because one task might be partially through updating data, when it is interrupted by the RTOS scheduler.  Then another threads starts reading/writing from global variables that were incompletely updated by the suspended thread.  BAD!</p>
<p>So, what is a person to do?</p>
<p>What I typically do is use an RTOS queue to communicate between the two tasks.  In the picture below you can see that the ntshell will send a message to the Tilt Data Manager that it wants to know the data from a Tilt (using the handle) and it wants the response sent back to the &#8220;response queue&#8221;.  When the Tilt Data Manager gets this command, it will build a response and push it back into the &#8220;response queue&#8221;.  With this scheme the Tilt Data Manager does not know which thread that it is talking to, just the handle of the queue.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/part8-data-2/" rel="attachment wp-att-10450"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/part8-data-1.png" alt="" width="982" height="330" class="alignnone size-full wp-image-10450" srcset="https://iotexpert.com/wp-content/uploads/2020/12/part8-data-1.png 982w, https://iotexpert.com/wp-content/uploads/2020/12/part8-data-1-300x101.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/part8-data-1-768x258.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/part8-data-1-600x202.png 600w" sizes="auto, (max-width: 982px) 100vw, 982px" /></a></p>
<h1>Create the Thread Safe Function</h1>
<p>Inside of my code, the first step in creating the thread safe communication is to add a command to the Tilt Commands, TDM_CMD_GET_DATAPOINT</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef enum {
    TDM_CMD_ADD_DATAPOINT,
    TDM_CMD_PROCESS_DATA,
    TDM_CMD_GET_DATAPOINT,
} tdm_cmd_t;</pre>
<p>The response to all questions about Tilts will be in the form of a &#8220;tdm_tiltData_t&#8221; structure.  Specifically one that has been malloc&#8217;d on the heap.  To support this I will create a function that:</p>
<ol>
<li>Mallocs a new structure</li>
<li>Copys the data</li>
<li>Fix the averaging</li>
<li>Returns a pointer to the new data</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">// This function returns a malloc'd copy of the front of the most recent datapoint ... this function should only be used 
// internally because it is not thread safe.
static tdm_tiltData_t *tdm_getDataPointCopy(tdm_tiltHandle_t handle)
{
    tdm_tiltData_t *dp;
    dp = malloc(sizeof(tdm_tiltData_t));
    memcpy(dp,(tdm_tiltData_t *)tiltDB[handle].data,sizeof(tdm_tiltData_t));

    dp-&gt;gravity = dp-&gt;gravity / tiltDB[handle].numDataPoints;
    dp-&gt;temperature = dp-&gt;temperature / tiltDB[handle].numDataPoints;

    return dp;
}</pre>
<p>Then I update the command queue to deal with the get data message.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">            case TDM_CMD_GET_DATAPOINT:
                dp = tdm_getDataPointCopy(msg.tilt);
                xQueueSend(msg.msg,&amp;dp,10);
            break;</pre>
<p>Then I create the public function for other threads to call the send the request for the data command.  This function is called in the context of the calling thread NOT the tiltDataManager.  It will create a temporary queue for the transaction, send the command, then sit and wait for the response.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">tdm_tiltData_t *tdm_getTiltData(tdm_tiltHandle_t handle)
{
    QueueHandle_t respqueue;
    tdm_tiltData_t *rsp;

    if(handle&lt;0 || handle&gt;=NUM_TILT || tiltDB[handle].data == 0 )
        return 0;

    respqueue = xQueueCreate(1,sizeof(tdm_tiltData_t *));
    if(respqueue == 0)
        return 0;

    tdm_cmdMsg_t msg;
    msg.msg = respqueue;
    msg.tilt = handle;
    msg.cmd =  TDM_CMD_GET_DATAPOINT;
    if(xQueueSend(tdm_cmdQueue,&amp;msg,0) != pdTRUE)
    {
        printf("failed to send to dmQueue\n");
    }
    xQueueReceive(respqueue,(void *)&amp;rsp,portMAX_DELAY);
    vQueueDelete(respqueue);
    return rsp;
}</pre>
<h1>Test with the ntshell</h1>
<p>To test things whole thing out I add a new command to ntshell.  This command just try all of the public APIs</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static int usrcmd_testdm(int argc, char **argv)
{
    tdm_tiltData_t *msg;

    printf("NumTilt = %d\n",tdm_getNumTilt());
    printf("Active = %X\n",(unsigned int)tdm_getActiveTiltMask());
    
    for(int i =0;i&lt;tdm_getNumTilt();i++)
    {
        printf("Color = %s\t#Seen=%d\t",
            tdm_getColorString(i),
            (int)tdm_getNumDataSeen(i));

        if(tdm_getActiveTiltMask() &amp; 1&lt;&lt;i)
        {
            msg = tdm_getTiltData(i);
            if(msg)
            {
                printf("G=%f\tT=%d",
                    msg-&gt;gravity,
                    (int)msg-&gt;temperature);
                free(msg);
            }
        }
        printf("\n");
    }
    return 0;
}
</pre>
<p>Here it is running.  Notice that I typed the command &#8220;testdm&#8221;  And that my test setup has heard two Tilts (remember I have a test broadcaster)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/screen-shot-2020-12-06-at-5-59-25-am/" rel="attachment wp-att-10453"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-5.59.25-AM-1024x518.png" alt="" width="1024" height="518" class="alignnone size-large wp-image-10453" srcset="https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-5.59.25-AM-1024x518.png 1024w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-5.59.25-AM-300x152.png 300w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-5.59.25-AM-768x389.png 768w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-5.59.25-AM-600x304.png 600w, https://iotexpert.com/wp-content/uploads/2020/12/Screen-Shot-2020-12-06-at-5.59.25-AM.png 1450w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>In the next article Ill add the TFT Display.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 7) Advertising Database</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 15 Feb 2021 14:04:31 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10363</guid>

					<description><![CDATA[Summary In part7 of the Tilt Hydrometer series I will create a new task called the &#8220;Tilt Data Manager&#8221; which will manage the current state of all of the Tilt Hydrometers. Story In the design of my system I want the Bluetooth Manager to get a high priority so that it can react to all [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>In part7 of the Tilt Hydrometer series I will create a new task called the &#8220;Tilt Data Manager&#8221; which will manage the current state of all of the Tilt Hydrometers.</p>
<h1>Story</h1>
<p>In the design of my system I want the Bluetooth Manager to get a high priority so that it can react to all of the advertising reports without dropping data.  In addition I want other tasks in the system to be able to find out about the state of &#8220;Tilts&#8221; without distracting the Bluetooth subsystem from its job, collecting data.  To that end I will create a new task called the &#8220;Tilt Data Manager&#8221; which have a command queue where the Bluetooth Manager can submit advertising reports and other tasks can ask for data. For example the future Display Manager task can ask for the current state of the Pink Tilt.  In addition this task will filter down the blast of data to a more reasonable amount.  In the picture below, you can see the current state of the architecture in blue, and the new updates in green.</p>
<p>&nbsp;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/part5/" rel="attachment wp-att-10365"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/part5-1024x514.png" alt="" width="1024" height="514" class="alignnone size-large wp-image-10365" srcset="https://iotexpert.com/wp-content/uploads/2020/11/part5-1024x514.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/part5-300x151.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/part5-768x385.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/part5-1536x771.png 1536w, https://iotexpert.com/wp-content/uploads/2020/11/part5-600x301.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/part5.png 1614w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
<h1>Tilt Database</h1>
<p>The first step is to build the data structures that will represent the database.  The question is, what do I want to save and how do I want to save it?  The answer to the first question is:</p>
<ol>
<li>Gravity</li>
<li>Temperature</li>
<li>RSSI</li>
<li>TxPower</li>
<li>Time (when the report was made)</li>
</ol>
<p>The answer to how is, a &#8220;struct&#8221; that represents one data point with the current data, and a pointer which can be used to make a linked list.  In future articles I will send this data structure around to the other tasks (like the display or filesystem) so I will put this structure into the public interface file &#8220;tiltDataManager.h&#8221;</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#pragma once

#include &lt;stdint.h&gt;
#include "FreeRTOS.h"
#include "queue.h"
#include "wiced_bt_ble.h"

typedef struct {
    float gravity;
    int temperature;
    int8_t rssi;
    int8_t txPower;
	uint32_t time;
    struct tdm_tiltData_t *next;
} tdm_tiltData_t;
</pre>
<p>You might recall from the previous article that I have an array of structures that hold information about the Tilts, one for Red, Green, Black, etc. I decided that rather than other tasks asking for a &#8220;int&#8221; that they should ask for a handle which is an alias for that int.  So, I define that next.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef int tdm_tiltHandle_t;</pre>
<p>The next step in building the database is to yank the struct and array out of the BluetoothManager.c and put it where it belongs, in the tiltDataManager.c.  In the list of devices I add a pointer to the &#8220;data&#8221; which is just a list of the datapoint defined in the structure tdb_tiltData_t.  In addition I add &#8220;numDataPoints&#8221; because I am going to implement a running average.  And a &#8220;numDataSeen&#8221; which is the total number that I have ever seen.  Here is the data structure:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#define TILT_IBEACON_HEADER_LEN 20
#define TILT_IBEACON_DATA_LEN 5
#define TILT_IBEACON_LEN (TILT_IBEACON_HEADER_LEN + TILT_IBEACON_DATA_LEN)
typedef struct  {
    char *colorName;
    uint8_t uuid[TILT_IBEACON_HEADER_LEN];
    tdm_tiltData_t *data;
    int numDataPoints;
    int numDataSeen;
} tilt_t;

#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},0,0,0},
    {"Green" ,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x20,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Black" ,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x30,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Purple",   {IBEACON_HEADER,0xA4,0x95,0xBB,0x40,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Orange",   {IBEACON_HEADER,0xA4,0x95,0xBB,0x50,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Blue"  ,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x60,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Yellow",   {IBEACON_HEADER,0xA4,0x95,0xBB,0x70,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Pink"  ,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x80,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
};
#define NUM_TILT (sizeof(tiltDB)/sizeof(tilt_t))</pre>
<h1>Update the Public Interface to the tiltDataManager</h1>
<p>Back in the tiltDataManager.h I will add two public function definitions</p>
<ol>
<li>The task (so that it can be started in main)</li>
<li>A function tdm_processIbeacon which the BluetoothManager can call to consume a datapoint</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void tdm_task(void *arg);
void tdm_processIbeacon(uint8_t *mfgAdvField,int len,wiced_bt_ble_scan_results_t *p_scan_result);
</pre>
<h1>Modify the Bluetooth Manager to Submit</h1>
<p>The next step is to yank out the old scanning callback in the Bluetooth Manager and replace it with a call to the new public interface to the tiltDataManager.  I spent a good bit of time thinking about where exactly I wanted the partition to exist between the Bluetooth Manager and the Tilt Data Manager.  I decided that the Tilt Data Manager would need to know how to decode the advertising packet, but only in that one function.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">case BTM_ENABLED_EVT:
            if (WICED_BT_SUCCESS == p_event_data-&gt;enabled.status)
            {
				wiced_bt_ble_observe(WICED_TRUE, 0,tdm_processIbeacon);
            }</pre>
<h1>Build the Command Queue</h1>
<p>The Tilt Data Manager will just &#8220;sit on a queue&#8221;, specifically a command queue.  When it receives messages from various sources it will do the needful.  For now there are two messages, TDM_CMD_ADD_DATA_POINT and TDM_CMD_PROCESS_DATA.  The message is a structure with</p>
<ol>
<li>What is the command</li>
<li>Which Tilt are we talking about</li>
<li>A generic void pointer</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef enum {
    TDM_CMD_ADD_DATAPOINT,
    TDM_CMD_PROCESS_DATA,
} tdm_cmd_t;

typedef struct {
    tdm_cmd_t cmd;
    tdm_tiltHandle_t tilt;
    void *msg;
} tdm_cmdMsg_t;

static QueueHandle_t tdm_cmdQueue;</pre>
<h1>Process the Command Queue &amp; tdm_task</h1>
<p>The main Tilt Data Manager task has two things going on</p>
<ol>
<li>A queue where messages can be sent</li>
<li>A periodic timer which will insert the message &#8220;TDM_CMD_PROCESS_DATA&#8221;</li>
</ol>
<p>The main tasks just waits for messages to be pushed into the command queue.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void tdm_task(void *arg)
{
    tdm_cmdQueue = xQueueCreate(10,sizeof(tdm_cmdMsg_t));
    tdm_cmdMsg_t msg;

    TimerHandle_t tdm_processDataTimer;

    // Call the process data function once per hour to filter the input data
    tdm_processDataTimer=xTimerCreate("process",1000*30,pdTRUE,0,tdm_submitProcessData);
    xTimerStart(tdm_processDataTimer,0);

    while(1)
    {
        xQueueReceive(tdm_cmdQueue,&amp;msg,portMAX_DELAY);
        switch(msg.cmd)
        {
            case TDM_CMD_ADD_DATAPOINT:
                tdm_addData(msg.tilt,msg.msg);
            break;

            case TDM_CMD_PROCESS_DATA:
                tdm_processData();
            break;
        }
    }
}</pre>
<h1>Process the Data</h1>
<p>Every 30 seconds a Process Data message is pushed into the command queue.  When that happens it calls the function tdm_processData which will:</p>
<ol>
<li>Iterate through the list of tilts</li>
<li>If there is data</li>
<li>Calculate the running average</li>
<li>Print out the current value</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">// There is a bunch of data coming out of the tilts... every second on 3 channels
// So you may endup with a boatload of data
// This function will take an average of all of the data that has been saved

static void tdm_processData()
{
    for(int i=0;i&lt;NUM_TILT;i++)
    {
        if(tiltDB[i].data == 0)
        {
            continue;
        }

        tiltDB[i].data-&gt;gravity /= tiltDB[i].numDataPoints;
        tiltDB[i].data-&gt;temperature /= tiltDB[i].numDataPoints;
        tiltDB[i].numDataPoints = 1;
        printf("Tilt %s Temperature = %d Gravity =%f\n",tiltDB[i].colorName,tiltDB[i].data-&gt;temperature,tiltDB[i].data-&gt;gravity);
    }
}</pre>
<h1>Add Data</h1>
<p>The add data function is called when a data point is submitted into the command queue.  When this happens there are two possible scenarios</p>
<ol>
<li>There is no history, in which case just setup the data pointer to this message</li>
<li>There is history, in which case just add to the running total.</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static void tdm_addData(tdm_tiltHandle_t handle, tdm_tiltData_t *data)
{
    if(tiltDB[handle].data == 0)
    {
        tiltDB[handle].data = data; 
        tiltDB[handle].numDataPoints = 1;
        tiltDB[handle].data-&gt;next=0;
    }
    else
    {
        tiltDB[handle].data-&gt;gravity     += data-&gt;gravity;
        tiltDB[handle].data-&gt;temperature += data-&gt;temperature;
        tiltDB[handle].numDataPoints += 1;

        free(data);
    }

    tiltDB[handle].numDataSeen += 1;
    tiltDB[handle].data-&gt;time    = data-&gt;time;
    tiltDB[handle].data-&gt;rssi    = data-&gt;rssi;
    tiltDB[handle].data-&gt;txPower = data-&gt;txPower;
}</pre>
<h1>Process the iBeacon</h1>
<p>The process iBeacon function is actually called in the context of the Bluetooth Manager.  But, if that is true, why did I put it into the Tilt Data Manager file?  Answer: because it needs to know all about what Tilts look like.  This function is essentially the same as the one from the previous article.  It looks for an iBeacon in the manufacturer specific data.  If it find it, then it decodes the Gravity, Temperature and txPower.  Then puts that data into a structure and submits it to the Tilt Data Manager command queue.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">//
// This function is called in the Bluetooth Manager Context
// Specifically it is the advertising observer callback
//
void tdm_processIbeacon(wiced_bt_ble_scan_results_t *p_scan_result,uint8_t *p_adv_data)
{

	uint8_t mfgFieldLen;
	uint8_t *mfgAdvField;
	mfgAdvField = wiced_bt_ble_check_advertising_data(p_adv_data,BTM_BLE_ADVERT_TYPE_MANUFACTURER,&amp;mfgFieldLen);
    
	if(!mfgAdvField)
        return;

    if(mfgFieldLen != TILT_IBEACON_LEN)
        return;

    for(int i=0;i&lt;NUM_TILT;i++)
    {
        if(memcmp(mfgAdvField,tiltDB[i].uuid,TILT_IBEACON_HEADER_LEN) == 0)
        {
            uint32_t timerTime = xTaskGetTickCount() / 1000;
		    int8_t txPower = mfgAdvField[24];
		    float gravity = ((float)((uint16_t)mfgAdvField[22] &lt;&lt; 8 | (uint16_t)mfgAdvField[23]))/1000;
		    int temperature = mfgAdvField[20] &lt;&lt; 8 | mfgAdvField[21];

            // The tilt repeater will send out 0's if it hasnt heard anything ... and they send out some crazy stuff
            // when they first startup 
            if(gravity&gt;2.0 || gravity&lt;0.95 || temperature &gt; 110 || gravity == 0 || temperature == 0)
                return;

            tdm_tiltData_t *data;
            data = malloc(sizeof(tdm_tiltData_t));
                
            data-&gt;gravity     = gravity;
            data-&gt;temperature = temperature;
            data-&gt;txPower     = txPower;
            data-&gt;time        = timerTime;
            data-&gt;rssi        = p_scan_result-&gt;rssi;

            tdm_submitNewData(i,data);
            
        }
    }
}
</pre>
<h1>Submit Commands</h1>
<p>To simply submitting commands, I create two helper functions:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void tdm_submitNewData(tdm_tiltHandle_t handle,tdm_tiltData_t *data)
{
    tdm_cmdMsg_t msg;
    msg.msg = data;
    msg.tilt = handle;
    msg.cmd =  TDM_CMD_ADD_DATAPOINT;
    if(xQueueSend(tdm_cmdQueue,&amp;msg,0) != pdTRUE)
    {
        printf("Failed to send to dmQueue\n");
        free(data);
    }
}

static void tdm_submitProcessData()
{
    tdm_cmdMsg_t msg;
    msg.cmd = TDM_CMD_PROCESS_DATA;
    xQueueSend(tdm_cmdQueue,&amp;msg,0);
}
</pre>
<h1>Update main.c</h1>
<p>In main.c I need to start the tdm_task</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">    xTaskCreate(tdm_task, "Tilt Data Manager", configMINIMAL_STACK_SIZE*2,0 /* args */ ,0 /* priority */, 0);
</pre>
<h1>Program and Test</h1>
<p>Now lets test this thing and see if I have it working.  Now I am glad that I did the work to build a Tilt Simulator.  You can see the window on top is the simulator.  I tell it to start broadcasting &#8220;Red&#8221; with Temperature of 70, Gravity of 1010 and TxPower of 99.  Then on the bottom window you can see that is what is coming out of the system every 30 seconds.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/screen-shot-2020-11-29-at-9-07-52-am/" rel="attachment wp-att-10420"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.07.52-AM-1024x879.png" alt="" width="1024" height="879" class="alignnone size-large wp-image-10420" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.07.52-AM-1024x879.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.07.52-AM-300x258.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.07.52-AM-768x660.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.07.52-AM-600x515.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-29-at-9.07.52-AM.png 1148w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>In the next article I will add the LCD display.</p>
<h1>tiltDataManager.h</h1>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#pragma once

#include &lt;stdint.h&gt;
#include "FreeRTOS.h"
#include "queue.h"
#include "wiced_bt_ble.h"

typedef struct {
    float gravity;
    int temperature;
    int8_t rssi;
    int8_t txPower;
	uint32_t time;
    struct tdm_tiltData_t *next;
} tdm_tiltData_t;

typedef int tdm_tiltHandle_t;

void tdm_task(void *arg);
void tdm_processIbeacon(wiced_bt_ble_scan_results_t *p_scan_result,uint8_t *p_adv_data);
</pre>
<h1>tiltDataManager.c</h1>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

#include "wiced_bt_ble.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"
#include "tiltDataManager.h"


/*********************************************************************************
*
* Tilt Database Definition
* 
*********************************************************************************/

#define TILT_IBEACON_HEADER_LEN 20
#define TILT_IBEACON_DATA_LEN 5
#define TILT_IBEACON_LEN (TILT_IBEACON_HEADER_LEN + TILT_IBEACON_DATA_LEN)
typedef struct  {
    char *colorName;
    uint8_t uuid[TILT_IBEACON_HEADER_LEN];
    tdm_tiltData_t *data;
    int numDataPoints;
    int numDataSeen;
} tilt_t;

#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},0,0,0},
    {"Green" ,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x20,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Black" ,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x30,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Purple",   {IBEACON_HEADER,0xA4,0x95,0xBB,0x40,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Orange",   {IBEACON_HEADER,0xA4,0x95,0xBB,0x50,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Blue"  ,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x60,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Yellow",   {IBEACON_HEADER,0xA4,0x95,0xBB,0x70,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
    {"Pink"  ,   {IBEACON_HEADER,0xA4,0x95,0xBB,0x80,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE},0,0,0},
};
#define NUM_TILT (sizeof(tiltDB)/sizeof(tilt_t))

/*********************************************************************************
* 
* Tilt Data Manager External Interface Queue
*
*********************************************************************************/
typedef enum {
    TDM_CMD_ADD_DATAPOINT,
    TDM_CMD_PROCESS_DATA,
} tdm_cmd_t;

typedef struct {
    tdm_cmd_t cmd;
    tdm_tiltHandle_t tilt;
    void *msg;
} tdm_cmdMsg_t;

static QueueHandle_t tdm_cmdQueue;

static void tdm_submitProcessData();



// There is a bunch of data coming out of the tilts... every second on 3 channels
// So you may endup with a boatload of data
// This function will take an average of all of the data that has been saved

static void tdm_processData()
{
    for(int i=0;i&lt;NUM_TILT;i++)
    {
        if(tiltDB[i].data == 0)
        {
            continue;
        }

        tiltDB[i].data-&gt;gravity /= tiltDB[i].numDataPoints;
        tiltDB[i].data-&gt;temperature /= tiltDB[i].numDataPoints;
        tiltDB[i].numDataPoints = 1;
        printf("Tilt %s Temperature = %d Gravity =%f\n",tiltDB[i].colorName,tiltDB[i].data-&gt;temperature,tiltDB[i].data-&gt;gravity);
    }
}


// This function will
// insert new data for that tilt if none has ever been seen
// or it will add the data to the current count
static void tdm_addData(tdm_tiltHandle_t handle, tdm_tiltData_t *data)
{
    if(tiltDB[handle].data == 0)
    {
        tiltDB[handle].data = data; 
        tiltDB[handle].numDataPoints = 1;
        tiltDB[handle].data-&gt;next=0;
    }
    else
    {
        tiltDB[handle].data-&gt;gravity     += data-&gt;gravity;
        tiltDB[handle].data-&gt;temperature += data-&gt;temperature;
        tiltDB[handle].numDataPoints += 1;

        free(data);
    }

    tiltDB[handle].numDataSeen += 1;
    tiltDB[handle].data-&gt;time    = data-&gt;time;
    tiltDB[handle].data-&gt;rssi    = data-&gt;rssi;
    tiltDB[handle].data-&gt;txPower = data-&gt;txPower;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Public Functions
// 
////////////////////////////////////////////////////////////////////////////////////////////////////////

void tdm_task(void *arg)
{
    tdm_cmdQueue = xQueueCreate(10,sizeof(tdm_cmdMsg_t));
    tdm_cmdMsg_t msg;

    TimerHandle_t tdm_processDataTimer;

    // Call the process data function once per hour to filter the input data
    tdm_processDataTimer=xTimerCreate("process",1000*30,pdTRUE,0,tdm_submitProcessData);
    xTimerStart(tdm_processDataTimer,0);

    while(1)
    {
        xQueueReceive(tdm_cmdQueue,&amp;msg,portMAX_DELAY);
        switch(msg.cmd)
        {
            case TDM_CMD_ADD_DATAPOINT:
                tdm_addData(msg.tilt,msg.msg);
            break;

            case TDM_CMD_PROCESS_DATA:
                tdm_processData();
            break;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// These functions submit commands to main command queue: tdm_cmdQueue
// 
////////////////////////////////////////////////////////////////////////////////////////////////////////

void tdm_submitNewData(tdm_tiltHandle_t handle,tdm_tiltData_t *data)
{
    tdm_cmdMsg_t msg;
    msg.msg = data;
    msg.tilt = handle;
    msg.cmd =  TDM_CMD_ADD_DATAPOINT;
    if(xQueueSend(tdm_cmdQueue,&amp;msg,0) != pdTRUE)
    {
        printf("Failed to send to dmQueue\n");
        free(data);
    }
}

static void tdm_submitProcessData()
{
    tdm_cmdMsg_t msg;
    msg.cmd = TDM_CMD_PROCESS_DATA;
    xQueueSend(tdm_cmdQueue,&amp;msg,0);
}


//
// This function is called in the Bluetooth Manager Context
// Specifically it is the advertising observer callback
//
void tdm_processIbeacon(wiced_bt_ble_scan_results_t *p_scan_result,uint8_t *p_adv_data)
{

	uint8_t mfgFieldLen;
	uint8_t *mfgAdvField;
	mfgAdvField = wiced_bt_ble_check_advertising_data(p_adv_data,BTM_BLE_ADVERT_TYPE_MANUFACTURER,&amp;mfgFieldLen);
    
	if(!mfgAdvField)
        return;

    if(mfgFieldLen != TILT_IBEACON_LEN)
        return;

    for(int i=0;i&lt;NUM_TILT;i++)
    {
        if(memcmp(mfgAdvField,tiltDB[i].uuid,TILT_IBEACON_HEADER_LEN) == 0)
        {
            uint32_t timerTime = xTaskGetTickCount() / 1000;
		    int8_t txPower = mfgAdvField[24];
		    float gravity = ((float)((uint16_t)mfgAdvField[22] &lt;&lt; 8 | (uint16_t)mfgAdvField[23]))/1000;
		    int temperature = mfgAdvField[20] &lt;&lt; 8 | mfgAdvField[21];

            // The tilt repeater will send out 0's if it hasnt heard anything ... and they send out some crazy stuff
            // when they first startup 
            if(gravity&gt;2.0 || gravity&lt;0.95 || temperature &gt; 110 || gravity == 0 || temperature == 0)
                return;

            tdm_tiltData_t *data;
            data = malloc(sizeof(tdm_tiltData_t));
                
            data-&gt;gravity     = gravity;
            data-&gt;temperature = temperature;
            data-&gt;txPower     = txPower;
            data-&gt;time        = timerTime;
            data-&gt;rssi        = p_scan_result-&gt;rssi;

            tdm_submitNewData(i,data);
            
        }
    }
}
</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 08 Feb 2021 12:52:56 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10408</guid>

					<description><![CDATA[Summary An update to the Tilt Hydrometer Simulator to allow individual advertising packets to be turned on &#38; off using the Multi-Advertising Capability of PSoC &#38; CYW43012 Story After I finished the last article I was sure that I was done with the Tilt Simulator.  But, when I walked away I started thinking about what [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>An update to the Tilt Hydrometer Simulator to allow individual advertising packets to be turned on &amp; off using the Multi-Advertising Capability of PSoC &amp; CYW43012</p>
<h1>Story</h1>
<p>After I finished the last article I was sure that I was done with the Tilt Simulator.  But, when I walked away I started thinking about what I had done, and I decided that I needed to make one more update.  Specifically I wanted to add two more commands, &#8220;enable&#8221; and &#8220;disable&#8221;.  I also realized that I had the wrong idea about the implementation of multi-adv.  I thought that you had to have all of the slots from 1 to n active at the same time.  The more I thought about it the more I realized that was wrong.  This is really cool for my situation as there are 8 different color Tilts and the are at least 8 slots.  So, I can assign each Tilt to its own slot in the controller.</p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
<h1>Change the Database</h1>
<p>Remember that my database is just an array of tilt_t structures.  That array goes from 0 to NUM_TILT and that the multi advertising capability goes from 1-a bunch.  So my algorithm for assigning tilts is just to put i+1, in other words the first Tilt is 0 and it will go in slot 1 etc.  Here is the original structure.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef struct  {
    char *colorName;    // The color string
    int slot;           // Which Bluetooth ADV Slot this Tilt is using
    bool dirty;         // A flag to tell if there has been a change since the adv was last written
    int rate;           // rate in ms
    int tempUpdate;     // The update rate for temperature
    int gravUpdate;     // The update rate for gravity
    uint8_t advData[TILT_IBEACON_LEN];
} tilt_t;</pre>
<p>To make this work, I will</p>
<ol>
<li>Get rid of slot</li>
<li>Add a boolean field called &#8220;enable&#8221;</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef struct  {
    char *colorName;    // The color string
    bool enable;
    bool dirty;         // A flag to tell if there has been a change since the adv was last written
    int rate;           // rate in ms
    int tempUpdate;     // The update rate for temperature
    int gravUpdate;     // The update rate for gravity
    uint8_t advData[TILT_IBEACON_LEN];
} tilt_t;</pre>
<h1>Add an Initialization Function</h1>
<p>Instead of re-initializing each advertising slot each time, I realized that I could initialize them all at once.  So, I create a function to do just that.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void btm_initialize()
{
    static wiced_bt_ble_multi_adv_params_t myParams = {
    .adv_int_min       = BTM_BLE_ADVERT_INTERVAL_MIN,
    .adv_int_max       = 96,
    .adv_type          = MULTI_ADVERT_NONCONNECTABLE_EVENT,
    .channel_map       = BTM_BLE_ADVERT_CHNL_37 | BTM_BLE_ADVERT_CHNL_38 | BTM_BLE_ADVERT_CHNL_39,
    .adv_filter_policy = BTM_BLE_ADVERT_FILTER_ALL_CONNECTION_REQ_ALL_SCAN_REQ,
    .adv_tx_power      = MULTI_ADV_TX_POWER_MAX_INDEX,
    .peer_bd_addr      = {0},
    .peer_addr_type    = BLE_ADDR_PUBLIC,
    .own_bd_addr       = {0},
    .own_addr_type     = BLE_ADDR_PUBLIC,
    };

    for(int i=0;i&lt;NUM_TILT;i++)
    {
        wiced_set_multi_advertisement_data(tiltDB[i].advData,sizeof(tiltDB[i].advData),i+1);
        wiced_set_multi_advertisement_params(i+1,&amp;myParams);
        wiced_start_multi_advertisements( MULTI_ADVERT_STOP, i+1);
    }
}
</pre>
<p>Then I updated the stack startup event to call the initialize function.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">        case BTM_ENABLED_EVT:
            printf("Started BT Stack Succesfully\n");
            btm_initialize();
            btm_cmdQueue = xQueueCreate(10,sizeof(btm_cmdMsg_t));
            wiced_init_timer_ext(&amp;btm_processDataTimer,btm_processCmdQueue,0,WICED_TRUE);
            wiced_start_timer_ext(&amp;btm_processDataTimer,BTM_QUEUE_RATE);
        break;</pre>
<h1>Update the Function btm_setAdvPacket</h1>
<p>When it is time to update the advertising packets, I just loop through the database</p>
<ol>
<li>If the tilt is dirty</li>
<li>Then update the data</li>
<li>And either turn on/off the advertisements for the tilt based on the state of the enable flag</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void btm_setAdvPacket()
{
    for(int i=0;i&lt;NUM_TILT;i++)
    {
        if(tiltDB[i].dirty)
        {   
            tiltDB[i].dirty = false;
            wiced_set_multi_advertisement_data(tiltDB[i].advData,sizeof(tiltDB[i].advData),i+1);
        
            if(tiltDB[i].enable)
                wiced_start_multi_advertisements( MULTI_ADVERT_START, i+1 );
            else
                wiced_start_multi_advertisements( MULTI_ADVERT_STOP, i+1 );
        }
    }
}</pre>
<h1>Add New Commands</h1>
<p>Now I need to add two new commands, BTM_CMD_ENABLE and BTM_CMD_DISABLE</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">typedef enum {
    BTM_CMD_SET_DATA,
    BTM_CMD_PRINT_TABLE,
    BTM_CMD_SET_UPDATE,
    BTM_CMD_ENABLE,
    BTM_CMD_DISABLE,
} btm_cmd_t;</pre>
<p>The code for enable is just to turn the enable flag on then turn on the advertising</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">            case BTM_CMD_ENABLE:
                tiltDB[msg.num].enable = true;
                wiced_start_multi_advertisements( MULTI_ADVERT_START, msg.num+1 );
            break;
</pre>
<p>A function for usrcmd is just to queue the message to enable</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void btm_updateEnable(int num)
{
    if(btm_cmdQueue == 0 || num&lt;0 || num&gt;=NUM_TILT)
    {
        return;
    }
    btm_cmdMsg_t msg;
    msg.cmd =    BTM_CMD_ENABLE;
    msg.num = num;
    xQueueSend(btm_cmdQueue,&amp;msg,0);
}</pre>
<p>The command for disable just disables the flag and the advertising</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">            case BTM_CMD_DISABLE:
                tiltDB[msg.num].enable = false;
                wiced_start_multi_advertisements( MULTI_ADVERT_STOP, msg.num+1 );
            break;</pre>
<p>And the usrcmd function just queues the message.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void btm_updateDisable(int num)
{
    if(btm_cmdQueue == 0 || num&lt;0 || num&gt;=NUM_TILT)
        return;
    btm_cmdMsg_t msg;
    msg.cmd =    BTM_CMD_DISABLE;
    msg.num = num;
    xQueueSend(btm_cmdQueue,&amp;msg,0);
}</pre>
<h1>Update the Printing</h1>
<p>Fix the print out to show the state of enabling instead of the &#8220;slot&#8221;</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void btm_printTable()
{
    printf("\n# Color   E   Rate T  UpT Grav UpG TxP\n");

    for(int i=0;i&lt;NUM_TILT;i++)
    {
        printf("%d %6s  %d %5d %3d %2d %4d %2d %3d\n",i,
            tiltDB[i].colorName,tiltDB[i].enable,
            tiltDB[i].rate*BTM_QUEUE_RATE,
            btm_getTemperature(i),tiltDB[i].tempUpdate,
            btm_getGravity(i),tiltDB[i].gravUpdate,
            tiltDB[i].advData[29]);
    }
}</pre>
<h1>Program and Test</h1>
<p>When I program the board I can</p>
<ol>
<li>set</li>
<li>print</li>
<li>enable</li>
<li>print</li>
<li>disable</li>
<li>print</li>
</ol>
<p>And see with the Tilt app on my phone that everything is working</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/screen-shot-2020-11-28-at-7-56-04-am/" rel="attachment wp-att-10410"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-28-at-7.56.04-AM-993x1024.png" alt="" width="993" height="1024" class="alignnone size-large wp-image-10410" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-28-at-7.56.04-AM-993x1024.png 993w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-28-at-7.56.04-AM-291x300.png 291w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-28-at-7.56.04-AM-768x792.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-28-at-7.56.04-AM-600x619.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-28-at-7.56.04-AM.png 1230w" sizes="auto, (max-width: 993px) 100vw, 993px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 5) Tilt Simulator &#038; Multi Advertising iBeacons</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 01 Feb 2021 14:20:47 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10368</guid>

					<description><![CDATA[Summary Using the PSoC 6 AnyCloud Bluetooth Stack to send out &#8220;multi-adv&#8221; Bluetooth advertising packets with a PSoC 6 and CYW43012.  Multi-adv means sending out multiple different simultaneous Bluetooth Advertising packets.  This includes building a project to simulate a Tilt Hydrometer. Story As I worked on the Tilt project I came to realize that what [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>Using the PSoC 6 AnyCloud Bluetooth Stack to send out &#8220;multi-adv&#8221; Bluetooth advertising packets with a PSoC 6 and CYW43012.  Multi-adv means sending out multiple different simultaneous Bluetooth Advertising packets.  This includes building a project to simulate a <a href="https://tilthydrometer.com" target="_blank" rel="noopener noreferrer">Tilt Hydrometer</a>.</p>
<h1>Story</h1>
<p>As I worked on the Tilt project I came to realize that what I really needed what a Tilt Simulator.  That is, a PSoC programmed with a command line interface that could send out the iBeacon messages that looked like the ones that the Tilt actually sent out.  Why?  Simple, all three of my Tilts are busy doing their day job measuring beer and they don&#8217;t have time to fool around being test subjects for my IoT Expert Articles.</p>
<p>The CYW43012 controller that I am using is capable of automatically rotating through a list of at least 8 beacons (maybe more, but I don&#8217;t know the exact number. I suppose I should figure it out).  In other words I can simultaneously simulate 8 Tilts at one time with one PSoC.</p>
<p>For this project, I want the project to be able to</p>
<ol>
<li>Simultaneously broadcast 0-8 of the Tilt iBeacon messages</li>
<li>Have a command line interface to control the Tilt iBeacons</li>
<li>Be able to set a ramp rate for Temperature and Gravity on a per Tilt basis</li>
</ol>
<p>The commands will be:</p>
<ol>
<li>print &#8211; print out the table of the information about the Tilt iBeacons</li>
<li>set &#8211; set the temperature, gravity and txPower e.g. &#8220;set 0 77 1020 99&#8221; would turn on beacon 1 with the temperature set to 77 and the gravity set to 1020 and the txPower set to 99</li>
<li>update &#8211; configure the update rate for temperature and gravity e.g. &#8220;update 6 1000 0 -1&#8221; would set tilt 6 with an update to happen once per 1000ms with a +0 temperature per update and a -1 gravity points per update</li>
</ol>
<p>Here is an example of the display.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/screen-shot-2020-11-27-at-7-04-29-am/" rel="attachment wp-att-10395"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.04.29-AM-1024x713.png" alt="" width="1024" height="713" class="alignnone size-large wp-image-10395" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.04.29-AM-1024x713.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.04.29-AM-300x209.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.04.29-AM-768x535.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.04.29-AM-600x418.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.04.29-AM.png 1192w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
<h1>Build the Basic Project</h1>
<p>Create a new project using the FreeRTOS NT Shell Template called Tilt Simulator</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/screen-shot-2020-11-25-at-11-11-31-am/" rel="attachment wp-att-10369"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.11.31-AM-1024x653.png" alt="" width="1024" height="653" class="alignnone size-large wp-image-10369" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.11.31-AM-1024x653.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.11.31-AM-300x191.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.11.31-AM-768x490.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.11.31-AM-1536x980.png 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.11.31-AM-2048x1307.png 2048w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.11.31-AM-600x383.png 600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Enable the bluetooth-freertos and btstack libraries.  Also enable the IoT Expert BT Utilities library.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/screen-shot-2020-11-25-at-11-19-14-am/" rel="attachment wp-att-10376"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.19.14-AM-899x1024.png" alt="" width="899" height="1024" class="alignnone size-large wp-image-10376" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.19.14-AM-899x1024.png 899w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.19.14-AM-263x300.png 263w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.19.14-AM-768x874.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.19.14-AM-600x683.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.19.14-AM.png 1126w" sizes="auto, (max-width: 899px) 100vw, 899px" /></a></p>
<p>In the command line copy the template bluetoothManager.* into the project</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/screen-shot-2020-11-25-at-11-14-30-am/" rel="attachment wp-att-10371"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.14.30-AM-1024x104.png" alt="" width="1024" height="104" class="alignnone size-large wp-image-10371" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.14.30-AM-1024x104.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.14.30-AM-300x30.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.14.30-AM-768x78.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.14.30-AM-600x61.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.14.30-AM.png 1398w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Start the Bluetooth configurator with &#8220;make config_bt&#8221; on the command line.  Press the new project file (the blank paper)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/screen-shot-2020-11-25-at-11-15-37-am/" rel="attachment wp-att-10372"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.37-AM-1024x996.png" alt="" width="1024" height="996" class="alignnone size-large wp-image-10372" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.37-AM-1024x996.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.37-AM-300x292.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.37-AM-768x747.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.37-AM-1536x1493.png 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.37-AM-600x583.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.37-AM.png 1732w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Select the P6 connectivity device</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/screen-shot-2020-11-25-at-11-15-49-am/" rel="attachment wp-att-10373"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.49-AM.png" alt="" width="806" height="246" class="alignnone size-large wp-image-10373" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.49-AM.png 806w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.49-AM-300x92.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.49-AM-768x234.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.15.49-AM-600x183.png 600w" sizes="auto, (max-width: 806px) 100vw, 806px" /></a></p>
<p>Press save and call your file &#8220;TiltSimulator&#8221; (it actually doesn&#8217;t matter what you call it).  This will generate the configuration structures you need.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/screen-shot-2020-11-25-at-11-16-21-am/" rel="attachment wp-att-10374"></a> <a href="https://iotexpert.com/?attachment_id=10375" rel="attachment wp-att-10375"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.16.52-AM.png" alt="" width="900" height="426" class="alignnone size-large wp-image-10375" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.16.52-AM.png 900w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.16.52-AM-300x142.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.16.52-AM-768x364.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-25-at-11.16.52-AM-600x284.png 600w" sizes="auto, (max-width: 900px) 100vw, 900px" /></a></p>
<p>In command line run &#8220;make vscode&#8221;.  Then edit the Makefile to include these components.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">COMPONENTS=FREERTOS WICED_BLE</pre>
<p>You should run a build/program right now as you will have a basic project with a blinking led, the Bluetooth stack running and a command line.</p>
<h1>Multiadv</h1>
<p>The CYW43012 has the ability to automatically rotate through a list of advertising packets.  Each packet can have different data including a different Bluetooth address (how crazy is that?)  Each packet can also be advertised at a different and configurable rate.  It does this all with no intervention by the host.</p>
<p>This functionality is documented in the Bluetooth Stack section called &#8220;MutiAdv&#8221; under Device Management.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/screen-shot-2020-11-27-at-7-50-32-am/" rel="attachment wp-att-10397"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.50.32-AM-1024x593.jpg" alt="" width="1024" height="593" class="alignnone size-large wp-image-10397" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.50.32-AM-1024x593.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.50.32-AM-300x174.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.50.32-AM-768x445.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.50.32-AM-1536x890.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.50.32-AM-2048x1187.jpg 2048w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-27-at-7.50.32-AM-600x348.jpg 600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Build the Database</h1>
<p>The database is just an array of structures.  There are only 8 entries in the database, one for each color Tilt.  In addition to a string representing the name, the database will have</p>
<ol>
<li>Which advertising slot that this tilt is using (0 means that it isn&#8217;t active)</li>
<li>If the data is &#8220;dirty&#8221; meaning it has changed in this table but has not yet been copied to the controller for broadcast</li>
<li>rate = how often to update the data in the that row</li>
<li>tempUPDATE = how much to change the data by each time an update is called</li>
<li>gravUPDATE = how much to change the gravity by each time an update is called</li>
<li>The actual bytes of the advertising packet</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">/*********************************************************************************
*
* Tilt Database Definition
* 
*********************************************************************************/

#define TILT_IBEACON_HEADER_LEN 25
#define TILT_IBEACON_DATA_LEN 5
#define TILT_IBEACON_LEN (TILT_IBEACON_HEADER_LEN + TILT_IBEACON_DATA_LEN)

typedef struct  {
    char *colorName;    // The color string
    int slot;           // Which Bluetooth ADV Slot this Tilt is using
    bool dirty;         // A flag to tell if there has been a change since the adv was last written
    int rate;           // rate in ms
    int tempUpdate;     // The update rate for temperature
    int gravUpdate;     // The update rate for gravity
    uint8_t advData[TILT_IBEACON_LEN];
} tilt_t;

// 02 01 04 = Flags
// 0x1A 0xFF = Manufacturer specific data
// 0x4c 0x00 = Apple
// 0x02 = iBeacon
// 0x15 = remaining data length
#define IBEACON_HEADER 0x02,0x01,0x04,0x1A,0xFF,0x4C,0x00,0x02,0x15

static tilt_t tiltDB [] =
{
    {"Red",    0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x10,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Green" , 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x20,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Black" , 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x30,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Purple", 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x40,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Orange", 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x50,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Blue"  , 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x60,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Yellow", 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x70,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Pink"  , 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x80,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
};
#define NUM_TILT (sizeof(tiltDB)/sizeof(tilt_t))

#define IBEACON_TEMP_HI (25)
#define IBEACON_TEMP_LO (26)
#define IBEACON_GRAV_HI (27)
#define IBEACON_GRAV_LO (28)
#define IBEACON_TXPOWER (29)</pre>
<p>Now I will make &#8220;getters and setters&#8221;.  These functions will set the correct bytes in the adverting packets, and can retrieve the current values in those bytes.  Remember in the Apple iBeacon format there are 5 &#8220;user&#8221; bytes</p>
<ol>
<li>Major byte 0 and byte 1</li>
<li>Minor byte 0 and bytes 1</li>
<li>txPower</li>
</ol>
<p>Apple didnt specify what you stored in the major/minor.  In fact they also didnt specify the endianness.  The Tilt guys decided that the would store BIG endian &#8211; oh well.</p>
<ol>
<li>Major = Temperature in degrees F</li>
<li>Minor = Gravity points i.e. 1050 means 1.050</li>
</ol>
<p>If you look in my code the &#8220;set&#8221; of temperature and gravity they will &#8220;wrap around&#8221;.  If you set a value BELOW the minimum it will set the highest value and if you set a value ABOVE the maximum it will set it to the lowest.  This allows my automatic update code to just count one direction.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">int btm_getTemperature(int num)
{
    return (uint16_t)tiltDB[num].advData[IBEACON_TEMP_HI] &lt;&lt; 8 | tiltDB[num].advData[IBEACON_TEMP_LO];
}

void btm_setTemperature(int num,uint16_t temperature)
{
    if(temperature &gt; 150)
        temperature = 10;
    if(temperature&lt;10)
        temperature = 150;

    int oldtemp = btm_getTemperature(num);

    tiltDB[num].advData[IBEACON_TEMP_HI] = (temperature &amp; 0xFF00) &gt;&gt; 8;    
    tiltDB[num].advData[IBEACON_TEMP_LO] = temperature &amp; 0xFF;
    if(temperature != oldtemp)
        tiltDB[num].dirty = true;    
}

int btm_getGravity(int num)
{
    return (uint16_t)tiltDB[num].advData[IBEACON_GRAV_HI] &lt;&lt; 8 | tiltDB[num].advData[IBEACON_GRAV_LO];
}

void btm_setGravity(int num,uint16_t gravity)
{
    // These if's cause the gravity to "wrap around" at 1200 and 900
    if(gravity&gt;1200)
        gravity = 900;
    if(gravity &lt;900)
        gravity = 1200;

    int oldgrav = btm_getGravity(num);
    tiltDB[num].advData[IBEACON_GRAV_HI] = (uint8_t)((gravity &amp; 0xFF00) &gt;&gt; 8);
    tiltDB[num].advData[IBEACON_GRAV_LO] = (uint8_t)(gravity &amp; 0xFF);
    if(oldgrav != gravity)
        tiltDB[num].dirty = true;
}

void btm_setTxPower(int num, int8_t txPower)
{
    tiltDB[num].advData[IBEACON_TXPOWER] = txPower;
    tiltDB[num].dirty = true;
}</pre>
<h1>Transfer Database to Bluetooth Controller</h1>
<p>The next two functions are used to bridge between the database and the controller.  The first function &#8220;btm_setAdvPacket&#8221; probably should have been called &#8220;btm_setAdvPackets&#8221; because its function is to copy the database bytes into the controller for actual broadcast.  This function is simply a loop that looks at each of the Tilts in the database.</p>
<ol>
<li>If they are in a &#8220;slot&#8221;</li>
<li>AND they are dirty (something has been changed since the last time this was called)</li>
<li>Then copy the data into the controller</li>
<li>Set the parameters</li>
<li>Start it advertising</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">// btm_setAdvPacket
//
// This function updates what the advertising packets and the state of the BT controller:
// It will loop through the table of iBeacons... then if they are in a slot &amp; they are dirty
// it will move them to the contoller and enable advertisements on that slot

void btm_setAdvPacket()
{
    static wiced_bt_ble_multi_adv_params_t myParams = {
    .adv_int_min       = BTM_BLE_ADVERT_INTERVAL_MIN,
    .adv_int_max       = 96,
    .adv_type          = MULTI_ADVERT_NONCONNECTABLE_EVENT,
    .channel_map       = BTM_BLE_ADVERT_CHNL_37 | BTM_BLE_ADVERT_CHNL_38 | BTM_BLE_ADVERT_CHNL_39,
    .adv_filter_policy = BTM_BLE_ADVERT_FILTER_ALL_CONNECTION_REQ_ALL_SCAN_REQ,
    .adv_tx_power      = MULTI_ADV_TX_POWER_MAX_INDEX,
    .peer_bd_addr      = {0},
    .peer_addr_type    = BLE_ADDR_PUBLIC,
    .own_bd_addr       = {0},
    .own_addr_type     = BLE_ADDR_PUBLIC,
    };

    for(int i=0;i&lt;NUM_TILT;i++)
    {
        if(tiltDB[i].slot &amp;&amp; tiltDB[i].dirty)
        {   
            tiltDB[i].dirty = false;
            wiced_set_multi_advertisement_data(tiltDB[i].advData,sizeof(tiltDB[i].advData),tiltDB[i].slot);
            wiced_set_multi_advertisement_params(tiltDB[i].slot,&amp;myParams);
            wiced_start_multi_advertisements( MULTI_ADVERT_START, tiltDB[i].slot );
        }
    }
}</pre>
<p>In order to &#8220;activate&#8221; a row in the database you need to assign it to a slot.  I do this by keeping track of the number of times I have assigned an entry in &#8220;btm_active&#8221; which is static to this function.  Now, shocker, I check to make sure that the caller didn&#8217;t accidentally call this before.  Finally I assign it to a slot and mark it as dirty (so that when the btm_setAdvPacket function is called the data will be put into the controller.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">/* btm_activate
*
* This function will put the tilt in the database entry num
* into the next available advertising slot
*
*/
void btm_activate(int num)
{
    // Keep track of the number of currently active iBeacons
    static int  btm_active=0;
    
    CY_ASSERT(num&lt;NUM_TILT);
    if(tiltDB[num].slot == 0) // Make sure that it is not already in a slow
    {
        btm_active += 1;
        tiltDB[num].slot = btm_active;
        tiltDB[num].dirty = true;
    }
}</pre>
<h1>Command Queue</h1>
<p>The command queue is used by the command line interface to submit changes to the Tilt database.  Specifically set, update and print.  The message is just the values for set &amp; update.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">/*********************************************************************************
* 
* Tilt Data Manager External Interface Queue
*
*********************************************************************************/
typedef enum {
    BTM_CMD_SET_DATA,
    BTM_CMD_PRINT_TABLE,
    BTM_CMD_SET_UPDATE,
} btm_cmd_t;

typedef struct {
    btm_cmd_t cmd;
    int num;
    int temperature;
    int gravity;
    int txPower;
} btm_cmdMsg_t;

static QueueHandle_t btm_cmdQueue=0;
static wiced_timer_ext_t btm_processDataTimer;</pre>
<p>The function to process the command queue has two functions in the systems</p>
<ol>
<li>Process commands from the command line</li>
<li>Cause the data to be updated (as configured by the command line)</li>
</ol>
<p>This function is run by a timer that is started when the Bluetooth stack turns on.  I have it set to run every 200ms.  Each time it runs it will read all of the messages in the queue and process them.  Once that is complete it will check to see if the Tilts need to be updated.</p>
<p>In my system there is a &#8220;base rate&#8221; which is set by the frequency of this function.  In other words the &#8220;count&#8221; variable counts the number of times this function has been called.  This is about &#8220;BTM_QUEUE_RATE&#8221;</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">/* btm_processCmdQueue
*
*  This function is called by a timer every BTM_QUEUE_RATE ms (around 200ms)
*  It processes commands from the GUI
*  It also updates the adverting packets if they are being updated at a rate
*
*/
void btm_processCmdQueue( wiced_timer_callback_arg_t cb_params )
{
    static int count = 0; // This counts the numbers of times the callback has happend

    btm_cmdMsg_t msg;
    while(xQueueReceive(btm_cmdQueue,&amp;msg,0) == pdTRUE)
    {
        switch(msg.cmd)
        {
            case BTM_CMD_SET_DATA:
                btm_setGravity(msg.num,msg.gravity);
                btm_setTemperature(msg.num,msg.temperature);
                btm_setTxPower(msg.num,msg.txPower);
                btm_activate(msg.num);
            break;

            case BTM_CMD_PRINT_TABLE:
                btm_printTable();
            break;

            case BTM_CMD_SET_UPDATE:
                tiltDB[msg.num].tempUpdate = msg.temperature;
                tiltDB[msg.num].gravUpdate = msg.gravity;
                tiltDB[msg.num].rate = msg.txPower / BTM_QUEUE_RATE;
            break;
        }
    }
    count = count + 1;

    // This block of code updates the current values with the update rate
    for(int i=0;i&lt;NUM_TILT;i++)
    {
        // If the slot active
        // and the update rate says that it is time
        if(tiltDB[i].slot &amp;&amp; count % tiltDB[i].rate  == 0)
        {
            btm_setTemperature(i,btm_getTemperature(i) + tiltDB[i].tempUpdate);
            btm_setGravity(i,btm_getGravity(i) + tiltDB[i].gravUpdate);
        }
    }
    btm_setAdvPacket();
}</pre>
<h1>Bluetooth Management</h1>
<p>When the Bluetooth Stack is turned on I need to do two things</p>
<ol>
<li>Initialize the command queue</li>
<li>Initialize a periodic timer that calls the &#8220;btm_processCmdQueue&#8221;</li>
</ol>
<p>Notice that every time something changes in the advertising packets the management callback is called with the event type &#8220;BTM_MULTI_ADVERT_RESP_EVENT&#8221;&#8230; to which I do NOTHING.  I suppose that I should check and make sure that whatever I did succeed.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">wiced_result_t app_bt_management_callback(wiced_bt_management_evt_t event, wiced_bt_management_evt_data_t *p_event_data)
{
    wiced_result_t result = WICED_BT_SUCCESS;

    switch (event)
    {
        case BTM_ENABLED_EVT:
            printf("Started BT Stack Succesfully\n");
            btm_cmdQueue = xQueueCreate(10,sizeof(btm_cmdMsg_t));
            wiced_init_timer_ext(&amp;btm_processDataTimer,btm_processCmdQueue,0,WICED_TRUE);
            wiced_start_timer_ext(&amp;btm_processDataTimer,BTM_QUEUE_RATE);
        break;

        case BTM_MULTI_ADVERT_RESP_EVENT: // Do nothing...
        break;

        default:
            printf("Unhandled Bluetooth Management Event: %s\n", btutil_getBTEventName( event));
        break;
    }

    return result;
}</pre>
<h1>Public Functions</h1>
<p>In the command line I want the ability to</p>
<ol>
<li>Print</li>
<li>Configure the data in Tilt advertising packet</li>
<li>Set the update rate</li>
</ol>
<p>That is what these three functions do.  Notice that I check that the command queue has been initialized.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">void btm_printTableCmd()
{
    if(btm_cmdQueue == 0)
        return;
    
    btm_cmdMsg_t msg;
    msg.cmd =    BTM_CMD_PRINT_TABLE;
    xQueueSend(btm_cmdQueue,&amp;msg,0);
}

void btm_setDataCmd(int num,int temperature,int gravity,int txPower)
{
    if(btm_cmdQueue == 0)
        return;

    btm_cmdMsg_t msg;
    msg.cmd =    BTM_CMD_SET_DATA;
    msg.num = num;
    msg.temperature = temperature;
    msg.gravity = gravity;
    msg.txPower = txPower;
    xQueueSend(btm_cmdQueue,&amp;msg,0);
}


void btm_updateDataCmd(int num,int rate ,int temperature,int gravity )
{
    if(btm_cmdQueue == 0)
        return;
    btm_cmdMsg_t msg;
    msg.cmd =    BTM_CMD_SET_UPDATE;
    msg.num = num;
    msg.temperature = temperature;
    msg.gravity = gravity;
    msg.txPower = rate;
    xQueueSend(btm_cmdQueue,&amp;msg,0);
}
</pre>
<h1>Update the Command Line</h1>
<p>In usrcmd.c I need the three functions which the command line will call.</p>
<ol>
<li>usrcmd_print &#8230; to print out the table</li>
<li>usrcmd_set to set the values in the Tilt database</li>
<li>usrcmd_update to set the update rate</li>
</ol>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static int usrcmd_print(int argc, char **argv)
{
    btm_printTableCmd();
    return 0;
}

static int usrcmd_set(int argc, char **argv)
{
    int gravity;
    int temperature;
    int txPower;
    int num;
    if(argc == 5)
    {
        sscanf(argv[1],"%d",&amp;num);
        sscanf(argv[2],"%d",&amp;temperature);
        sscanf(argv[3],"%d",&amp;gravity);
        sscanf(argv[4],"%d",&amp;txPower);

        btm_setDataCmd(num,temperature,gravity,txPower);
    }
    return 0;
}

static int usrcmd_update(int argc, char **argv)
{
    int gravity;
    int temperature;
    int rate;
    int num;
    if(argc == 5)
    {
        sscanf(argv[1],"%d",&amp;num);
        sscanf(argv[2],"%d",&amp;rate);
        sscanf(argv[3],"%d",&amp;temperature);
        sscanf(argv[4],"%d",&amp;gravity);

        btm_updateDataCmd(num,rate,temperature,gravity);
    }
    return 0;
}</pre>
<p>And once you have those functions you need to turn them on in the command line</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">static const cmd_table_t cmdlist[] = {
    { "help", "This is a description text string for help command.", usrcmd_help },
    { "info", "This is a description text string for info command.", usrcmd_info },
    { "clear", "Clear the screen", usrcmd_clear },
    { "pargs","print the list of arguments", usrcmd_pargs},
#ifdef configUSE_TRACE_FACILITY 
#if configUSE_STATS_FORMATTING_FUNCTIONS ==1
    { "tasks","print the list of RTOS Tasks", usrcmd_list},
#endif
#endif
    { "print", "Print table", usrcmd_print },
    { "set", "set num temperature gravity txpower", usrcmd_set },
    { "update", "update num rate temperature gravity", usrcmd_update },
};</pre>
<p>In the next article I will be back to the &#8220;client&#8221; end of my system and will build a database.</p>
<h1>Full Functions</h1>
<p>Here are the full functions &amp; files.  You can also &#8220;git&#8221; this at git@github.com:iotexpert/TiltSimulator.git</p>
<ol>
<li>bluetoothManager.h</li>
<li>bluetoothManager.c</li>
<li>main.c</li>
<li>usrcmd.c</li>
</ol>
<p><strong>bluetoothManager.h</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#pragma once
#include "wiced_bt_stack.h"
#include "wiced_bt_dev.h"

wiced_result_t app_bt_management_callback(wiced_bt_management_evt_t event, wiced_bt_management_evt_data_t *p_event_data);
void btm_printTable();

void btm_printTableCmd();
void btm_setDataCmd(int num,int temperature,int gravity,int txPower);
void btm_updateDataCmd(int num,int rate ,int temperature,int gravity );
</pre>
<p><strong>bluetoothManager.c</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#include "cybsp.h"

#include "FreeRTOS.h"

#include "bluetoothManager.h"
#include "wiced_bt_stack.h"
#include "wiced_bt_dev.h"
#include "wiced_bt_trace.h"
#include "wiced_timer.h"
#include "queue.h"
#include "btutil.h"

// Read the queue
#define BTM_QUEUE_RATE 200

/*********************************************************************************
*
* Tilt Database Definition
* 
*********************************************************************************/

#define TILT_IBEACON_HEADER_LEN 25
#define TILT_IBEACON_DATA_LEN 5
#define TILT_IBEACON_LEN (TILT_IBEACON_HEADER_LEN + TILT_IBEACON_DATA_LEN)

typedef struct  {
    char *colorName;    // The color string
    int slot;           // Which Bluetooth ADV Slot this Tilt is using
    bool dirty;         // A flag to tell if there has been a change since the adv was last written
    int rate;           // rate in ms
    int tempUpdate;     // The update rate for temperature
    int gravUpdate;     // The update rate for gravity
    uint8_t advData[TILT_IBEACON_LEN];
} tilt_t;

// 02 01 04 = Flags
// 0x1A 0xFF = Manufacturer specific data
// 0x4c 0x00 = Apple
// 0x02 = iBeacon
// 0x15 = remaining data length
#define IBEACON_HEADER 0x02,0x01,0x04,0x1A,0xFF,0x4C,0x00,0x02,0x15

static tilt_t tiltDB [] =
{
    {"Red",    0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x10,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Green" , 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x20,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Black" , 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x30,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Purple", 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x40,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Orange", 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x50,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Blue"  , 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x60,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Yellow", 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x70,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
    {"Pink"  , 0, true, 0,0,0, {IBEACON_HEADER,0xA4,0x95,0xBB,0x80,0xC5,0xB1,0x4B,0x44,0xB5,0x12,0x13,0x70,0xF0,0x2D,0x74,0xDE, 0x00,0x00,0x00,0x00,0x00}},
};
#define NUM_TILT (sizeof(tiltDB)/sizeof(tilt_t))

#define IBEACON_TEMP_HI (25)
#define IBEACON_TEMP_LO (26)
#define IBEACON_GRAV_HI (27)
#define IBEACON_GRAV_LO (28)
#define IBEACON_TXPOWER (29)

/*********************************************************************************
* 
* Tilt Data Manager External Interface Queue
*
*********************************************************************************/
typedef enum {
    BTM_CMD_SET_DATA,
    BTM_CMD_PRINT_TABLE,
    BTM_CMD_SET_UPDATE,
} btm_cmd_t;

typedef struct {
    btm_cmd_t cmd;
    int num;
    int temperature;
    int gravity;
    int txPower;
} btm_cmdMsg_t;

static QueueHandle_t btm_cmdQueue=0;
static wiced_timer_ext_t btm_processDataTimer;


/*********************************************************************************
* 
* These functions are used to interact with the Bluetooth Advertising Controller
*
*********************************************************************************/
// btm_setAdvPacket
//
// This function updates what the advertising packets and the state of the BT controller:
// It will loop through the table of iBeacons... then if they are in a slot &amp; they are dirty
// it will move them to the contoller and enable advertisements on that slot

void btm_setAdvPacket()
{
    static wiced_bt_ble_multi_adv_params_t myParams = {
    .adv_int_min       = BTM_BLE_ADVERT_INTERVAL_MIN,
    .adv_int_max       = 96,
    .adv_type          = MULTI_ADVERT_NONCONNECTABLE_EVENT,
    .channel_map       = BTM_BLE_ADVERT_CHNL_37 | BTM_BLE_ADVERT_CHNL_38 | BTM_BLE_ADVERT_CHNL_39,
    .adv_filter_policy = BTM_BLE_ADVERT_FILTER_ALL_CONNECTION_REQ_ALL_SCAN_REQ,
    .adv_tx_power      = MULTI_ADV_TX_POWER_MAX_INDEX,
    .peer_bd_addr      = {0},
    .peer_addr_type    = BLE_ADDR_PUBLIC,
    .own_bd_addr       = {0},
    .own_addr_type     = BLE_ADDR_PUBLIC,
    };

    for(int i=0;i&lt;NUM_TILT;i++)
    {
        if(tiltDB[i].slot &amp;&amp; tiltDB[i].dirty)
        {   
            tiltDB[i].dirty = false;
            wiced_set_multi_advertisement_data(tiltDB[i].advData,sizeof(tiltDB[i].advData),tiltDB[i].slot);
            wiced_set_multi_advertisement_params(tiltDB[i].slot,&amp;myParams);
            wiced_start_multi_advertisements( MULTI_ADVERT_START, tiltDB[i].slot );
        }
    }
}

/* btm_activate
*
* This function will put the tilt in the database entry num
* into the next available advertising slot
*
*/
void btm_activate(int num)
{
    // Keep track of the number of currently active iBeacons
    static int  btm_active=0;
    
    CY_ASSERT(num&lt;NUM_TILT);
    if(tiltDB[num].slot == 0) // Make sure that it is not already in a slow
    {
        btm_active += 1;
        tiltDB[num].slot = btm_active;
        tiltDB[num].dirty = true;
    }
}

/*********************************************************************************
* 
* This next set of functions is the API to the database
*
*********************************************************************************/


int btm_getTemperature(int num)
{
    return (uint16_t)tiltDB[num].advData[IBEACON_TEMP_HI] &lt;&lt; 8 | tiltDB[num].advData[IBEACON_TEMP_LO];
}

void btm_setTemperature(int num,uint16_t temperature)
{
    if(temperature &gt; 150)
        temperature = 10;
    if(temperature&lt;10)
        temperature = 150;

    int oldtemp = btm_getTemperature(num);

    tiltDB[num].advData[IBEACON_TEMP_HI] = (temperature &amp; 0xFF00) &gt;&gt; 8;    
    tiltDB[num].advData[IBEACON_TEMP_LO] = temperature &amp; 0xFF;
    if(temperature != oldtemp)
        tiltDB[num].dirty = true;    
}

int btm_getGravity(int num)
{
    return (uint16_t)tiltDB[num].advData[IBEACON_GRAV_HI] &lt;&lt; 8 | tiltDB[num].advData[IBEACON_GRAV_LO];
}

void btm_setGravity(int num,uint16_t gravity)
{
    // These if's cause the gravity to "wrap around" at 1200 and 900
    if(gravity&gt;1200)
        gravity = 900;
    if(gravity &lt;900)
        gravity = 1200;

    int oldgrav = btm_getGravity(num);
    tiltDB[num].advData[IBEACON_GRAV_HI] = (uint8_t)((gravity &amp; 0xFF00) &gt;&gt; 8);
    tiltDB[num].advData[IBEACON_GRAV_LO] = (uint8_t)(gravity &amp; 0xFF);
    if(oldgrav != gravity)
        tiltDB[num].dirty = true;
}

void btm_setTxPower(int num, int8_t txPower)
{
    tiltDB[num].advData[IBEACON_TXPOWER] = txPower;
    tiltDB[num].dirty = true;
}

void btm_printTable()
{
    printf("\n# Color   S   Rate T  UpT Grav UpG TxP\n");

    for(int i=0;i&lt;NUM_TILT;i++)
    {
        printf("%d %6s  %d %5d %3d %2d %4d %2d %3d\n",i,
            tiltDB[i].colorName,tiltDB[i].slot,
            tiltDB[i].rate*BTM_QUEUE_RATE,
            btm_getTemperature(i),tiltDB[i].tempUpdate,
            btm_getGravity(i),tiltDB[i].gravUpdate,
            tiltDB[i].advData[29]);
    }
}

/* btm_processCmdQueue
*
*  This function is called by a timer every BTM_QUEUE_RATE ms (around 200ms)
*  It processes commands from the GUI
*  It also updates the adverting packets if they are being updated at a rate
*
*/
void btm_processCmdQueue( wiced_timer_callback_arg_t cb_params )
{
    static int count = 0; // This counts the numbers of times the callback has happend

    btm_cmdMsg_t msg;
    while(xQueueReceive(btm_cmdQueue,&amp;msg,0) == pdTRUE)
    {
        switch(msg.cmd)
        {
            case BTM_CMD_SET_DATA:
                btm_setGravity(msg.num,msg.gravity);
                btm_setTemperature(msg.num,msg.temperature);
                btm_setTxPower(msg.num,msg.txPower);
                btm_activate(msg.num);
            break;

            case BTM_CMD_PRINT_TABLE:
                btm_printTable();
            break;

            case BTM_CMD_SET_UPDATE:
                tiltDB[msg.num].tempUpdate = msg.temperature;
                tiltDB[msg.num].gravUpdate = msg.gravity;
                tiltDB[msg.num].rate = msg.txPower / BTM_QUEUE_RATE;
            break;
        }
    }
    count = count + 1;

    // This block of code updates the current values with the update rate
    for(int i=0;i&lt;NUM_TILT;i++)
    {
        // If the slot active
        // and the update rate says that it is time
        if(tiltDB[i].slot &amp;&amp; count % tiltDB[i].rate  == 0)
        {
            btm_setTemperature(i,btm_getTemperature(i) + tiltDB[i].tempUpdate);
            btm_setGravity(i,btm_getGravity(i) + tiltDB[i].gravUpdate);
        }
    }
    btm_setAdvPacket();
}

/**************************************************************************************************
* Function Name: app_bt_management_callback()
***************************************************************************************************
* Summary:
*   This is a Bluetooth stack event handler function to receive management events from
*   the BLE stack and process as per the application.
*
* Parameters:
*   wiced_bt_management_evt_t event             : BLE event code of one byte length
*   wiced_bt_management_evt_data_t *p_event_data: Pointer to BLE management event structures
*
* Return:
*  wiced_result_t: Error code from WICED_RESULT_LIST or BT_RESULT_LIST
*
*************************************************************************************************/
wiced_result_t app_bt_management_callback(wiced_bt_management_evt_t event, wiced_bt_management_evt_data_t *p_event_data)
{
    wiced_result_t result = WICED_BT_SUCCESS;

    switch (event)
    {
        case BTM_ENABLED_EVT:
            printf("Started BT Stack Succesfully\n");
            btm_cmdQueue = xQueueCreate(10,sizeof(btm_cmdMsg_t));
            wiced_init_timer_ext(&amp;btm_processDataTimer,btm_processCmdQueue,0,WICED_TRUE);
            wiced_start_timer_ext(&amp;btm_processDataTimer,BTM_QUEUE_RATE);
        break;

        case BTM_MULTI_ADVERT_RESP_EVENT: // Do nothing...
        break;

        default:
            printf("Unhandled Bluetooth Management Event: %s\n", btutil_getBTEventName( event));
        break;
    }

    return result;
}

/*********************************************************************************
* 
* These are publicly callable functions to cause actions by the bluetooth manager
* These are called by the GUI
*
*********************************************************************************/

void btm_printTableCmd()
{
    if(btm_cmdQueue == 0)
        return;
    
    btm_cmdMsg_t msg;
    msg.cmd =    BTM_CMD_PRINT_TABLE;
    xQueueSend(btm_cmdQueue,&amp;msg,0);
}

void btm_setDataCmd(int num,int temperature,int gravity,int txPower)
{
    if(btm_cmdQueue == 0)
        return;

    btm_cmdMsg_t msg;
    msg.cmd =    BTM_CMD_SET_DATA;
    msg.num = num;
    msg.temperature = temperature;
    msg.gravity = gravity;
    msg.txPower = txPower;
    xQueueSend(btm_cmdQueue,&amp;msg,0);
}


void btm_updateDataCmd(int num,int rate ,int temperature,int gravity )
{
    if(btm_cmdQueue == 0)
        return;
    btm_cmdMsg_t msg;
    msg.cmd =    BTM_CMD_SET_UPDATE;
    msg.num = num;
    msg.temperature = temperature;
    msg.gravity = gravity;
    msg.txPower = rate;
    xQueueSend(btm_cmdQueue,&amp;msg,0);
}
</pre>
<p>&nbsp;</p>
<p><strong>main.c</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#include "cyhal.h"
#include "cybsp.h"
#include "cy_retarget_io.h"
#include &lt;stdio.h&gt;
#include "FreeRTOS.h"
#include "task.h"
#include "usrcmd.h"

#include "bluetoothManager.h"
#include "cycfg_bt_settings.h"
#include "bt_platform_cfg_settings.h"

volatile int uxTopUsedPriority ;
TaskHandle_t blinkTaskHandle;


void blink_task(void *arg)
{
    cyhal_gpio_init(CYBSP_USER_LED,CYHAL_GPIO_DIR_OUTPUT,CYHAL_GPIO_DRIVE_STRONG,0);

    for(;;)
    {
    	cyhal_gpio_toggle(CYBSP_USER_LED);
    	vTaskDelay(500);
    }
}


int main(void)
{
    uxTopUsedPriority = configMAX_PRIORITIES - 1 ; // enable OpenOCD Thread Debugging

    /* Initialize the device and board peripherals */
    cybsp_init() ;
    __enable_irq();

    cy_retarget_io_init(CYBSP_DEBUG_UART_TX, CYBSP_DEBUG_UART_RX, CY_RETARGET_IO_BAUDRATE);



    cybt_platform_config_init(&amp;bt_platform_cfg_settings);
    wiced_bt_stack_init (app_bt_management_callback, &amp;wiced_bt_cfg_settings);

    // Stack size in WORDs
    // Idle task = priority 0
    xTaskCreate(blink_task, "blinkTask", configMINIMAL_STACK_SIZE,0 /* args */ ,0 /* priority */, &amp;blinkTaskHandle);
    xTaskCreate(usrcmd_task, "usrcmd_task", configMINIMAL_STACK_SIZE*4,0 /* args */ ,0 /* priority */, 0);
    vTaskStartScheduler();
}

/* [] END OF FILE */
</pre>
<p>&nbsp;</p>
<p><strong>usrcmd.c</strong></p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">/**
 * @file usrcmd.c
 * @author CuBeatSystems
 * @author Shinichiro Nakamura
 * @copyright
 * ===============================================================
 * Natural Tiny Shell (NT-Shell) Version 0.3.1
 * ===============================================================
 * Copyright (c) 2010-2016 Shinichiro Nakamura
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */


#include "ntopt.h"
#include "ntlibc.h"
#include "ntshell.h"
#include &lt;stdio.h&gt;

#include "ntshell.h"
#include "ntlibc.h"
#include "psoc6_ntshell_port.h"

#include "FreeRTOS.h"
#include "task.h"
#include "bluetoothManager.h"

static ntshell_t ntshell;

typedef int (*USRCMDFUNC)(int argc, char **argv);

static int usrcmd_ntopt_callback(int argc, char **argv, void *extobj);
static int usrcmd_help(int argc, char **argv);
static int usrcmd_info(int argc, char **argv);
static int usrcmd_clear(int argc, char **argv);
static int usrcmd_pargs(int argc, char **argv);
#ifdef configUSE_TRACE_FACILITY
#if configUSE_STATS_FORMATTING_FUNCTIONS ==1
static int usrcmd_list(int argc, char **argv);
#endif
#endif
static int usrcmd_print(int argc, char **argv);
static int usrcmd_set(int argc, char **argv);
static int usrcmd_update(int argc, char **argv);

typedef struct {
    char *cmd;
    char *desc;
    USRCMDFUNC func;
} cmd_table_t;

static const cmd_table_t cmdlist[] = {
    { "help", "This is a description text string for help command.", usrcmd_help },
    { "info", "This is a description text string for info command.", usrcmd_info },
    { "clear", "Clear the screen", usrcmd_clear },
    { "pargs","print the list of arguments", usrcmd_pargs},
#ifdef configUSE_TRACE_FACILITY 
#if configUSE_STATS_FORMATTING_FUNCTIONS ==1
    { "tasks","print the list of RTOS Tasks", usrcmd_list},
#endif
#endif
    { "print", "Print table", usrcmd_print },
    { "set", "set num temperature gravity txpower", usrcmd_set },
    { "update", "update num temperature gravity", usrcmd_update },
};


void usrcmd_task()
{

  setvbuf(stdin, NULL, _IONBF, 0);
  printf("Started user command task with NT Shell\n");
  ntshell_init(
	       &amp;ntshell,
	       ntshell_read,
	       ntshell_write,
	       ntshell_callback,
	       (void *)&amp;ntshell);
  ntshell_set_prompt(&amp;ntshell, "AnyCloud&gt; ");
  vtsend_erase_display(&amp;ntshell.vtsend);
  ntshell_execute(&amp;ntshell);
}

int usrcmd_execute(const char *text)
{
    return ntopt_parse(text, usrcmd_ntopt_callback, 0);
}

static int usrcmd_ntopt_callback(int argc, char **argv, void *extobj)
{
    if (argc == 0) {
        return 0;
    }
    const cmd_table_t *p = &amp;cmdlist[0];
    for (unsigned int i = 0; i &lt; sizeof(cmdlist) / sizeof(cmdlist[0]); i++) {
        if (ntlibc_strcmp((const char *)argv[0], p-&gt;cmd) == 0) {
            return p-&gt;func(argc, argv);
        }
        p++;
    }
    printf("%s","Unknown command found.\n");
    return 0;
}

static int usrcmd_help(int argc, char **argv)
{
    const cmd_table_t *p = &amp;cmdlist[0];
    for (unsigned int i = 0; i &lt; sizeof(cmdlist) / sizeof(cmdlist[0]); i++) {
        printf("%s",p-&gt;cmd);
        printf("%s","\t:");
        printf("%s",p-&gt;desc);
        printf("%s","\n");
        p++;
    }
    return 0;
}


static int usrcmd_info(int argc, char **argv)
{
    if (argc != 2) {
        printf("%s","info sys\n");
        printf("%s","info ver\n");
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "sys") == 0) {
        printf("%s","PSoC 6 MBED Monitor\n");
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "ver") == 0) {
        printf("%s","Version 0.0.0\n");
        return 0;
    }
    printf("%s","Unknown sub command found\n");
    return -1;
}


static int usrcmd_clear(int argc, char **argv)
{
    vtsend_erase_display_home(&amp;ntshell.vtsend);
    return 0;
}

static int usrcmd_pargs(int argc, char **argv)
{
    printf("ARGC = %d\n",argc);

    for(int i =0;i&lt;argc;i++)
    {
        printf("argv[%d] = %s\n",i,argv[i]);
    }
    return 0;

}

#ifdef configUSE_TRACE_FACILITY
#if configUSE_STATS_FORMATTING_FUNCTIONS ==1
static int usrcmd_list(int argc,char **argv)
{
    // 40 bytes/task + some margin
    char buff[40*10 + 100];

    vTaskList( buff );
    printf("Name          State Priority   Stack  Num\n");
    printf("------------------------------------------\n");
    printf("%s",buff);

    printf("‘B’ – Blocked\n‘R’ – Ready\n‘D’ – Deleted (waiting clean up)\n‘S’ – Suspended, or Blocked without a timeout\n");
    printf("Stack = bytes free at highwater\n");
    return 0;
}
#endif
#endif


static int usrcmd_print(int argc, char **argv)
{
    btm_printTableCmd();
    return 0;
}

static int usrcmd_set(int argc, char **argv)
{
    int gravity;
    int temperature;
    int txPower;
    int num;
    if(argc == 5)
    {
        sscanf(argv[1],"%d",&amp;num);
        sscanf(argv[2],"%d",&amp;temperature);
        sscanf(argv[3],"%d",&amp;gravity);
        sscanf(argv[4],"%d",&amp;txPower);

        btm_setDataCmd(num,temperature,gravity,txPower);
    }
    return 0;
}

static int usrcmd_update(int argc, char **argv)
{
    int gravity;
    int temperature;
    int rate;
    int num;
    if(argc == 5)
    {
        sscanf(argv[1],"%d",&amp;num);
        sscanf(argv[2],"%d",&amp;rate);
        sscanf(argv[3],"%d",&amp;temperature);
        sscanf(argv[4],"%d",&amp;gravity);

        btm_updateDataCmd(num,rate,temperature,gravity);
    }
    return 0;
}
</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 4) Advertising Packet Error?</title>
		<link>https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 25 Jan 2021 12:58:51 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10308</guid>

					<description><![CDATA[Summary This article discusses a detailed Bluetooth Capture using Linux and a Frontline Bluetooth Scanner.  With those tools I discover the cause of my bug. Where is the Tilt? As I was trying to get the program running, I was faced with a problem that sent me down a serious serious rabbit hole.  The problem [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>This article discusses a detailed Bluetooth Capture using Linux and a Frontline Bluetooth Scanner.  With those tools I discover the cause of my bug.</p>
<p><span><p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p></span></p>
<h1>Where is the Tilt?</h1>
<p>As I was trying to get the program running, I was faced with a problem that sent me down a serious serious rabbit hole.  The problem was that I could see the Tilts on the iPhone App, but they were not printing out on the screen.  But why?</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/img_9c463f999f55-1/" rel="attachment wp-att-10310"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/IMG_9C463F999F55-1-473x1024.jpeg" alt="" width="473" height="1024" class="alignnone size-large wp-image-10310" srcset="https://iotexpert.com/wp-content/uploads/2020/11/IMG_9C463F999F55-1-473x1024.jpeg 473w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_9C463F999F55-1-139x300.jpeg 139w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_9C463F999F55-1-768x1662.jpeg 768w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_9C463F999F55-1-710x1536.jpeg 710w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_9C463F999F55-1-600x1299.jpeg 600w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_9C463F999F55-1.jpeg 828w" sizes="auto, (max-width: 473px) 100vw, 473px" /></a></p>
<p>I ran Light Blue, but there are so many Bluetooth Devices in range that I couldn&#8217;t see a way to sort it out.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/img_4cdf4c40f554-1/" rel="attachment wp-att-10311"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/IMG_4CDF4C40F554-1-473x1024.jpeg" alt="" width="473" height="1024" class="alignnone size-large wp-image-10311" srcset="https://iotexpert.com/wp-content/uploads/2020/11/IMG_4CDF4C40F554-1-473x1024.jpeg 473w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_4CDF4C40F554-1-139x300.jpeg 139w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_4CDF4C40F554-1-768x1662.jpeg 768w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_4CDF4C40F554-1-710x1536.jpeg 710w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_4CDF4C40F554-1-600x1299.jpeg 600w, https://iotexpert.com/wp-content/uploads/2020/11/IMG_4CDF4C40F554-1.jpeg 828w" sizes="auto, (max-width: 473px) 100vw, 473px" /></a></p>
<h1>What in the world is &#8220;hcidump&#8221;</h1>
<p>On Karl&#8217;s <a href="https://kvurd.com/blog/tilt-hydrometer-ibeacon-data-format/" target="_blank" rel="noopener noreferrer">blog</a> he writes that you should use hcitool and hcidump to view what is going on.  Well, I am mostly an embedded guys, but OK.</p>
<p>I first install Linux on a Raspberry Pi 3B+ which has a Cypress CYW43455 WiFi Bluetooth Combo device onboard.  In this configuration the Bluetooth framework is split into a controller running on the CYW43455 and a Host Stack running on the Raspberry Pi Linux chip BCM2837B0.  The Host and the Controller communicate with each other through a UART.  The protocol that is used on that UART is called &#8220;HCI&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/hci/" rel="attachment wp-att-10325"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/hci.png" alt="" width="756" height="292" class="alignnone size-full wp-image-10325" srcset="https://iotexpert.com/wp-content/uploads/2020/11/hci.png 756w, https://iotexpert.com/wp-content/uploads/2020/11/hci-300x116.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/hci-600x232.png 600w" sizes="auto, (max-width: 756px) 100vw, 756px" /></a></p>
<p>Specifically, what Karl told you to do is:</p>
<ol>
<li>In one window tell the Bluetooth Controller to start looking for advertising packets &#8220;lesscan&#8221;</li>
<li>In another windows, start snooping on the HCI bus and printing out the packets in hex</li>
</ol>
<p>In the picture below you can see that I first run &#8220;hcitool dev&#8221; to get a list of the Bluetooth Devices.  It turns on out the Raspberry Pi configuration that I have there is only one bluetooth device and it is called &#8220;hci0&#8221;.  Then I use &#8220;hcitool -i hci0 lescan&#8221; to send a command to the controller to start BLE scanning.  The hcitool command will report just the MAC address and possibly the name (if it happens to be in the advertising packet).  Its too bad that hcitool lescan doesn&#8217;t have a switch to display the advertising packets.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-23-at-4-24-01-pm/" rel="attachment wp-att-10322"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.24.01-PM.png" alt="" width="888" height="954" class="alignnone size-full wp-image-10322" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.24.01-PM.png 888w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.24.01-PM-279x300.png 279w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.24.01-PM-768x825.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.24.01-PM-600x645.png 600w" sizes="auto, (max-width: 888px) 100vw, 888px" /></a></p>
<p>In another window I run &#8220;sudo hcidump -R &gt; out.txt&#8221;.  The hcidump command will &#8220;snoop&#8221; on the HCI uart and will print out all of the raw packets going between the host and the controller.   After a while, I open on the out.txt and start looking through the raw data to find my Tilt.  I recognize the Tilt by looking for the UUID in the iBeacon which is &#8220;A4 95 BB &#8230;&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-23-at-4-20-30-pm/" rel="attachment wp-att-10321"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.20.30-PM.png" alt="" width="934" height="826" class="alignnone size-full wp-image-10321" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.20.30-PM.png 934w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.20.30-PM-300x265.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.20.30-PM-768x679.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-23-at-4.20.30-PM-600x531.png 600w" sizes="auto, (max-width: 934px) 100vw, 934px" /></a></p>
<p>But what is all of the other stuff on that line?  For that answer we will need to dig through the Bluetooth core spec.  If you look in Volume 4 (HCI), Part A (UART Transport Layer), Chapter 1 (General) it starts with a nice picture of the system.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-33-31-am/" rel="attachment wp-att-10327"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.33.31-AM-1024x342.png" alt="" width="1024" height="342" class="alignnone size-large wp-image-10327" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.33.31-AM-1024x342.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.33.31-AM-300x100.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.33.31-AM-768x257.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.33.31-AM-600x201.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.33.31-AM.png 1370w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Then in Chapter 2 (Protocol) it says:</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-35-02-am/" rel="attachment wp-att-10328"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.35.02-AM-1024x794.png" alt="" width="1024" height="794" class="alignnone size-large wp-image-10328" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.35.02-AM-1024x794.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.35.02-AM-300x233.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.35.02-AM-768x596.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.35.02-AM-600x465.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.35.02-AM.png 1426w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>So, first byte, the 0x04 means that this is an &#8220;HCI Event Packet&#8221;, meaning this is something that happened on the controller which is going back to the Host.  In fact if you look at the log above you will see that the lines are preceded by a &#8220;&gt;&#8221; which means packets coming into the Host.  Now we know that there is an event packet, so we follow further down into the spec in Part &#8220;E&#8221; which describes the event packet.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-40-15-am/" rel="attachment wp-att-10329"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.40.15-AM-1024x640.png" alt="" width="1024" height="640" class="alignnone size-large wp-image-10329" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.40.15-AM-1024x640.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.40.15-AM-300x187.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.40.15-AM-768x480.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.40.15-AM-600x375.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.40.15-AM.png 1386w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>The first byte, the &#8220;0x3E&#8221; is an event code and the 0x2A is the length of the packet (count from the 0x02 through to the end)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-46-39-am/" rel="attachment wp-att-10330"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.46.39-AM.png" alt="" width="884" height="90" class="alignnone size-full wp-image-10330" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.46.39-AM.png 884w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.46.39-AM-300x31.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.46.39-AM-768x78.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.46.39-AM-600x61.png 600w" sizes="auto, (max-width: 884px) 100vw, 884px" /></a></p>
<p>Keep digging further down into the spec to find out what the &#8220;event code 0x3E&#8221; and you will find that it is a &#8220;LE Meta event&#8221;.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-50-45-am/" rel="attachment wp-att-10332"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.50.45-AM-1024x329.png" alt="" width="1024" height="329" class="alignnone size-large wp-image-10332" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.50.45-AM-1024x329.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.50.45-AM-300x96.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.50.45-AM-768x247.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.50.45-AM-600x193.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.50.45-AM.png 1364w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>OK so that means that the &#8220;02&#8221; is the &#8220;Subevent_Code&#8221;.  OK keep digging and you will find that the 02 Subevven_Code is &#8220;HCI_LE_Advertising_Report&#8221;.  Then it gives you a list of what data will follow.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-53-12-am/" rel="attachment wp-att-10334"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.53.12-AM-973x1024.png" alt="" width="973" height="1024" class="alignnone size-large wp-image-10334" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.53.12-AM-973x1024.png 973w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.53.12-AM-285x300.png 285w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.53.12-AM-768x808.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.53.12-AM-600x631.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.53.12-AM.png 1372w" sizes="auto, (max-width: 973px) 100vw, 973px" /></a></p>
<p>So, the 02 is an advertising report.  The 01 says that there is only 1 advertising report.  Q: Why is this a parameter?  A: Because the controller can save a few bytes by clubbing multiple advertising reports into 1 HCI packet.  Which it did not do in this case.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-55-27-am/" rel="attachment wp-att-10335"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.55.27-AM.png" alt="" width="868" height="90" class="alignnone size-full wp-image-10335" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.55.27-AM.png 868w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.55.27-AM-300x31.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.55.27-AM-768x80.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.55.27-AM-600x62.png 600w" sizes="auto, (max-width: 868px) 100vw, 868px" /></a></p>
<p>Next is the 03 which is the &#8220;Event_Type[i]&#8221;.  In this case it says that this thing is non connectable undirected advertising. (more on this later)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-56-07-am/" rel="attachment wp-att-10336"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.56.07-AM-1024x319.png" alt="" width="1024" height="319" class="alignnone size-large wp-image-10336" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.56.07-AM-1024x319.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.56.07-AM-300x93.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.56.07-AM-768x239.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.56.07-AM-600x187.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.56.07-AM.png 1376w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>OK what about 01 &#8230;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-59-17-am/" rel="attachment wp-att-10337"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.17-AM.png" alt="" width="884" height="92" class="alignnone size-full wp-image-10337" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.17-AM.png 884w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.17-AM-300x31.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.17-AM-768x80.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.17-AM-600x62.png 600w" sizes="auto, (max-width: 884px) 100vw, 884px" /></a><br />
The 01 says that it is a random address and then the next 6 bytes is the MAC address.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-9-59-54-am/" rel="attachment wp-att-10338"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.54-AM-1024x385.png" alt="" width="1024" height="385" class="alignnone size-large wp-image-10338" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.54-AM-1024x385.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.54-AM-300x113.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.54-AM-768x289.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.54-AM-600x226.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-9.59.54-AM.png 1350w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Now the 0x1E is the length of the data in the advertising packet</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-02-29-am/" rel="attachment wp-att-10339"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.02.29-AM.png" alt="" width="874" height="96" class="alignnone size-full wp-image-10339" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.02.29-AM.png 874w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.02.29-AM-300x33.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.02.29-AM-768x84.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.02.29-AM-600x66.png 600w" sizes="auto, (max-width: 874px) 100vw, 874px" /></a></p>
<p>The actual advertising pack is next.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-13-36-am/" rel="attachment wp-att-10343"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.13.36-AM.png" alt="" width="880" height="94" class="alignnone size-full wp-image-10343" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.13.36-AM.png 880w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.13.36-AM-300x32.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.13.36-AM-768x82.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.13.36-AM-600x64.png 600w" sizes="auto, (max-width: 880px) 100vw, 880px" /></a></p>
<p>Recall the format from section 11.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-09-47-am/" rel="attachment wp-att-10342"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.09.47-AM-1024x982.png" alt="" width="1024" height="982" class="alignnone size-large wp-image-10342" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.09.47-AM-1024x982.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.09.47-AM-300x288.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.09.47-AM-768x737.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.09.47-AM-600x576.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.09.47-AM.png 1472w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>The 02 01 04 is the first field of the advertising packet and it represents</p>
<ul>
<li>02 &#8211; length of the field</li>
<li>01 &#8211; «Flags»</li>
<li>04 &#8211; flags</li>
</ul>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-03-38-am/" rel="attachment wp-att-10340"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.03.38-AM.png" alt="" width="880" height="94" class="alignnone size-full wp-image-10340" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.03.38-AM.png 880w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.03.38-AM-300x32.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.03.38-AM-768x82.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.03.38-AM-600x64.png 600w" sizes="auto, (max-width: 880px) 100vw, 880px" /></a></p>
<p>You can find the 01 from the &#8220;<a href="https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/" target="_blank" rel="noopener noreferrer">Assigned numbers and GAP</a>&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-05-06-am/" rel="attachment wp-att-10341"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.05.06-AM-1024x134.png" alt="" width="1024" height="134" class="alignnone size-large wp-image-10341" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.05.06-AM-1024x134.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.05.06-AM-300x39.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.05.06-AM-768x100.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.05.06-AM-1536x201.png 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.05.06-AM-2048x268.png 2048w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.05.06-AM-600x78.png 600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Then when you look in the Core Specification Supplement Part A section 1.3 you will find that the &#8220;4&#8221; means &#8220;BR/EDR not supported&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-15-28-am/" rel="attachment wp-att-10344"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.15.28-AM.png" alt="" width="994" height="680" class="alignnone size-full wp-image-10344" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.15.28-AM.png 994w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.15.28-AM-300x205.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.15.28-AM-768x525.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.15.28-AM-600x410.png 600w" sizes="auto, (max-width: 994px) 100vw, 994px" /></a></p>
<p>The next field is 0x1A in length and a field type of &#8220;0xFF&#8221; &#8211; Ah&#8230;. manufacture specific data</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-17-17-am/" rel="attachment wp-att-10345"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.17.17-AM.png" alt="" width="866" height="90" class="alignnone size-full wp-image-10345" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.17.17-AM.png 866w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.17.17-AM-300x31.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.17.17-AM-768x80.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.17.17-AM-600x62.png 600w" sizes="auto, (max-width: 866px) 100vw, 866px" /></a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-18-21-am/" rel="attachment wp-att-10346"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.18.21-AM.png" alt="" width="958" height="578" class="alignnone size-full wp-image-10346" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.18.21-AM.png 958w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.18.21-AM-300x181.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.18.21-AM-768x463.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.18.21-AM-600x362.png 600w" sizes="auto, (max-width: 958px) 100vw, 958px" /></a></p>
<p>The 4C 00 is Apples Company Identifier Code.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-19-21-am/" rel="attachment wp-att-10347"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.19.21-AM.png" alt="" width="874" height="90" class="alignnone size-full wp-image-10347" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.19.21-AM.png 874w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.19.21-AM-300x31.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.19.21-AM-768x79.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.19.21-AM-600x62.png 600w" sizes="auto, (max-width: 874px) 100vw, 874px" /></a></p>
<p>Which you can find on the <a href="https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/" target="_blank" rel="noopener noreferrer">Bluetooth Sig Company Identifiers page</a>.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-21-02-am/" rel="attachment wp-att-10348"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.21.02-AM.png" alt="" width="1006" height="102" class="alignnone size-full wp-image-10348" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.21.02-AM.png 1006w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.21.02-AM-300x30.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.21.02-AM-768x78.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.21.02-AM-600x61.png 600w" sizes="auto, (max-width: 1006px) 100vw, 1006px" /></a></p>
<p>From the previous article we know the rest is an iBeacon</p>
<ul>
<li>02 &#8211; iBeacon subtype</li>
<li>0x15 &#8211; length</li>
<li>Then the A4&#8230;. is UUID which maps to a &#8220;Pink&#8221; Tilt</li>
</ul>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-23-19-am/" rel="attachment wp-att-10349"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.23.19-AM.png" alt="" width="888" height="92" class="alignnone size-full wp-image-10349" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.23.19-AM.png 888w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.23.19-AM-300x31.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.23.19-AM-768x80.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.23.19-AM-600x62.png 600w" sizes="auto, (max-width: 888px) 100vw, 888px" /></a></p>
<p>Then we get the Temperature 0x004F (BIG ENDIAN!!!!) also known as 79 Degrees F (see Imperial&#8230; my people)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-25-43-am/" rel="attachment wp-att-10350"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.25.43-AM.png" alt="" width="882" height="94" class="alignnone size-full wp-image-10350" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.25.43-AM.png 882w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.25.43-AM-300x32.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.25.43-AM-768x82.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.25.43-AM-600x64.png 600w" sizes="auto, (max-width: 882px) 100vw, 882px" /></a></p>
<p>Then the gravity 0x03FD also known as 1.021 &#8211; NOTICE THIS IS BIG ENDIAN!!!!</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-27-34-am/" rel="attachment wp-att-10351"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.27.34-AM.png" alt="" width="870" height="88" class="alignnone size-full wp-image-10351" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.27.34-AM.png 870w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.27.34-AM-300x30.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.27.34-AM-768x78.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.27.34-AM-600x61.png 600w" sizes="auto, (max-width: 870px) 100vw, 870px" /></a></p>
<p>Then transmit power C5 (also known as -59)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-29-23-am/" rel="attachment wp-att-10352"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.23-AM.png" alt="" width="874" height="88" class="alignnone size-full wp-image-10352" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.23-AM.png 874w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.23-AM-300x30.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.23-AM-768x77.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.23-AM-600x60.png 600w" sizes="auto, (max-width: 874px) 100vw, 874px" /></a></p>
<p>Finally the RSSI 0xA5 which is also known as -91 (2&#8217;s complement 1 byte)</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-10-29-58-am/" rel="attachment wp-att-10353"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.58-AM.png" alt="" width="870" height="90" class="alignnone size-full wp-image-10353" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.58-AM.png 870w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.58-AM-300x31.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.58-AM-768x79.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-10.29.58-AM-600x62.png 600w" sizes="auto, (max-width: 870px) 100vw, 870px" /></a></p>
<h1>Debugging the Linux Bluetooth</h1>
<p>I had a few problems getting this going which led me to the door of some interesting questions (which I won&#8217;t answer here).  But, here are the problems and solutions.</p>
<p><strong>Q1</strong> When I &#8220;hcidump -R&#8221; I only get this:</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-12-01-46-pm/" rel="attachment wp-att-10356"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.01.46-PM.png" alt="" width="850" height="180" class="alignnone size-full wp-image-10356" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.01.46-PM.png 850w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.01.46-PM-300x64.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.01.46-PM-768x163.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.01.46-PM-600x127.png 600w" sizes="auto, (max-width: 850px) 100vw, 850px" /></a></p>
<p><strong>A1</strong> You need to be root to see the raw data.  Run &#8220;sudo hcidump -R&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-12-03-04-pm/" rel="attachment wp-att-10357"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.03.04-PM-950x1024.png" alt="" width="950" height="1024" class="alignnone size-large wp-image-10357" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.03.04-PM-950x1024.png 950w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.03.04-PM-278x300.png 278w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.03.04-PM-768x828.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.03.04-PM-600x647.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-12.03.04-PM.png 1078w" sizes="auto, (max-width: 950px) 100vw, 950px" /></a></p>
<p>Q2: I don&#8217;t see my device?</p>
<p>A2: If you start the scan before your run the hcidump you might miss the device.  When you scan try running &#8220;hcitool lescan &#8211;duplicates&#8221; which will turn off duplicate filtering.</p>
<p>Q3: I get &#8220;Set scan parameters failed: Input/output error&#8221;</p>
<p>A3: I am not totally sure I have this right because I was not able to recreate the problem (which I had in spades when I started).  But, try doing a &#8220;sudo hciconfig hci0 down&#8221; followed by a &#8220;hciconfig hci0 up&#8221;</p>
<p>Q4: I read that &#8220;hcitool&#8221; and &#8220;hcidump&#8221; are deprecated.</p>
<p>A4: I read the same thing.  If you don&#8217;t have them try &#8220;sudo apt-get install bluez-tools&#8221;.  I would like to know the &#8220;real&#8221; answer to this question</p>
<p>Q5: How does hcidump get the packets from the HCI?</p>
<p>A5: I have no idea.  But I would like to know that answer to this question</p>
<p>Q6: I tried &#8220;btmon&#8221;.  Does that work?</p>
<p>A6: Yes</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/screen-shot-2020-11-24-at-1-29-18-pm/" rel="attachment wp-att-10360"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-1.29.18-PM-1024x367.png" alt="" width="1024" height="367" class="alignnone size-large wp-image-10360" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-1.29.18-PM-1024x367.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-1.29.18-PM-300x107.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-1.29.18-PM-768x275.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-1.29.18-PM-600x215.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-24-at-1.29.18-PM.png 1184w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Q7: What is the bluetoothd?</p>
<p>A7: I dont know.</p>
<p>Q8: Do hcidump and hcitools talk to the BluetoothD or do they talk to dbus or do they talk to the kernel through a socket?</p>
<p>A8: I don&#8217;t know</p>
<p>Q9: Why can&#8217;t I see the Tilt in my advertising scanner</p>
<p>A9: Keep reading <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<h1>Frontline Bluetooth Scanner</h1>
<p>I can see the Tilt in the Linux and on the iPhone,  but I still can&#8217;t see it in my AnyCloud project.  Why?  The next thing that I did was get a Frontline Bluetooth Sniffer.  When I started capturing packets I could see the device.  Here is the &#8220;Tilt&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/capture/" rel="attachment wp-att-10319"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/capture-1024x577.png" alt="" width="1024" height="577" class="alignnone size-large wp-image-10319" srcset="https://iotexpert.com/wp-content/uploads/2020/11/capture-1024x577.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/capture-300x169.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/capture-768x433.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/capture-1536x865.png 1536w, https://iotexpert.com/wp-content/uploads/2020/11/capture-600x338.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/capture.png 1924w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>And here is the &#8220;Tilt Repeater&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/repeater/" rel="attachment wp-att-10320"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/repeater-1024x577.jpg" alt="" width="1024" height="577" class="alignnone size-large wp-image-10320" srcset="https://iotexpert.com/wp-content/uploads/2020/11/repeater-1024x577.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/11/repeater-300x169.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/11/repeater-768x433.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/11/repeater-1536x865.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/11/repeater-600x338.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/11/repeater.jpg 1924w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Fix the Advertising Scanner</h1>
<p>After more digging I figured it out.  Remember from earlier that the Tilt advertises as &#8220;Non connectable&#8221;.  Well it turns out that when I built the advertising scanner I used the function &#8220;wiced_bt_ble_scan&#8221;.  This function was put into WICED to use for the connection procedure.  In other words it FILTERS devices that are non connectable.  In order to see those devices you need to call &#8220;wiced_bt_ble_observe&#8221;.  Wow that was a long article to explain that one little bug.  Oh well it was a fun learning experience.</p>
<h1></h1>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 3) Advertising Scanner</title>
		<link>https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/#comments</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 18 Jan 2021 16:59:42 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=10279</guid>

					<description><![CDATA[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. Build the Basic Project Start by making a new project using the CY8CKIT-062S2-43012.  I chose this kit because [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>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.</p>
<p><span><p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p></span></p>
<h1>Build the Basic Project</h1>
<p>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.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-07-54-am/" rel="attachment wp-att-10280"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.07.54-AM-1024x845.jpg" alt="" width="1024" height="845" class="alignnone size-large wp-image-10280" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.07.54-AM-1024x845.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.07.54-AM-300x248.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.07.54-AM-768x634.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.07.54-AM-1536x1267.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.07.54-AM-600x495.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.07.54-AM.jpg 1852w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>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 &#8220;git tags&#8221;</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-08-37-am/" rel="attachment wp-att-10281"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.08.37-AM-1024x845.jpg" alt="" width="1024" height="845" class="alignnone size-large wp-image-10281" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.08.37-AM-1024x845.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.08.37-AM-300x248.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.08.37-AM-768x634.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.08.37-AM-1536x1268.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.08.37-AM-600x495.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.08.37-AM.jpg 1844w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>After the project is made run &#8220;make modlibs&#8221; so that you can add some libraries.  Specifically the bluetooth-freertos and btstack libraries.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-11-42-am/" rel="attachment wp-att-10282"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.11.42-AM-1024x784.jpg" alt="" width="1024" height="784" class="alignnone size-large wp-image-10282" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.11.42-AM-1024x784.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.11.42-AM-300x230.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.11.42-AM-768x588.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.11.42-AM-1536x1177.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.11.42-AM-2048x1569.jpg 2048w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.11.42-AM-600x460.jpg 600w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Now that I have all of the libraries I need, run &#8220;make vscode&#8221; to build the project files for vscode.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-12-46-am/" rel="attachment wp-att-10283"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.12.46-AM-1024x890.jpg" alt="" width="1024" height="890" class="alignnone size-large wp-image-10283" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.12.46-AM-1024x890.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.12.46-AM-300x261.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.12.46-AM-768x667.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.12.46-AM-1536x1335.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.12.46-AM-600x521.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.12.46-AM.jpg 1956w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>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.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-15-39-am/" rel="attachment wp-att-10284"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.15.39-AM-1024x227.png" alt="" width="1024" height="227" class="alignnone size-large wp-image-10284" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.15.39-AM-1024x227.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.15.39-AM-300x67.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.15.39-AM-768x170.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.15.39-AM-600x133.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.15.39-AM.png 1272w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>You need to make two changes to the Makefile.  Well you dont have to change the name of the App &#8230; but I typically do.  You do need to add the FREERTOS and WICED_BLE components to your project.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">APPNAME=Tilt2


....


COMPONENTS=FREERTOS WICED_BLE</pre>
<p>Now, edit main.c to include some stuff.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#include "bluetoothManager.h"
#include "cycfg_bt_settings.h"
#include "bt_platform_cfg_settings.h"</pre>
<p>And start the bluetooth stack.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">    cybt_platform_config_init(&amp;bt_platform_cfg_settings);
    wiced_bt_stack_init (app_bt_management_callback, &amp;wiced_bt_cfg_settings);</pre>
<p>You need to get the bluetooth configuration files.  To do that run the Bluetooth Configurator by running &#8220;make config_bt&#8221;.  Click the paper to make a new configuration.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-19-43-am/" rel="attachment wp-att-10286"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.43-AM-1024x755.png" alt="" width="1024" height="755" class="alignnone size-large wp-image-10286" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.43-AM-1024x755.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.43-AM-300x221.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.43-AM-768x566.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.43-AM-600x442.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.43-AM.png 1270w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Pick out the PSoC6 MCU with 43xxxx Connectivity.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-19-58-am/" rel="attachment wp-att-10287"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.58-AM.png" alt="" width="894" height="322" class="alignnone size-large wp-image-10287" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.58-AM.png 894w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.58-AM-300x108.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.58-AM-768x277.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.19.58-AM-600x216.png 600w" sizes="auto, (max-width: 894px) 100vw, 894px" /></a> <a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-20-19-am/" rel="attachment wp-att-10288"></a></p>
<p>Go to the GAP Settings tab and add the Observer configuration</p>
<p><a href="https://iotexpert.com/?attachment_id=10289" rel="attachment wp-att-10289"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.20.25-AM.png" alt="" width="516" height="330" class="alignnone size-large wp-image-10289" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.20.25-AM.png 516w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.20.25-AM-300x192.png 300w" sizes="auto, (max-width: 516px) 100vw, 516px" /></a></p>
<p>Then set the low duty scan parameters to 60ms and turn off the timeout.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-22-32-am/" rel="attachment wp-att-10290"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.32-AM-1024x665.png" alt="" width="1024" height="665" class="alignnone size-large wp-image-10290" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.32-AM-1024x665.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.32-AM-300x195.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.32-AM-768x499.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.32-AM-1536x997.png 1536w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.32-AM-600x390.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.32-AM.png 1722w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Save your configuration.  Notice that I call it tilt2.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-22-51-am/" rel="attachment wp-att-10291"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.51-AM.png" alt="" width="902" height="436" class="alignnone size-large wp-image-10291" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.51-AM.png 902w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.51-AM-300x145.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.51-AM-768x371.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.22.51-AM-600x290.png 600w" sizes="auto, (max-width: 902px) 100vw, 902px" /></a></p>
<p>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.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-19-at-6-35-45-am/" rel="attachment wp-att-10285"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.35.45-AM-1024x376.png" alt="" width="1024" height="376" class="alignnone size-large wp-image-10285" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.35.45-AM-1024x376.png 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.35.45-AM-300x110.png 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.35.45-AM-768x282.png 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.35.45-AM-600x220.png 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-19-at-6.35.45-AM.png 1248w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>iBeacon Packet Format</h1>
<p>On the Tilt Hydrometer <a href="https://tilthydrometer.com/pages/faqs" target="_blank" rel="noopener noreferrer">FAQ</a> they give you a link to Karl Urdevics <a href="https://kvurd.com/blog/tilt-hydrometer-ibeacon-data-format/" target="_blank" rel="noopener noreferrer">blog</a> where he describes the way that the Tilt works.  Pretty simple, each Tilt Hydrometer is hardcoded to a color.  Each &#8220;color&#8221; sends out an <a href="https://en.wikipedia.org/wiki/IBeacon" target="_blank" rel="noopener noreferrer">iBeacon</a> 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 <a href="https://iotexpert.com/anycloud-bluetooth-advertising-scanner-part-3/" target="_blank" rel="noopener noreferrer">here</a>.  But, in summary, a BLE device can advertise up to 31 bytes of data.  That data is divided into fields of the following form:</p>
<ul>
<li>Field Type (1 byte) &#8211; The type is one of the <a href="https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/" target="_blank" rel="noopener noreferrer">Bluetooth GAP Assigned Numbers</a></li>
<li>Length (1 byte)</li>
<li>Length -1 bytes</li>
</ul>
<p>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 &#8220;<span>«Manufacturer Specific Data»&#8221; which has the field code 0xFF.  The Manufacturer Specific Data field type is divided into</span></p>
<ul>
<li>16-bit Manufacturers ID</li>
<li>Data</li>
</ul>
<p>For the iBeacon the <span>Manufacturer</span> ID is 0X004C &#8211; which is Apple&#8217;s ID.  They further subdivided the data.  Here is the complete format of that data.</p>
<div class="table-responsive"><table  style="width:95%; "  class="easy-table easy-table-default " border="1">
<thead>
<tr><th >Bytes</th>
<th >Data</th>
<th >Name</th>
<th >Comment</th>
</tr>
</thead>
<tbody>
<tr><td >1</td>
<td >0xFF</td>
<td >Manufacturer Data</td>
<td >Bluetooth GAP Assigned Number</td>
</tr>

<tr><td >1</td>
<td >0x1A</td>
<td >Length of Field</td>
</tr>

<tr><td >2</td>
<td >0&#215;04 0x0C</td>
<td ><span>Manufacturers'</span> UUID</td>
<td >Apples Bluetooth <span>Manufacturer </span>ID</td>
</tr>

<tr><td >1</td>
<td >02</td>
<td >Apple defined</td>
<td > 0x02=Subtype iBeacon</td>
</tr>

<tr><td >1</td>
<td >0&#215;15</td>
<td >Length</td>
<td >Apple defined length</td>
</tr>

<tr><td >16</td>
<td >????</td>
<td >UUID</td>
<td >The universally unique identifier for the data type of the iBeacon (these are defined by Tilt)</td>
</tr>

<tr><td >2</td>
<td >????</td>
<td >Major Value</td>
<td >Gravity in 1/1000 of SG</td>
</tr>

<tr><td >2</td>
<td >????</td>
<td >Minor Value</td>
<td >Temperature in degrees F (yup, Imperial!)</td>
</tr>

<tr><td >1</td>
<td >??</td>
<td >Signal Power</td>
<td >dBm Transmit Power as measured at 1m</td>
</tr>
</tbody></table></div>
<p>It turns out that Tilt &#8220;hardcodes&#8221; the UUID during manufacturing for the color of the Tilt (remember each Tilt is one of 8 colors)</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">Red:    A495BB10C5B14B44B5121370F02D74DE
Green:  A495BB20C5B14B44B5121370F02D74DE
Black:  A495BB30C5B14B44B5121370F02D74DE
Purple: A495BB40C5B14B44B5121370F02D74DE
Orange: A495BB50C5B14B44B5121370F02D74DE
Blue:   A495BB60C5B14B44B5121370F02D74DE
Yellow: A495BB70C5B14B44B5121370F02D74DE
Pink:   A495BB80C5B14B44B5121370F02D74DE</pre>
<h1>Add Advertising Observer</h1>
<p>Now that we have a working project template, and we know what we are looking for in the BLE advertising land, I&#8217;ll setup some data structures to map of the colors and UUIDs.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">#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))</pre>
<p>Then I will create and advertising callback that will</p>
<ul>
<li>Look for the manufacturer specific data field in the advertising packet</li>
<li>If the length of that field is 25 then we have found a possible iBeacon</li>
<li>Loop through the different possible Tilt&#8217;s</li>
<li>Compare the data in the advertising packet against the data + UUID in the packet</li>
<li>If it matches then decode the Gravity, txPower and Temperature and print it</li>
</ul>
<pre class="EnlighterJSRAW" data-enlighter-language="c">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,&amp;mfgFieldLen);
    
	if(mfgFieldData &amp;&amp; mfgFieldLen == TILT_IBEACON_HEADER_LEN + TILT_IBEACON_DATA_LEN)
    {
        for(int i=0;i&lt;NUM_TILT;i++)
        {
            if(memcmp(mfgFieldData,tiltDB[i].uuid,TILT_IBEACON_HEADER_LEN)==0)
            {
                float gravity = ((float)((uint16_t)mfgFieldData[22] &lt;&lt; 8 | (uint16_t)mfgFieldData[23]))/1000;
		        int temperature = mfgFieldData[20] &lt;&lt; 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;
            }
        }
    }
}</pre>
<p>In the Bluetooth Management callback I want to turn on scanning (actually observing).</p>
<pre class="EnlighterJSRAW" data-enlighter-language="c">        case BTM_ENABLED_EVT:
            if (WICED_BT_SUCCESS == p_event_data-&gt;enabled.status)
            {
				wiced_bt_ble_observe(WICED_TRUE, 0,btm_advScanResultCback);
            }
            else
            {
            	printf("Error enabling BTM_ENABLED_EVENT\n");
            }</pre>
<p>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 <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> .  It is also interesting to see that they broadcast 5 packets at 5dBm, then 5 at -59dBm.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/screen-shot-2020-11-20-at-10-30-23-am/" rel="attachment wp-att-10296"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM-1024x1021.jpg" alt="" width="1024" height="1021" class="alignnone size-large wp-image-10296" srcset="https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM-1024x1021.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM-300x300.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM-150x150.jpg 150w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM-768x765.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM-500x500.jpg 500w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM-600x598.jpg 600w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM-100x100.jpg 100w, https://iotexpert.com/wp-content/uploads/2020/11/Screen-Shot-2020-11-20-at-10.30.23-AM.jpg 1196w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Now that we have data coming out, in the next article Ill address a couple of funky things that I noticed.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 2) Architecture</title>
		<link>https://iotexpert.com/tilt-hydrometer-architecture-part-2/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-architecture-part-2/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 11 Jan 2021 19:05:52 +0000</pubDate>
				<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=9778</guid>

					<description><![CDATA[Summary This article is a walk through of the architecture of the PSoC 6 &#8211; AnyCloud firmware which I will implement for my Tilt Hydrometer IoT application Story I started this whole thing by just writing some code without really thinking about the architecture too much.   I had been wanting to try the Bluetooth [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>This article is a walk through of the architecture of the PSoC 6 &#8211; AnyCloud firmware which I will implement for my Tilt Hydrometer IoT application</p>
<p><span><p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p></span></p>
<h1>Story</h1>
<p>I started this whole thing by just writing some code without really thinking about the architecture too much.   I had been wanting to try the Bluetooth SDK inside of AnyCloud and I was just focused on how that worked inside of the PSoC 6.  As I wrote the code, I naturally implemented with a common design pattern of</p>
<ol>
<li>Individual tasks have responsibility for a specific hardware block</li>
<li>Each task has a command queue to which other tasks can send commands</li>
<li>Tasks that need to have data fed to them by other tasks have a data queue</li>
</ol>
<p>As I worked on the code and things started to get a bit more involved I decided that I had better draw a picture of the system.  This picture served two main purposes.</p>
<ol>
<li>It kept me on track</li>
<li>I knew that I was going to need it for this series of articles</li>
</ol>
<p>Here is the architecture of what I have implemented for this series of articles.  As of this writing I have not started started not the wifi or the file system part of the implementation but I think that my design will work.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/arch1/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/arch1-1024x514.png" alt="" class="alignnone wp-image-10692 size-large" width="1024" height="514" srcset="https://iotexpert.com/wp-content/uploads/2020/09/arch1-1024x514.png 1024w, https://iotexpert.com/wp-content/uploads/2020/09/arch1-300x151.png 300w, https://iotexpert.com/wp-content/uploads/2020/09/arch1-768x385.png 768w, https://iotexpert.com/wp-content/uploads/2020/09/arch1-1536x771.png 1536w, https://iotexpert.com/wp-content/uploads/2020/09/arch1.png 1614w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>The system has 7 tasks:</p>
<div class="table-responsive"><table  style="width:95%; "  class="easy-table easy-table-default " border="1">
<thead>
<tr><th >Task</th>
<th >Role</th>
</tr>
</thead>
<tbody>
<tr><td >Bluetooth Manager</td>
<td >Listens for advertising packets in the correct format.  When it finds them it submits the data to the Tilt Data Manager</td>
</tr>

<tr><td >Tilt Data Manager</td>
<td >A database of all of the "Tilt" data</td>
</tr>

<tr><td >NTShell</td>
<td >A serial command line to control the system</td>
</tr>

<tr><td >Display Manager</td>
<td >A task to handle all of the ST7789V display functions</td>
</tr>

<tr><td >CapSense Manager</td>
<td >Responsibility for reading capsense buttons and the slider and sending commands to the display task</td>
</tr>

<tr><td >File System Manager</td>
<td >Responsibility for writing data to the SD Card and possibly the SPI flash</td>
</tr>

<tr><td >WiFi Manager</td>
<td >An MQTT interface to the internet (maybe)</td>
</tr>
</tbody></table></div>
<p>There are two places in the picture above where my lines have to cross, something that I really hate.  So I decided to try a different picture to explain the system.  The picture below describes the tasks, what data they own, what hardware they own, what the inputs are and what the outputs are.</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/arch2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/arch2-1024x526.png" alt="" class="alignnone wp-image-10693 size-large" width="1024" height="526" srcset="https://iotexpert.com/wp-content/uploads/2020/09/arch2-1024x526.png 1024w, https://iotexpert.com/wp-content/uploads/2020/09/arch2-300x154.png 300w, https://iotexpert.com/wp-content/uploads/2020/09/arch2-768x395.png 768w, https://iotexpert.com/wp-content/uploads/2020/09/arch2-1536x789.png 1536w, https://iotexpert.com/wp-content/uploads/2020/09/arch2.png 1864w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>In the next article Ill start the basic project and create an advertising observer.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-architecture-part-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tilt Hydrometer (Part 1) Overview &#038; Out-of-Box</title>
		<link>https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/</link>
					<comments>https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/#respond</comments>
		
		<dc:creator><![CDATA[Alan Hawse]]></dc:creator>
		<pubDate>Mon, 04 Jan 2021 12:31:11 +0000</pubDate>
				<category><![CDATA[4343W]]></category>
		<category><![CDATA[CY8CKIT-062S2-43012]]></category>
		<category><![CDATA[PSoC 6]]></category>
		<category><![CDATA[Tilt Hydrometer Data Collection]]></category>
		<guid isPermaLink="false">https://iotexpert.com/?p=9747</guid>

					<description><![CDATA[Summary A discussion of a new series of articles about using the PSoC 6 + 43XXXX Wifi/Bluetooth combo chips to implement a data collection system for the Tilt2 Hydrometer.  Even if you aren&#8217;t specifically interested in hydrometers, this is a general purpose discussion of the design of an IoT device. Story In the middle of [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>Summary</h1>
<p>A discussion of a new series of articles about using the PSoC 6 + 43XXXX Wifi/Bluetooth combo chips to implement a data collection system for the <a href="https://tilthydrometer.com/products/copy-of-tilt-floating-wireless-hydrometer-and-thermometer-for-brewing" target="_blank" rel="noopener noreferrer">Tilt2 Hydrometer</a>.  Even if you aren&#8217;t specifically interested in hydrometers, this is a general purpose discussion of the design of an IoT device.</p>
<h1>Story</h1>
<p>In the middle of the Covid lockdown my 21-year-old daughter suggested that we start brewing beer.  This was always something that I have been interested in so I said &#8220;Sure!&#8221;.  What does this have to do with IoT you might ask?  I am an engineer and I love data.  Two of the key pieces of <a href="https://en.wikipedia.org/wiki/Beer_measurement" target="_blank" rel="noopener noreferrer">data</a> that you are interested in while fermenting beer are:</p>
<ul>
<li>The gravity of the beer</li>
<li>The temperature of the beer</li>
</ul>
<p>If you don&#8217;t know anything about brewing beer, it is simple (people have been doing it a long time&#8230; even with no IoT)</p>
<ol>
<li>Start with grain</li>
<li>Mill the grain</li>
<li>Heat the grain with water to turn it into sugar water (called wort)</li>
<li>Add yeast</li>
<li>Wait while the yeast converts the sugar in the wort into alcohol and carbon dioxide</li>
<li>Bottle the beer (or keg)</li>
<li>Drink</li>
</ol>
<p>Back to the metrics.  The &#8220;specific gravity&#8221; or just &#8220;gravity&#8221; is just the ratio of the density of your solution to plain water.  This is an indication of sugar in the wort solution.  At the start of the fermentation you will have &#8220;lots&#8221; of sugar, and no alcohol.  By the end you will have &#8220;lots&#8221; of alcohol and not much sugar.  You can tell how things are going by monitoring the gravity of the beer, which is a proxy metric for how much sugar has been converted to alcohol.</p>
<p>There are two common ways to measure the gravity:</p>
<ul>
<li>A <a href="https://en.wikipedia.org/wiki/Hydrometer" target="_blank" rel="noopener noreferrer">float hydrometer</a> &#8211; sugar water is denser then water, so a &#8220;float&#8221; will float lower in the solution as the sugar gets converted to alcohol.</li>
<li>A <a href="https://en.wikipedia.org/wiki/Refractometer" target="_blank" rel="noopener noreferrer">refractometer</a> &#8211; the index of refraction of the solution changes as the sugar concentration changes (this is an amazing old-school technology</li>
</ul>
<p>As I was learning about this whole process I found the <a href="https://tilthydrometer.com" target="_blank" rel="noopener noreferrer">tilt hydrometer</a>.  This device has</p>
<ul>
<li>A Bluetooth MCU (<a href="https://www.u-blox.com/sites/default/files/BMD-300_DataSheet_%28UBX-19033350%29.pdf" target="_blank" rel="noopener noreferrer">u-blox BMD-300</a>)</li>
<li>An I2C accelerometer (<a href="https://www.nxp.com/docs/en/data-sheet/MMA8451Q.pdf" target="_blank" rel="noopener noreferrer">NXP MMA8451Q</a>)</li>
<li>An I2C temperature sensor (<a href="https://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf" target="_blank" rel="noopener noreferrer">Microchip MCP9808</a>)</li>
<li>Enclosed in a sealed plastic tube of known volume and weight (known density)</li>
</ul>
<p>As the gravity of the beer changes, the device floats at a different angle (because it floats lower/higher).  They use the accelerometer to measure the apparent angle of gravity to calculate the angle of the device.  This angle is then used to calculate the density of the solution it is floating in.  They then broadcast the calculated gravity and temperature in Apple Bluetooth iBeacon format.</p>
<p>When I saw this, I thought &#8220;perfect&#8221; I know what to do with that.  I should build a device that can collect all of the data, display it, save it to an SPI flash and put it into the cloud.  It should look something like this: (each Tilt is identified by 1 of 8 colors&#8230; pink in this case).</p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0026/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0026-1024x711.jpg" alt="" class="alignnone wp-image-10642 size-large" width="1024" height="711" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0026-1024x711.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0026-300x208.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0026-768x533.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0026-1536x1066.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0026-2048x1422.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Yes, I know they have an iPhone app, but I want to build a single device that sits in my brewery all of the time.  And yes I know they have a Raspberry Pi app, but that isn&#8217;t the point.</p>
<p>My device will have the following characteristics:</p>
<p>A Display with:</p>
<ul>
<li>A Splash Screen</li>
<li>A Table of all Tilts, Gravity and Temperature</li>
<li>Single: One screen per tilt with the specific data including debugging</li>
<li>Single: A graph of the active data for one specific tilt</li>
<li>Single: A table of all of the recordings from that specific tilt</li>
<li>The WiFi Status</li>
<li>The Bluetooth Status</li>
</ul>
<p>Bluetooth System that can:</p>
<ul>
<li>Record tilt data as broadcast in iBeacon advertising packets</li>
<li>Repeat tilt data (maybe)</li>
<li>Introducer WiFi (probably)</li>
</ul>
<p>CapSense button GUI to:</p>
<ul>
<li>Next Screen</li>
<li>Auto Mode</li>
<li>Reset current</li>
<li>Dump recorded data to the SD Card</li>
</ul>
<p>Command Line</p>
<ul>
<li>A UART based command line to debug &amp; learn</li>
</ul>
<p>USB</p>
<ul>
<li>Mass Storage to see files</li>
<li>USB &lt;-&gt; UART Bridge</li>
</ul>
<p>Power Supply via USB Port</p>
<ul>
<li>Plug in Type-C using Cypress BCR</li>
</ul>
<p>WiFi</p>
<ul>
<li>MQTT Publish to AWS</li>
<li>NTP &#8211; to find the time</li>
<li>Local webserver</li>
<li>MDNS</li>
</ul>
<p>RTC</p>
<ul>
<li>Keep Track of current Time</li>
</ul>
<p>SPI NOR Flash</p>
<ul>
<li>Record the data</li>
</ul>
<p>SD CARD</p>
<ul>
<li>Dump the fixed SPI Flash  recordings to a removable SD CARD &amp; remove data from the SPI Flash</li>
</ul>
<p>Here is another picture of what I am thinking (well actually what I implemented for this series of articles)<br />
<a href="https://iotexpert.com/" rel="attachment wp-att-9748"></a><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0025-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0025-1024x738.jpg" alt="" class="alignnone wp-image-10645 size-large" width="1024" height="738" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0025-1024x738.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0025-300x216.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0025-768x553.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0025-1536x1107.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0025-2048x1476.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Un-boxing</h1>
<p>To get this show on the road, I ordered three tilts and two repeaters from Baron Brew Equipment.<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0038-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0038-781x1024.jpg" alt="" class="alignnone wp-image-10656 size-large" width="781" height="1024" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0038-781x1024.jpg 781w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0038-229x300.jpg 229w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0038-768x1007.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0038-1172x1536.jpg 1172w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0038-1562x2048.jpg 1562w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0038-scaled.jpg 1953w" sizes="auto, (max-width: 781px) 100vw, 781px" /></a></p>
<p>It included a neat little quick start picture showing how to get going.<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0037-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0037-778x1024.jpg" alt="" class="alignnone wp-image-10655 size-large" width="778" height="1024" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0037-778x1024.jpg 778w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0037-228x300.jpg 228w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0037-768x1010.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0037-1168x1536.jpg 1168w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0037-1557x2048.jpg 1557w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0037-scaled.jpg 1946w" sizes="auto, (max-width: 778px) 100vw, 778px" /></a></p>
<p>Then the box of goodies.<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0036/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0036-792x1024.jpg" alt="" class="alignnone wp-image-10654 size-large" width="792" height="1024" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0036-792x1024.jpg 792w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0036-232x300.jpg 232w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0036-768x993.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0036-1188x1536.jpg 1188w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0036-1584x2048.jpg 1584w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0036-scaled.jpg 1980w" sizes="auto, (max-width: 792px) 100vw, 792px" /></a></p>
<p>There are 8-possible tilts, Red, Green, Orange, Blue, Black, Yellow, Purpose and Pink (each Tilt his &#8220;hardcoded&#8221; to identify itself as a specific color)<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0028-scaled-e1598972738342/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0028-scaled-e1598972738342-1024x513.jpg" alt="" class="alignnone wp-image-10646 size-large" width="1024" height="513" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0028-scaled-e1598972738342-1024x513.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0028-scaled-e1598972738342-300x150.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0028-scaled-e1598972738342-768x385.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0028-scaled-e1598972738342-1536x769.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0028-scaled-e1598972738342-2048x1026.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Tilt Hydrometer</h1>
<p>Here is a picture of the &#8220;blue&#8221; one (notice I put the wrong box in the picture)<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0039-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0039-1024x585.jpg" alt="" class="alignnone wp-image-10657 size-large" width="1024" height="585" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0039-1024x585.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0039-300x171.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0039-768x439.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0039-1536x877.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0039-2048x1170.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>The tilt comes in a plastic tube.  Which has a label to remind you to take it out of the tube (my experience is that you should be embarrassed to have to read most warning labels <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /> )<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0033-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0033-903x1024.jpg" alt="" class="alignnone wp-image-10652 size-large" width="903" height="1024" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0033-903x1024.jpg 903w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0033-264x300.jpg 264w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0033-768x871.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0033-1354x1536.jpg 1354w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0033-1805x2048.jpg 1805w" sizes="auto, (max-width: 903px) 100vw, 903px" /></a></p>
<p>It is about 100mm long (about 4 inches).  The bluetooth module is at the top, U3 is the temperature sensor and U2 (which is under the black 3-d printed plastic) is the accelerometer.<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0034-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0034-1024x619.jpg" alt="" class="alignnone wp-image-10653 size-large" width="1024" height="619" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0034-1024x619.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0034-300x181.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0034-768x464.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0034-1536x928.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0034-2048x1238.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>Repeater</h1>
<p>If you put a Bluetooth device floating in a bunch of beer, surrounded by a metal fermentation container, you will not be able to hear the Bluetooth signal.  To solve this problem the Tilt people made a <a href="https://tilthydrometer.com/products/tilt-repeater" target="_blank" rel="noopener noreferrer">repeater</a> which can rest on the top of the fermenter.  It listens for the weak signal, then rebroadcasts with a higher gain antenna.</p>
<p>Here is a picture of the repeater.  Notice that it uses the BMD-301 which has an external SMA antenna.<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0045-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0045-1024x710.jpg" alt="" class="alignnone wp-image-10658 size-large" width="1024" height="710" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0045-1024x710.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0045-300x208.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0045-768x532.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0045-1536x1065.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0045-2048x1420.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>It also comes in a nice plastic tube.<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0029-3/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0029-1024x524.jpg" alt="" class="alignnone wp-image-10648 size-large" width="1024" height="524" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0029-1024x524.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0029-300x154.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0029-768x393.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0029-1536x787.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0029-2048x1049.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>The repeater can only re-broadcast one color at a time.  The button to switches between the 8 colors and off.<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0032/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0032-1024x740.jpg" alt="" class="alignnone wp-image-10651 size-large" width="1024" height="740" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0032-1024x740.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0032-300x217.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0032-768x555.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0032-1536x1110.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0032-2048x1479.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Each time you press the button the 3-color LED lights up with the color that represents which tilt color that it is repeating. Red-&gt;Green-&gt;&#8230; Pink-&gt;Off<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0030-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0030-1024x340.jpg" alt="" class="alignnone wp-image-10649 size-large" width="1024" height="340" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0030-1024x340.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0030-300x100.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0030-768x255.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0030-1536x510.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0030-2048x680.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>It also has a huge rechargeable battery.<br />
<a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/img_0031-2/"><img loading="lazy" decoding="async" src="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0031-1024x327.jpg" alt="" class="alignnone wp-image-10650 size-large" width="1024" height="327" srcset="https://iotexpert.com/wp-content/uploads/2020/09/IMG_0031-1024x327.jpg 1024w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0031-300x96.jpg 300w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0031-768x246.jpg 768w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0031-1536x491.jpg 1536w, https://iotexpert.com/wp-content/uploads/2020/09/IMG_0031-2048x655.jpg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h1>The Plan</h1>
<p>Here is a list of the articles that I plan to write</p>
<p>This series is broken up into the following 12 articles with a few additional possible articles. </p>
<p><a href="https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/" target="_blank" rel="noopener">Tilt Hydrometer (Part 1) Overview &amp; Out-of-Box</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-architecture-part-2/" target="_blank" rel="noopener">Tilt Hydrometer (Part 2) Architecture</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-advertising-scanner-part-3/">Tilt Hydrometer (Part 3) Advertising Scanner</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-4-advertising-packet-error/" target="_blank" rel="noopener">Tilt Hydrometer (Part 4) Advertising Packet Error?</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-5-tilt-simulator-multi-advertising-ibeacons/">Tilt Hydrometer (Part 5) Tilt Simulator &amp; Multi Advertising iBeacons</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-6-tilt-simulator-an-upgrade/" target="_blank" rel="noopener">Tilt Hydrometer (Part 6) Tilt Simulator an Upgrade</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-7-advertising-database/">Tilt Hydrometer (Part 7) Advertising Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-8-read-the-database/">Tilt Hydrometer (Part 8) Read the Database</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-9-an-lcd-display/">Tilt Hydrometer (Part 9) An LCD Display</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-10-the-display-state-machine/">Tilt Hydrometer (Part 10) The Display State Machine</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-11-draw-the-display-screens/">Tilt Hydrometer (Part 11) Draw the Display Screens</a></p>
<p><a href="https://iotexpert.com/tilt-hydrometer-part-12-capsense/">Tilt Hydrometer (Part 12) CapSense</a></p>
<p>Tilt Hydrometer: LittleFS &amp; SPI Flash (Part ?)</p>
<p>Tilt Hydrometer: WiFi Introducer (Part ?)</p>
<p>Tilt Hydrometer: WebServer (Part ?)</p>
<p>Tilt Hydrometer: Amazon MQTT (Part ?)</p>
<p>Tilt Hydrometer: Printed Circuit Board (Part ?)</p>
<p>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.</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://iotexpert.com/tilt-hydrometer-overview-out-of-box-part-1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
