<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jared Wolff</title>
    <description>The latest articles on DEV Community by Jared Wolff (@jaredwolff).</description>
    <link>https://dev.to/jaredwolff</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F101363%2F3e4ad1ea-6fd8-4b0f-94fd-cc97acddf353.jpg</url>
      <title>DEV Community: Jared Wolff</title>
      <link>https://dev.to/jaredwolff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jaredwolff"/>
    <language>en</language>
    <item>
      <title>Create an Open Source Powered Air Quality Dashboard</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Mon, 11 Oct 2021 14:05:54 +0000</pubDate>
      <link>https://dev.to/jaredwolff/create-an-open-source-powered-air-quality-dashboard-p3p</link>
      <guid>https://dev.to/jaredwolff/create-an-open-source-powered-air-quality-dashboard-p3p</guid>
      <description>&lt;p&gt;&lt;strong&gt;This post was originally from &lt;a href="https://www.jaredwolff.com/open-source-air-quality/"&gt;jaredwolff.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Depending on where you live, your air quality can be a &lt;em&gt;big deal.&lt;/em&gt; Whether it's pollution from factories or particulates from burning forest fires, they're not good for your lungs or overall health. &lt;/p&gt;

&lt;p&gt;While there are plenty of air quality meters out there, it's doubtful they're open source never-mind reasonably priced. In this post, i'll be showing you how to connect an Air Quality Wing, publish data to the web via Golioth and view your data in real time using Grafana. &lt;/p&gt;

&lt;p&gt;So first, let's get started by assembling hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; this post is fairly lengthy. You can download a PDF version of it &lt;a href="https://sendfox.com/lp/mndzvv"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Assembling hardware
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OvINU1_p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/IMG_5543.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OvINU1_p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/IMG_5543.jpg" alt="All the circuit boards used"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this project, you'll need a few things to get started. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.jaredwolff.com/store/air-quality-wing/"&gt;An Air Quality Wing&lt;/a&gt; with connected PM2.5 sensor&lt;/li&gt;
&lt;li&gt;A Feather compatible processor board. We'll be using the &lt;a href="https://www.jaredwolff.com/store/nrf9160-feather/"&gt;nRF9160 Feather&lt;/a&gt; which is NB IoT and LTE Cat-M enabled.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://www.adafruit.com/product/2890"&gt;Feather doubler&lt;/a&gt; to connect the two together.&lt;/li&gt;
&lt;li&gt;A SIM card with your favorite provider. Some of the options out there include Hologram, Soracom and Twilio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Assembly is straight forward and only requires you to solder headers if you haven't already. Here's an example of using female headers on the Air Quality Wing. &lt;/p&gt;

&lt;p&gt;Every Air Quality Wing comes with a set of male headers. One is 12 pins and the other is 16.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J49HmnnH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/store/air-quality-wing/images/air-quality-wing-headers.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J49HmnnH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/store/air-quality-wing/images/air-quality-wing-headers.jpeg" alt="Air Quality Wing with male headers"&gt;&lt;/a&gt;&lt;br&gt;
The best way to solder them is size them correctly, place them in a breadboard or already assembled doubler and place the board on top for soldering. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vt0VnXAH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/IMG_5549.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vt0VnXAH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/IMG_5549.jpg" alt="Soldering the Air Quality Wing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Don't spend too much time holding your iron to the pins as the breadboard is not meant for high heat usage!&lt;/p&gt;

&lt;p&gt;You will also have to do a similar solder job if you use a Feather doubler. More on the assembly for that board can be &lt;a href="https://learn.adafruit.com/featherwing-proto-and-doubler/assembly"&gt;found here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the end you should have a setup that looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lu_Sca4z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/IMG_5556.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lu_Sca4z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/IMG_5556.jpg" alt="Air Quality Wing and nRF9160 Feather assembled and inserted into Featherwing Doubler"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up a device
&lt;/h2&gt;

&lt;p&gt;Currently the Air Quality Wing works with the following boards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nRF9160 Feather&lt;/li&gt;
&lt;li&gt;Particle Xenon&lt;/li&gt;
&lt;li&gt;nRF52840 DK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Adding support for new boards is simple thanks to Zephyrs ability to target many hardware targets for the same core source code. In this sample we'll be using the nRF9160 Feather since it can publish directly to Grafana. So here's the instructions to getting the firmware up and running:&lt;/p&gt;
&lt;h3&gt;
  
  
  Set Up Python Virtual Environment
&lt;/h3&gt;

&lt;p&gt;One of the main dependencies for Zephyr is Python. Instead of installing the required packages directly to your system, I highly recommend use a virtual environment instead. &lt;/p&gt;

&lt;p&gt;First lets make a project folder. We can call it &lt;code&gt;aqw&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; aqw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't have &lt;code&gt;virtualenv&lt;/code&gt; installed already, make sure you install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;virtualenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set up a python virtual environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;virtualenv &lt;span class="nt"&gt;-p&lt;/span&gt; python3 &lt;span class="nb"&gt;env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then enable it by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source env&lt;/span&gt;/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install &lt;code&gt;west&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;west&lt;/code&gt; is the command line utility for Zephyr. You'll need it to build the firmware.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;west
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initialize and update project
&lt;/h3&gt;

&lt;p&gt;Next we'll download the source code and install the dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;west init &lt;span class="nt"&gt;-m&lt;/span&gt; https://github.com/circuitdojo/air-quality-wing-zephyr-demo.git  &lt;span class="nt"&gt;--manifest-rev&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;west update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will download all the dependencies. If there are ever fixes to this sample you'll need to pull the changes and then re-run &lt;code&gt;west update&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; west update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install remaining python dependencies
&lt;/h3&gt;

&lt;p&gt;Finally,  there are some important dependencies needed to build your freshly cloned Zephyr repository. Let's install those here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; zephyr/scripts/requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install toolchain
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For &lt;strong&gt;Mac&lt;/strong&gt; run the following: (it does require you install &lt;code&gt;wget&lt;/code&gt;. &lt;code&gt;brew&lt;/code&gt; is an easy way to do so: &lt;code&gt;brew install wget&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~
 wget &lt;span class="s2"&gt;"https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-mac.tar.bz2"&lt;/span&gt;
 &lt;span class="nb"&gt;tar &lt;/span&gt;xvfj gcc-arm-none-eabi-9-2019-q4-major-mac.tar.bz2
 &lt;span class="nb"&gt;rm &lt;/span&gt;gcc-arm-none-eabi-9-2019-q4-major-mac.tar.bz2
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; for Catalina (and newer) users you will get an error when running these utilities for the first time. You must allow them to be executed in your Security preferences.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w6fM_Uqa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/cannot-be-opened.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w6fM_Uqa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/cannot-be-opened.jpeg" alt="Error running ARM Toolchain"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For &lt;strong&gt;Windows&lt;/strong&gt; you can download and install the toolchain with &lt;a href="https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads/9-2019-q4-major"&gt;this direct link.&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it for your SDK install! Next, we'll want to generate credentials for your device and add them to the sample code. We'll do that next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get credentials
&lt;/h3&gt;

&lt;p&gt;First jump over to Golioth and &lt;a href="https://golioth.io/#community"&gt;create an account&lt;/a&gt; if you haven't already. Golioth is finally in open beta which means anyone can join!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tq-JRn54--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-join.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tq-JRn54--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-join.png" alt="Golioth Landing Page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once instead you'll be prompted to create a project. So let's do that now. I've named my project Air Quality Wing but you can name it whatever you want. Only unique project names can be used at the moment so you'll need to name yours differently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S3-kRbdo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-create-project.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S3-kRbdo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-create-project.png" alt="Create a Project inside Golioth"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure you click &lt;strong&gt;Save&lt;/strong&gt; to continue to the next step.&lt;/p&gt;

&lt;p&gt;Once you have a project created, it will open up other options within Golioth including the &lt;strong&gt;Devices&lt;/strong&gt; tab. Click on the &lt;strong&gt;Devices&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5_Uc2-Mo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-devices-side-menu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5_Uc2-Mo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-devices-side-menu.png" alt="Devices entry in left side menu"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click the &lt;strong&gt;Create&lt;/strong&gt; drop down and then &lt;strong&gt;Provision with Credentials&lt;/strong&gt;. You you should get a screen like the one below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8d33Zykr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-device-fast-path-provision.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8d33Zykr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-device-fast-path-provision.png" alt="Device fast path Provision"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Change the device name to whatever you'd like. Change the identity to match if you'd like. (or leave it be) Finally, add your super secret pre-shared key. You'll need the &lt;strong&gt;Identity&lt;/strong&gt; and &lt;strong&gt;PSK&lt;/strong&gt; for the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add credentials to sample code
&lt;/h3&gt;

&lt;p&gt;If you've gotten this far, you have your device provisioned! Next is to add the &lt;strong&gt;Identity&lt;/strong&gt; and &lt;strong&gt;PSK&lt;/strong&gt; to the demo code.&lt;/p&gt;

&lt;p&gt;You will need to edit &lt;code&gt;demo/golioth/config/golioth.conf&lt;/code&gt; with your credentials in order to connect to Golioth's backend. Update &lt;code&gt;CONFIG_GOLIOTH_SYSTEM_CLIENT_PSK_ID&lt;/code&gt; and &lt;code&gt;CONFIG_GOLIOTH_SYSTEM_CLIENT_PSK&lt;/code&gt; with the credentials you used in the previous step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;# Golioth credentials
&lt;/span&gt;&lt;span class="n"&gt;CONFIG_GOLIOTH_SYSTEM_CLIENT_PSK_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;INSERT CLIENT ID HERE&amp;gt;"&lt;/span&gt;
&lt;span class="n"&gt;CONFIG_GOLIOTH_SYSTEM_CLIENT_PSK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;INSERT YOUR SUPER SECRET HERE&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, if you're not using the PM2.5 sensor you may need to comment out &lt;code&gt;&amp;amp;hpma_sensor,&lt;/code&gt; in &lt;code&gt;static struct aqw_sensor *sensors[] = {&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build and Deploy
&lt;/h3&gt;

&lt;p&gt;Finally build the application by running the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;demo
west build &lt;span class="nt"&gt;-b&lt;/span&gt; circuitdojo_feather_nrf9160_ns &lt;span class="nt"&gt;-s&lt;/span&gt; golioth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once built you'll get to the end of a bunch of output. The end should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;5/16] Linking C executable zephyr/zephyr_prebuilt.elf

&lt;span class="o"&gt;[&lt;/span&gt;11/16] Linking C executable zephyr/zephyr.elf
Memory region         Used Size  Region Size  %age Used
           FLASH:      189380 B     425472 B     44.51%
            SRAM:       44376 B     178968 B     24.80%
        IDT_LIST:          0 GB         2 KB      0.00%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then plug in your nRF9160 Feather and either program using &lt;code&gt;newtmgr&lt;/code&gt; or &lt;code&gt;nrfjprog&lt;/code&gt;. I'll include both here. &lt;/p&gt;

&lt;p&gt;For programming directly over USB use &lt;code&gt;newtmgr&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newtmgr &lt;span class="nt"&gt;-c&lt;/span&gt; serial image upload build/zephyr/app_update.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have a programmer setup, use &lt;code&gt;nrfjprog&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nrfjprog &lt;span class="nt"&gt;--program&lt;/span&gt; build/zephyr/merged.hex &lt;span class="nt"&gt;--chiperase&lt;/span&gt; &lt;span class="nt"&gt;--reset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open up a serial console and make sure the board has booted properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPM: NS image at 0x20200
SPM: NS MSP at 0x2001d2e8
SPM: NS reset vector at 0x264b5
SPM: prepare to jump to Non-Secure image.
*** Booting Zephyr OS build v2.6.99-ncs1  ***
[00:00:00.204,681] &amp;lt;err&amp;gt; i2c_nrfx_twim: Error 0x0BAE0002 occurred for message 0
[00:00:00.218,902] &amp;lt;inf&amp;gt; golioth_system: Initializing
[00:00:00.219,268] &amp;lt;inf&amp;gt; aqw_golioth_demo: Connecting..
[00:00:01.979,187] &amp;lt;inf&amp;gt; aqw_golioth_demo: LTE cell changed: Cell ID: 13368336, Tracking area: 1547
[00:00:02.044,860] &amp;lt;inf&amp;gt; aqw_golioth_demo: RRC mode: Connected
[00:00:03.416,809] &amp;lt;inf&amp;gt; aqw_golioth_demo: RRC mode: Idle
[00:00:13.547,454] &amp;lt;inf&amp;gt; aqw_golioth_demo: RRC mode: Connected
[00:00:16.709,899] &amp;lt;inf&amp;gt; aqw_golioth_demo: Network registration status: Connected - roaming
[00:00:16.709,930] &amp;lt;inf&amp;gt; aqw_golioth_demo: Air Quality Wing Golioth Demo
[00:00:16.710,235] &amp;lt;inf&amp;gt; aqw_golioth_demo: PSM parameter update: TAU: -1, Active time: -1
[00:00:18.313,415] &amp;lt;inf&amp;gt; golioth_system: Starting connect
[00:00:18.682,647] &amp;lt;inf&amp;gt; golioth_system: Client connected!
[00:00:20.848,358] &amp;lt;inf&amp;gt; aqw_golioth_demo: Temperature: 26.32333°C
[00:00:20.848,388] &amp;lt;inf&amp;gt; aqw_golioth_demo: Humidity: 60.771179%
[00:00:20.848,419] &amp;lt;inf&amp;gt; aqw_golioth_demo: PM2.5: 4.0 ppm
[00:00:20.848,541] &amp;lt;inf&amp;gt; aqw_golioth_demo: Data size: 42
[00:00:27.561,889] &amp;lt;inf&amp;gt; aqw_golioth_demo: RRC mode: Idle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've gotten some sensor measurements within the first 30 seconds, you're golden!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Grafana
&lt;/h2&gt;

&lt;p&gt;You're almost there! Let's get a Grafana interface set up. If you already have Grafana this should be a very straight forward integration. If you don't I recommend you check out Grafana's &lt;a href="https://grafana.com/products/cloud/"&gt;hosted service&lt;/a&gt;. I've also written about setting up Grafana in a &lt;a href="https://www.jaredwolff.com/how-to-make-an-amazing-looking-iot-dashboard-in-no-time/"&gt;past post.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  API keys for Golioth
&lt;/h3&gt;

&lt;p&gt;In order to get the streamed data from the nRF9160 Feather and Air Quality Wing, you'll need to set up API credentials for your Golioth Account.&lt;/p&gt;

&lt;p&gt;Navigate to the API Keys section and click &lt;strong&gt;Create a Key!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yw0jPgnA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-create-api-key.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yw0jPgnA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-create-api-key.png" alt="Create an API key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure you have &lt;strong&gt;API Key&lt;/strong&gt; selected and click &lt;strong&gt;Save&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M3NhrVRe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-api-key-choice.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M3NhrVRe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-api-key-choice.png" alt="Choose API key instead of JWT Token"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally you'll get a window like the one below. Take note of your &lt;strong&gt;X-API-Key&lt;/strong&gt; value and the project name. We'll need both in a second!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NvYlMy2H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-managing-api-key.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NvYlMy2H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/golioth-managing-api-key.png" alt="API Key Created. Inside Managing API Key response"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up your data source
&lt;/h3&gt;

&lt;p&gt;Now let's get over to Grafana.&lt;/p&gt;

&lt;p&gt;First we'll want to create a new &lt;strong&gt;Data Source&lt;/strong&gt; by going to the gear icon on the left navigation bar and clicking &lt;strong&gt;Data Sources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fd1X65YH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-configuration-data-sources-option.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fd1X65YH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-configuration-data-sources-option.png" alt="Create a new datasauce by going to Data Sources"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen click &lt;strong&gt;Add data source&lt;/strong&gt; and use the &lt;strong&gt;JSON API&lt;/strong&gt; option. If you do not see the &lt;strong&gt;JSON API&lt;/strong&gt; option you may need to install it first. Instructions on how &lt;a href="https://grafana.com/grafana/plugins/marcusolsson-json-datasource/?tab=installation"&gt;are here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u1UpHXXW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-json-api-select.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u1UpHXXW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-json-api-select.png" alt="Create a JSON API data source"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next you'll want to change the &lt;strong&gt;Name&lt;/strong&gt; to &lt;strong&gt;Golioth&lt;/strong&gt; and also your &lt;strong&gt;URL&lt;/strong&gt; to &lt;code&gt;https://api.golioth.io/v1/projects/{your-project-id}&lt;/code&gt; where your &lt;code&gt;{your-project-id}&lt;/code&gt; is the same one indicated earlier in the API key generation step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; do not include the &lt;code&gt;/devices&lt;/code&gt; suffix. Insert the URL above as shown with your project identifier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DVUZU8Pb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-data-sources-create-json-api.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DVUZU8Pb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-data-sources-create-json-api.png" alt="Set the API URL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally go to the bottom and setup a &lt;strong&gt;Header&lt;/strong&gt; under the &lt;strong&gt;Custom HTTP Headers&lt;/strong&gt; section. Set the name to &lt;strong&gt;X-API-Key&lt;/strong&gt; and the value to the API key generated earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4Eob0trm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-set-hustom-http-headers-data-source.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4Eob0trm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-set-hustom-http-headers-data-source.png" alt="Set the X-API-Key value"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click &lt;strong&gt;Save &amp;amp; test&lt;/strong&gt;. You should get a nice big &lt;strong&gt;Success&lt;/strong&gt; check mark if things check out!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vC2s3v6n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-data-source-test-success.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vC2s3v6n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-data-source-test-success.png" alt="Success after testing API connection"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating your dashboard
&lt;/h3&gt;

&lt;p&gt;In this step, we'll import a dashboard that i've already created. You can download it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jaredwolff.com/open-source-air-quality/files/Air%20Quality%20Wing-1633657750477.json"&gt;Dashboard JSON Export&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click the &lt;strong&gt;+&lt;/strong&gt; button on the left side menu and click &lt;strong&gt;Import&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VX2stQf7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-create-import-option.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VX2stQf7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-create-import-option.png" alt="Create new dashboard by importing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click the &lt;strong&gt;Upload JSON file&lt;/strong&gt; button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--o_dIOV4x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-upoad-json-file-import.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--o_dIOV4x--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-upoad-json-file-import.png" alt="Upload from JSON file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then select the &lt;strong&gt;Golioth&lt;/strong&gt; data source that we set up earlier and click &lt;strong&gt;Import&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m1bIjPXC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-dashboard-import-select-golioth-data-soruce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m1bIjPXC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-dashboard-import-select-golioth-data-soruce.png" alt="Select Data Source during Import"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The graphs should automatically pull data for your device and it should show some pretty graphs!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1OdIViuk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-final-dashboard.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1OdIViuk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/grafana-final-dashboard.png" alt="Data fetched and working!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You've done it!&lt;/p&gt;

&lt;p&gt;Now you can start playing around with the panels and tweak as you need to. More on creating the panel from scratch you can look at &lt;a href="https://github.com/golioth/samples/tree/main/grafana-stream-api"&gt;Golioth's setup documentation here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And that's a wrap!&lt;/p&gt;

&lt;p&gt;I hope this has been a useful introduction if this is your first time playing with Zephyr. Additionally, now that we have services like Golioth, I'm excited to see more people use it across different hardware platforms and projects. &lt;/p&gt;

&lt;p&gt;I'm also excited to announce that the latest version of the nRF9160 Feather are available. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CJyLqCc9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/nrf9160-feather-labled.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CJyLqCc9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/nrf9160-feather-labled.png" alt="Labeled picture of nRF9160 Feather v4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the biggest changes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Revamped power supply&lt;/li&gt;
&lt;li&gt;  USB connector to USB-C&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The latest Air Quality Wing, is also finally available!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---CalIIjR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/air-quality-wing-headers.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---CalIIjR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/open-source-air-quality/images/air-quality-wing-headers.jpeg" alt="Air Quality Wing v6"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the big changes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updating the onboard sensors to Sensirion parts&lt;/li&gt;
&lt;li&gt;Improved thermal management to prevent contaminating temperature readings&lt;/li&gt;
&lt;li&gt;Reduced size to fit Featherwing profile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can get some right now by &lt;a href="https://www.jaredwolff.com/store/"&gt;heading over to my store. 🏬&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks for reading. If you have questions or run into problems the &lt;a href="https://community.jaredwolff.com"&gt;Circuit Dojo community&lt;/a&gt; is open and ready to help. &lt;/p&gt;

</description>
      <category>howto</category>
      <category>zephyr</category>
      <category>opensource</category>
      <category>golioth</category>
    </item>
    <item>
      <title>How to Connect the nRF9160 Feather to Mosquitto</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Mon, 03 Aug 2020 01:59:03 +0000</pubDate>
      <link>https://dev.to/jaredwolff/how-to-connect-the-nrf9160-feather-to-mosquitto-4j18</link>
      <guid>https://dev.to/jaredwolff/how-to-connect-the-nrf9160-feather-to-mosquitto-4j18</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U5zaP24w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/moeu77aizwv11uzp9xem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U5zaP24w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/moeu77aizwv11uzp9xem.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One thing that's always tripped me up as an IoT developer is figuring the best way to transmit data. There are many different kinds of radios and mediums. On top of that, there are different protocols to boot!&lt;/p&gt;

&lt;p&gt;As of this writing, there is one protocol that has reigned supreme in the IoT world:&lt;/p&gt;

&lt;p&gt;MQTT&lt;/p&gt;

&lt;p&gt;Unlike an HTTP server, a device can connect, publish, and subscribe to topics. These topics then get sent to a broker and distributed to other subscribed devices. It also happens that MQTT on Nordic's nRF9160 is well supported.&lt;/p&gt;

&lt;p&gt;In this post, I'll show you how to connect the nRF9160 Feather to a self-hosted &lt;a href="https://github.com/eclipse/mosquitto"&gt;Mosquitto&lt;/a&gt; instance. You'll learn how to generate your own certificates, and get a hang on how to test your connections.&lt;/p&gt;

&lt;p&gt;Ready to play? Let's get to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to host?
&lt;/h2&gt;

&lt;p&gt;If you want to host Mosquitto, you'll need a server. Since Mosquitto is written in C it's lightweight and can go almost anywhere. Plus it sips resources so you can install it on a budget VPS without much worry. That's where a VPS provider like Digital Ocean or Vultr comes in.&lt;/p&gt;

&lt;p&gt;To set up a new server here are some steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login to Digital Ocean. If you don’t have Digital Ocean and would like to support click &lt;a href="https://m.do.co/c/9574d3846a29"&gt;here&lt;/a&gt; to create an account. ($100 credit for 60 days if you do!)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new Droplet&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JRqE0-y5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.21_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JRqE0-y5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.21_PM.png" alt="Create a new droplet"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Choose the FreeBSD 12.1 with UFS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X5W07CvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.29_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X5W07CvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.29_PM.png" alt="Create a FreeBSD 12.1 Droplet with UFS"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Choose the $5 instance. That's usually more than enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C5YbNf8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.42_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C5YbNf8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.42_PM.png" alt="Select tier of Droplet"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure you import your public key. Otherwise, you won't be able to immediately use password-less login.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QC35nRJl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.50_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QC35nRJl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.50_PM.png" alt="Choose authentication method"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hit that green &lt;strong&gt;Create Droplet&lt;/strong&gt; button, and let's get this show on the road.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tlxvuG79--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.54_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tlxvuG79--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.54_PM.png" alt="Create Droplet button"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Important Extra step
&lt;/h3&gt;

&lt;p&gt;For the certs to work with Mosquitto, you'll have to set a domain to point to your VPS IP address. A CNAME or A record works. If you're unsure how to do that, &lt;a href="https://misago.gitbook.io/docs/setup/domain"&gt;here's a good guide.&lt;/a&gt; Note which (sub)domain you've used. We'll need it in a bit...&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Mosquitto
&lt;/h2&gt;

&lt;p&gt;I run my servers inside of a FreeBSD jail using &lt;a href="https://bastillebsd.org"&gt;Bastille&lt;/a&gt;. In this tutorial, we'll be skipping the jail part and focus on getting the nRF9160 Feather working.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You should be set with a Digital Ocean instance (or similar) using FreeBSD. If you don’t head back up to the &lt;strong&gt;Where to host?&lt;/strong&gt; section.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next to install &lt;code&gt;mosquitto&lt;/code&gt; one your droplet run &lt;code&gt;pkg install mosquitto&lt;/code&gt;. If you're running something other than FreeBSD, this command may differ.&lt;br&gt;
&lt;code&gt;apt-get install mosquitto&lt;/code&gt; works on Debian based systems. If you want the most up to date repositories, make sure you run &lt;code&gt;sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa&lt;/code&gt; beforehand.&lt;br&gt;
Here's what the full output on FreeBSD looks like:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;mosquitto
The package management tool is not yet installed on your system.
Do you want to fetch and &lt;span class="nb"&gt;install &lt;/span&gt;it now? &lt;span class="o"&gt;[&lt;/span&gt;y/N]: y
Bootstrapping pkg from pkg+http://pkg.FreeBSD.org/FreeBSD:12:amd64/quarterly, please wait...
Verifying signature with trusted certificate pkg.freebsd.org.2013102301... &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] Installing pkg-1.14.6...
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] Extracting pkg-1.14.6: 100%
Updating FreeBSD repository catalogue...
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] Fetching meta.conf: 100%    163 B   0.2kB/s    00:01
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] Fetching packagesite.txz: 100%    6 MiB   6.6MB/s    00:01
Processing entries: 100%
FreeBSD repository update completed. 31943 packages processed.
All repositories are up to date.
Updating database digests format: 100%
The following 4 package&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; will be affected &lt;span class="o"&gt;(&lt;/span&gt;of 0 checked&lt;span class="o"&gt;)&lt;/span&gt;:

New packages to be INSTALLED:
        c-ares: 1.16.1
        ca_root_nss: 3.55
        e2fsprogs-libuuid: 1.45.6
        mosquitto: 1.6.7

Number of packages to be installed: 4

The process will require 2 MiB more space.
682 KiB to be downloaded.

Proceed with this action? &lt;span class="o"&gt;[&lt;/span&gt;y/N]: y
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;1/4] Fetching mosquitto-1.6.7.txz: 100%  226 KiB 231.1kB/s    00:01
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;2/4] Fetching ca_root_nss-3.55.txz: 100%  285 KiB 291.5kB/s    00:01
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;3/4] Fetching e2fsprogs-libuuid-1.45.6.txz: 100%   34 KiB  34.7kB/s    00:01
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;4/4] Fetching c-ares-1.16.1.txz: 100%  138 KiB 140.9kB/s    00:01
Checking integrity... &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0 conflicting&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;1/4] Installing ca_root_nss-3.55...
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;1/4] Extracting ca_root_nss-3.55: 100%
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;2/4] Installing e2fsprogs-libuuid-1.45.6...
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;2/4] Extracting e2fsprogs-libuuid-1.45.6: 100%
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;3/4] Installing c-ares-1.16.1...
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;3/4] Extracting c-ares-1.16.1: 100%
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;4/4] Installing mosquitto-1.6.7...
&lt;span class="o"&gt;===&amp;gt;&lt;/span&gt; Creating &lt;span class="nb"&gt;users
&lt;/span&gt;Using existing user &lt;span class="s1"&gt;'nobody'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;4/4] Extracting mosquitto-1.6.7: 100%
&lt;span class="o"&gt;=====&lt;/span&gt;
Message from ca_root_nss-3.55:

&lt;span class="nt"&gt;--&lt;/span&gt;
FreeBSD does not, and can not warrant that the certification authorities
whose certificates are included &lt;span class="k"&gt;in &lt;/span&gt;this package have &lt;span class="k"&gt;in &lt;/span&gt;any way been
audited &lt;span class="k"&gt;for &lt;/span&gt;trustworthiness or RFC 3647 compliance.

Assessment and verification of trust is the &lt;span class="nb"&gt;complete &lt;/span&gt;responsibility of the
system administrator.

This package installs symlinks to support root certificates discovery by
default &lt;span class="k"&gt;for &lt;/span&gt;software that uses OpenSSL.

This enables SSL Certificate Verification by client software without manual
intervention.

If you prefer to &lt;span class="k"&gt;do &lt;/span&gt;this manually, replace the following symlinks with
either an empty file or your site-local certificate bundle.

  &lt;span class="k"&gt;*&lt;/span&gt; /etc/ssl/cert.pem
  &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/etc/ssl/cert.pem
  &lt;span class="k"&gt;*&lt;/span&gt; /usr/local/openssl/cert.pem
&lt;span class="o"&gt;=====&lt;/span&gt;
Message from mosquitto-1.6.7:

&lt;span class="nt"&gt;--&lt;/span&gt;
The mosquitto MQTT Python driver is now provided by net/py-paho-mqtt
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All installed package configuration lives at &lt;code&gt;/usr/local/etc/mosquitto/&lt;/code&gt; we'll need to edit &lt;code&gt;mosquitto.conf&lt;/code&gt; in that folder to use certificates. Here's what it looks like:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Daemon configuration&lt;/span&gt;
pid_file /var/run/mosquitto.pid
user nobody

&lt;span class="c"&gt;# Port to use for the default listener.&lt;/span&gt;
port 8885

&lt;span class="c"&gt;# At least one of cafile or capath must be defined.&lt;/span&gt;
cafile /root/pki/ca.crt

&lt;span class="c"&gt;# Path to the PEM encoded server certificate.&lt;/span&gt;
certfile /root/pki/issued/mosquitto.crt

&lt;span class="c"&gt;# Path to the PEM encoded keyfile.&lt;/span&gt;
keyfile /root/pki/private/mosquitto.key

&lt;span class="c"&gt;# Path to CRL file&lt;/span&gt;
&lt;span class="c"&gt;#crlfile /root/pki/crl.pem&lt;/span&gt;

&lt;span class="c"&gt;# Each client has their own cert&lt;/span&gt;
require_certificate &lt;span class="nb"&gt;true
&lt;/span&gt;use_identity_as_username &lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# listener port-number [ip address/host name]&lt;/span&gt;
listener 1883
protocol mqtt

&lt;span class="c"&gt;# listener port-number [ip address/host name]&lt;/span&gt;
&lt;span class="c"&gt;# listener 8080&lt;/span&gt;
&lt;span class="c"&gt;# protocol websockets&lt;/span&gt;

&lt;span class="c"&gt;# =================================================================&lt;/span&gt;
&lt;span class="c"&gt;# Logging&lt;/span&gt;
&lt;span class="c"&gt;# =================================================================&lt;/span&gt;
log_dest syslog

&lt;span class="c"&gt;# Types of messages to log.&lt;/span&gt;
log_type all
&lt;span class="c"&gt;#log_type warning&lt;/span&gt;
&lt;span class="c"&gt;# websockets_log_level 127&lt;/span&gt;

&lt;span class="c"&gt;# -----------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# Default authentication and topic access control&lt;/span&gt;
&lt;span class="c"&gt;# -----------------------------------------------------------------&lt;/span&gt;
&lt;span class="c"&gt;# password_file /usr/local/etc/mosquitto/pwfile&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before we can start the server we'll need to provision some RSA certificates. We'll get to that in the next step!&lt;/p&gt;

&lt;h2&gt;
  
  
  Provision Certs
&lt;/h2&gt;

&lt;p&gt;You can use &lt;strong&gt;easy-rsa&lt;/strong&gt; to generate a CA server and client certs. (These instructions come from &lt;a href="https://github.com/OpenVPN/easy-rsa/blob/master/README.quickstart.md"&gt;this guide&lt;/a&gt;.) For production, you should generate your keys and certs on an offline machine. That way your private keys are safe if your server becomes a target.&lt;/p&gt;

&lt;p&gt;First, install &lt;code&gt;easy-rsa&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;easy-rsa
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 1 package&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)&lt;/span&gt; will be affected &lt;span class="o"&gt;(&lt;/span&gt;of 0 checked&lt;span class="o"&gt;)&lt;/span&gt;:

New packages to be INSTALLED:
        easy-rsa: 3.0.7

Number of packages to be installed: 1

44 KiB to be downloaded.

Proceed with this action? &lt;span class="o"&gt;[&lt;/span&gt;y/N]: y
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;1/1] Fetching easy-rsa-3.0.7.txz: 100%   44 KiB  44.8kB/s    00:01
Checking integrity... &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0 conflicting&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;1/1] Installing easy-rsa-3.0.7...
&lt;span class="o"&gt;[&lt;/span&gt;mosquitto] &lt;span class="o"&gt;[&lt;/span&gt;1/1] Extracting easy-rsa-3.0.7: 100%
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then lets begin the cert creation process!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ easyrsa init-pki

Note: using Easy-RSA configuration from: /usr/local/share/easy-rsa/vars

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /root/pki
$
$ easyrsa build-ca

Note: using Easy-RSA configuration from: /usr/local/share/easy-rsa/vars
Using SSL: openssl OpenSSL 1.1.1d-freebsd  10 Sep 2019

Enter New CA Key Passphrase:
Re-Enter New CA Key Passphrase:
Generating RSA private key, 2048 bit long modulus (2 primes)
......................+++++
..................................................................................+++++
e is 65537 (0x010001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:testserver.jaredwolff.com

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/root/pki/ca.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You will be prompted for a password at the &lt;code&gt;build-ca&lt;/code&gt; step. Make sure you keep this password handy.&lt;/p&gt;

&lt;p&gt;Then to generate a server cert use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ easyrsa gen-req mosquitto nopass

Note: using Easy-RSA configuration from: /usr/local/share/easy-rsa/vars
Using SSL: openssl OpenSSL 1.1.1d-freebsd  10 Sep 2019
Generating a RSA private key
...............+++++
........................................+++++
writing new private key to '/root/pki/easy-rsa-82720.X2NVQ0/tmp.akOxhO'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [mosquitto]:testserver.jaredwolff.com

Keypair and certificate request completed. Your files are:
req: /root/pki/reqs/mosquitto.req
key: /root/pki/private/mosquitto.key
$
$ easyrsa sign-req server mosquitto

Note: using Easy-RSA configuration from: /usr/local/share/easy-rsa/vars
Using SSL: openssl OpenSSL 1.1.1d-freebsd  10 Sep 2019

You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 825 days:

subject=
    commonName                = testserver.jaredwolff.com

Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes
Using configuration from /root/pki/easy-rsa-82744.hyuGzt/tmp.lZHLEH
Enter pass phrase for /root/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'testserver.jaredwolff.com'
Certificate is to be certified until Nov  3 01:12:53 2022 GMT (825 days)

Write out database with 1 new entries
Data Base Updated

Certificate created at: /root/pki/issued/mosquitto.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll be prompted for both the Common Name (i.e. your server name) and the CA cert password in the above step. &lt;strong&gt;Important&lt;/strong&gt;: the &lt;strong&gt;Common Name&lt;/strong&gt; needs to match the domain name of your server! (Remember, we wrote that down earlier?)&lt;/p&gt;

&lt;p&gt;To generate the nRF9160 cert use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ easyrsa gen-req nrf9160 nopass batch
$ easyrsa sign-req client nrf9160 batch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Follow the same procedure as earlier. The only difference is that we're generating a &lt;strong&gt;client&lt;/strong&gt; cert instead of a &lt;strong&gt;server&lt;/strong&gt; cert.&lt;/p&gt;

&lt;p&gt;Once complete, we'll need some files. Here's a full list below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For your Mosquitto Server&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/root/pki/ca.crt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/root/pki/private/mosquitto.key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/root/pki/issued/mosquitto.crt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For your nRF9160 Feather&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/root/pki/ca.crt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/root/pki/private/nrf9160.key&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/root/pki/issued/nrf9160.crt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're using the configuration from above, it's already pointing to your server certificates. All we have to do now is start &lt;code&gt;mosquitto&lt;/code&gt;!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;service mosquitto start
Cannot &lt;span class="s1"&gt;'start'&lt;/span&gt; mosquitto. Set mosquitto_enable to YES &lt;span class="k"&gt;in&lt;/span&gt; /etc/rc.conf or use &lt;span class="s1"&gt;'onestart'&lt;/span&gt; instead of &lt;span class="s1"&gt;'start'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you get an error about &lt;code&gt;mosquitto_enable&lt;/code&gt; simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;sysrc &lt;span class="nv"&gt;mosquitto_enable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YES
&lt;span class="nv"&gt;$ &lt;/span&gt;service mosquitto start
Starting mosquitto.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This enables &lt;code&gt;mosquitto&lt;/code&gt; to start when your system starts.&lt;/p&gt;

&lt;p&gt;Now, check if &lt;code&gt;mosquitto&lt;/code&gt; is running by using &lt;code&gt;ps aux&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ps aux
USER     PID %CPU %MEM   VSZ  RSS TT  STAT STARTED    TIME COMMAND
root   82401  0.0  0.2 11472 2424  -  SsJ  01:02   0:00.00 /usr/sbin/syslogd &lt;span class="nt"&gt;-ss&lt;/span&gt;
root   82457  0.0  0.2 11408 2284  -  IsJ  01:02   0:00.00 /usr/sbin/cron &lt;span class="nt"&gt;-J&lt;/span&gt; 60 &lt;span class="nt"&gt;-s&lt;/span&gt;
nobody 82900  0.0  0.6 16352 6212  -  SsJ  01:17   0:00.02 /usr/local/sbin/mosquitto &lt;span class="nt"&gt;-c&lt;/span&gt; /usr/local/etc/mosquitto/mosquitto.conf &lt;span class="nt"&gt;-d&lt;/span&gt;
root   82488  0.0  0.3 12096 2848  0  IJ   01:02   0:00.01 login &lt;span class="o"&gt;[&lt;/span&gt;pam] &lt;span class="o"&gt;(&lt;/span&gt;login&lt;span class="o"&gt;)&lt;/span&gt;
root   82489  0.0  0.3 13092 3504  0  SJ   01:02   0:00.03 &lt;span class="nt"&gt;-csh&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;csh&lt;span class="o"&gt;)&lt;/span&gt;
root   82902  0.0  0.3 11704 2540  0  R+J  01:17   0:00.00 ps aux
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now that we have a server loaded and running. Let's get the firmware working!&lt;/p&gt;

&lt;h2&gt;
  
  
  Firmware bits
&lt;/h2&gt;

&lt;p&gt;Dealing with certificates on the nRF9160 Feather is a two-step process. The first step is to load the certificates using the &lt;code&gt;at_client&lt;/code&gt; firmware. The second is to load the &lt;code&gt;mqtt_simple&lt;/code&gt; library with added TLS support. Let's tackle the certs first!&lt;/p&gt;

&lt;h3&gt;
  
  
  Program &lt;code&gt;at_client&lt;/code&gt; first
&lt;/h3&gt;

&lt;p&gt;Change directories to &lt;code&gt;ncs/nrf/samples/nrf9160/at_client/&lt;/code&gt; and start a fresh build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;west&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;circuitdojo_feather_nrf9160ns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then flash to your board using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;west&lt;/span&gt; &lt;span class="n"&gt;flash&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;erase&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;nrfjprog&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll need this sample on your board for the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add certs to device
&lt;/h3&gt;

&lt;p&gt;To install our new certs, we'll need nRF Connect Desktop installed. You can download it &lt;a href="https://www.nordicsemi.com/Software-and-tools/Development-Tools/nRF-Connect-for-desktop/Download#infotabs"&gt;by going here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll also need a custom version of &lt;strong&gt;LTE Link Monitor.&lt;/strong&gt; You can get the modified version on &lt;a href="http://localhost:3000/files/pc-nrfconnect-linkmonitor-1.1.1.tgz"&gt;docs.jaredwolff.com.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, Install nRF Connect Desktop app. Then, copy the LTE Link Monitor .tgz file to &lt;code&gt;%USERPROFILE%\.nrfconnect-apps\local&lt;/code&gt; (on Windows) or &lt;code&gt;$HOME/.nrfconnect-apps/local&lt;/code&gt; (on Linux/macOS). Here's an example of where it is on Windows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hKtb9W6_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/windows-nrfconnect-apps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hKtb9W6_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/windows-nrfconnect-apps.png" alt="Windows nRF Connect Apps"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Close and re-open nRF Connect Desktop (if it's open).&lt;/p&gt;

&lt;p&gt;Then, click &lt;em&gt;Open&lt;/em&gt; next to the v1.1.1 version of LTE Link Monitor. It will also have &lt;strong&gt;&lt;em&gt;local&lt;/em&gt;&lt;/strong&gt; written underneath it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HUyLFIAE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/lte-link-monitor.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HUyLFIAE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/lte-link-monitor.png" alt="LTE Link Monitor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, let's launch it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JTv-fR36--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.25.16_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JTv-fR36--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.25.16_PM.png" alt="LTE Link Monitor select device"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you've opened the port, hit the reset button. Make sure you turn off &lt;strong&gt;Automatic requests.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aiDP2cYp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/lte-link-monitor-automatic-requests.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aiDP2cYp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/lte-link-monitor-automatic-requests.png" alt="LTE Link Monitor automatic **requests**"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then in the command box send &lt;strong&gt;AT+CFUN=4&lt;/strong&gt;. This will shut down your modem so it's ready to upload certs. You can run &lt;strong&gt;AT+CFUN?&lt;/strong&gt; to confirm your modem in that mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sHauu7kw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.25.22_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sHauu7kw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.25.22_PM.png" alt="LTE Link Monitor Certificate Manager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open up the Certificate manager.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z2aDqsmk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.25.41_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z2aDqsmk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.25.41_PM.png" alt="LTE Link Monitor Security Tag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure you set the security tag. In this case, I'm using 1234. This is an important identifier that you'll need later. Make it whatever you want but I would avoid using 16842753. This is the default tag for NRF Cloud. You don't want to blast away your nRF Cloud certs!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y3CvE56W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.26.42_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y3CvE56W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.26.42_PM.png" alt="LTE Link Monitor with certificates entered"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy and paste the contents of your &lt;code&gt;ca.crt&lt;/code&gt;, &lt;code&gt;nrf9160.crt&lt;/code&gt; and &lt;code&gt;nrf9160.key&lt;/code&gt; into the boxes. (in that order)  You can easily get the certs by using &lt;code&gt;cat&lt;/code&gt; on Unix/Linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;cat.crt
&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; CERTIFICATE-----
MIIDdTCCAl2gAwIBAgIUDLkBxLLQO9wosNDtA7E9qvqHOxMwDQYJKoZIhvcNAQEL
BQAwJDEiMCAGA1UEAwwZdGVzdHNlcnZlci5qYXJlZHdvbGZmLmNvbTAeFw0yMDA3
MzEwMTExNDJaFw0zMDA3MjkwMTExNDJaMCQxIjAgBgNVBAMMGXRlc3RzZXJ2ZXIu
amFyZWR3b2xmZi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3
de1v8k+FXzY/Im7Z2YKS7wwbBRft5CUxqP1sdYJgMvheS9LhFufk81URZ0lHD9pK
aNPxU1UEmnLvVDTGLJ+YAmMH08xn17FS1R1UVPYzi2ouwqRM2pR9EStsSlP9Zj44
1MsdizABnnlkZndUVLL/gjc4cNsNncMLBSEbsz6b5WzhtAGg3rOpdAxSSblZVSFw
bquCgg5hb2NUzy+JxGtUIsE5d6CxTDdSs4Z3FK/RRYjmCG6qsaya4N5W35yf8h5O
StfKRecl3kq2kCnWa6P+lErG4wuxIBtMkgz2zV+zd1tz4aHXxSdoZTqLz7dTVbFA
zEVnKD+ZReBG+4fwUL6rAgMBAAGjgZ4wgZswHQYDVR0OBBYEFIvdGnjrxRPzvXQi
7XJ70LzpZSOjMF8GA1UdIwRYMFaAFIvdGnjrxRPzvXQi7XJ70LzpZSOjoSikJjAk
MSIwIAYDVQQDDBl0ZXN0c2VydmVyLmphcmVkd29sZmYuY29tghQMuQHEstA73Ciw
0O0DsT2q+oc7EzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0B
AQsFAAOCAQEAIzz1nSSDkPueNPlADRYMDOMFNkxoKA+gRXwDVa7y39As7IZp7Fqr
KAH79U1XtGyDlt6FPKTvDJ7jtd4y8auIGVQO7z3AG9pVU1imIWZHoIqgBUCEhsjp
uMxD23kRCX5kd9dsmF9WOGGxb4kkMv83Rh2rCONQmvnozuI3fJv2ZFX/ORoADGLP
OPSJPl11x+2rxPxiLi+T8RyzDh3DwqnPVsSnbRWV7hosaN0ip/cbnSTaIul9mbCY
ID6qm9leqlY/gha9aZfg+tv1Lm6PT6o8Pzek2VeDoIS5YERBMOwV84hQrZjV3vIE
&lt;span class="nv"&gt;jT6y663HGsl7KvqVaWdV3fM6Cr7f0QdR5A&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;span class="nt"&gt;-----END&lt;/span&gt; CERTIFICATE-----
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll need everything from &lt;code&gt;-----BEGIN CERTIFICATE-----&lt;/code&gt; to &lt;code&gt;-----END CERTIFICATE-----&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aT9woP45--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.28.41_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aT9woP45--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-30_at_10.28.41_PM.png" alt="LTE Link Monitor Update certificates button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the &lt;strong&gt;Log&lt;/strong&gt; area for more details. If all went well, it should say your certificates updated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fkdqJZSD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_2.54.48_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fkdqJZSD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_2.54.48_PM.png" alt="LTE Link Monitor Ceritificate Update progress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the mqtt_simple example
&lt;/h3&gt;

&lt;p&gt;We'll be using the &lt;code&gt;mqtt_simple&lt;/code&gt; sample within the nRF Connect SDK repository. The full path is: &lt;code&gt;ncs/nrf/samples/nrf9160/mqtt_simple&lt;/code&gt;. We'll need to make a few edits to add full TLS compatibility. All the files are within the &lt;code&gt;mqtt_simple&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;First, we'll have to update the &lt;code&gt;proj.conf&lt;/code&gt; file. See the highlighted differences:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vHqUwg-g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_3.14.47_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vHqUwg-g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_3.14.47_PM.png" alt="prj.conf differences"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;# Set the PDP Context&lt;/code&gt; section is especially important if you're using a Hologram SIM card. (Included with the nRF9160 Feather) If you are using a SIM that doesn't need it, you do not need this section.&lt;/p&gt;

&lt;p&gt;Adopt your &lt;code&gt;CONFIG_MQTT_BROKER_HOSTNAME&lt;/code&gt; to your hostname. (configured at the beginning of this guide).&lt;/p&gt;

&lt;p&gt;You'll also have to add these lines in &lt;code&gt;KConfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;SEC_TAG&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;"Security tag to use for the connection"&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;PEER_VERIFY&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;"Peer verify parameter for mqtt_client"&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;help&lt;/span&gt;
            &lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;VERIFY_NONE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;VERIFY_OPTIONAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;VERIFY_REQUIRED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally in main add this block to the top of your file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#if defined(CONFIG_MQTT_LIB_TLS)
&lt;/span&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;sec_tag_t&lt;/span&gt; &lt;span class="n"&gt;sec_tag_list&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;CONFIG_SEC_TAG&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="cp"&gt;#endif &lt;/span&gt;&lt;span class="cm"&gt;/* defined(CONFIG_MQTT_LIB_TLS) */&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then add this block to &lt;code&gt;client_init&lt;/code&gt; under &lt;code&gt;#if defined(CONFIG_MQTT_LIB_TLS)&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;  &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;mqtt_sec_config&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tls_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MQTT_TRANSPORT_SECURE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;peer_verify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CONFIG_PEER_VERIFY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cipher_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cipher_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sec_tag_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ARRAY_SIZE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sec_tag_list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sec_tag_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sec_tag_list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;tls_config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CONFIG_MQTT_BROKER_HOSTNAME&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The changes should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zfjAHr89--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_1.23.00_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zfjAHr89--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_1.23.00_PM.png" alt="main.c differences"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then build with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;west&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="n"&gt;circuitdojo_feather_nrf9160ns&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, flash it using &lt;code&gt;west flash&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;west&lt;/span&gt; &lt;span class="n"&gt;flash&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;erase&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt; &lt;span class="n"&gt;nrfjprog&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open your serial terminal and double check that your nRF9160 Feather is connecting. You can also use LTE Link Monitor to view your progress as well. (Example below)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0ZSU35sH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_3.31.54_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0ZSU35sH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_3.31.54_PM.png" alt="LTE Link Monitor terminal showing connection progress"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lots of the information above came from &lt;a href="https://devzone.nordicsemi.com/nordic/cellular-iot-guides/b/software-and-protocols/posts/enabling-and-testing-tls-in-mqtt_5f00_simple"&gt;Nordic&lt;/a&gt;'s post on the subject.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending a message
&lt;/h2&gt;

&lt;p&gt;We're almost there! You've configured your nRF9160 Feather to connect to Mosquitto using self-generated certificates. The last part is connecting another device to see if the nRF9160 Feather replies to a message.&lt;/p&gt;

&lt;p&gt;I've created a new set of certs for this purpose. I called them &lt;code&gt;test&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;easyrsa gen-req &lt;span class="nb"&gt;test &lt;/span&gt;nopass batch
&lt;span class="nv"&gt;$ &lt;/span&gt;easyrsa sign-req client &lt;span class="nb"&gt;test &lt;/span&gt;batch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I copied them to my desktop using CyberDuck (a great little visual SFTP client):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6H-0meXV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_3.40.11_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6H-0meXV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-07-31_at_3.40.11_PM.png" alt="CyberDuck file downloads"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use something like &lt;code&gt;scp&lt;/code&gt; if your confident in your command line file transfer abilities! Then, open a terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;mosquitto_sub &lt;span class="nt"&gt;--cafile&lt;/span&gt; ca.crt &lt;span class="nt"&gt;--cert&lt;/span&gt; test.crt &lt;span class="nt"&gt;--key&lt;/span&gt; test.key &lt;span class="nt"&gt;-q&lt;/span&gt; 1 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; testserver.jaredwolff.com &lt;span class="nt"&gt;-p&lt;/span&gt; 8885 &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"/my/publish/topic"&lt;/span&gt; &amp;amp;
mosquitto_pub &lt;span class="nt"&gt;--cafile&lt;/span&gt; ca.crt &lt;span class="nt"&gt;--cert&lt;/span&gt; test.crt &lt;span class="nt"&gt;--key&lt;/span&gt; test.key &lt;span class="nt"&gt;-q&lt;/span&gt; 1 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; testserver.jaredwolff.com &lt;span class="nt"&gt;-p&lt;/span&gt; 8885 &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"/my/subscribe/topic"&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"hello there"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You should see an output like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mosquitto_sub &lt;span class="nt"&gt;--cafile&lt;/span&gt; ca.crt &lt;span class="nt"&gt;--cert&lt;/span&gt; test.crt &lt;span class="nt"&gt;--key&lt;/span&gt; test.key &lt;span class="nt"&gt;-q&lt;/span&gt; 1 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; testserver.jaredwolff.com &lt;span class="nt"&gt;-p&lt;/span&gt; 8885 &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"/my/publish/topic"&lt;/span&gt; &amp;amp;
&lt;span class="nv"&gt;$ &lt;/span&gt;mosquitto_pub &lt;span class="nt"&gt;--cafile&lt;/span&gt; ca.crt &lt;span class="nt"&gt;--cert&lt;/span&gt; test.crt &lt;span class="nt"&gt;--key&lt;/span&gt; test.key &lt;span class="nt"&gt;-q&lt;/span&gt; 1 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; testserver.jaredwolff.com &lt;span class="nt"&gt;-p&lt;/span&gt; 8885 &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"/my/subscribe/topic"&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"hello there"&lt;/span&gt;
Client mosq-CczskQKzMKdtTo4O4s sending CONNECT
Client mosq-CczskQKzMKdtTo4O4s received CONNACK &lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt;
Client mosq-CczskQKzMKdtTo4O4s sending PUBLISH &lt;span class="o"&gt;(&lt;/span&gt;d0, q1, r0, m1, &lt;span class="s1"&gt;'/my/subscribe/topic'&lt;/span&gt;, ... &lt;span class="o"&gt;(&lt;/span&gt;11 bytes&lt;span class="o"&gt;))&lt;/span&gt;
Client mosq-CczskQKzMKdtTo4O4s received PUBACK &lt;span class="o"&gt;(&lt;/span&gt;Mid: 1, RC:0&lt;span class="o"&gt;)&lt;/span&gt;
Client mosq-CczskQKzMKdtTo4O4s sending DISCONNECT
MacBook-Pro:Downloads jaredwolff&lt;span class="nv"&gt;$ &lt;/span&gt;Client mosq-qK8tMlJk0Qri4Z7jUo sending PINGREQ
Client mosq-qK8tMlJk0Qri4Z7jUo received PINGRESP
MacBook-Pro:Downloads jaredwolff&lt;span class="nv"&gt;$ &lt;/span&gt;Client mosq-qK8tMlJk0Qri4Z7jUo received PUBLISH &lt;span class="o"&gt;(&lt;/span&gt;d0, q0, r0, m0, &lt;span class="s1"&gt;'/my/publish/topic'&lt;/span&gt;, ... &lt;span class="o"&gt;(&lt;/span&gt;11 bytes&lt;span class="o"&gt;))&lt;/span&gt;
hello there
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Booyah! You have an active working connection to your very own Mosquitto server!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've made it to the end! By this point in the post, you should have a Mosquitto server running and an nRF9160 connected. Now you can use your new-found skills to add more devices to your deployments and more!&lt;/p&gt;

&lt;p&gt;If you haven't had a chance to play with the nRF9160 you should check out the nRF9160 Feather. It has Nordic Semiconductor's nRF9160 LTE-M, NB IoT + GPS Combo, plus flexible power supply, external flash, and low power shutdown. Oh, and did I mention it's 100% open source?  Learn more by checking out the campaign on &lt;a href="https://www.jaredwolff.com/store/nrf91-feather/"&gt;GroupGets and Hackster Launch.&lt;/a&gt; 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6JB1pNV_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/IMG_8749.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6JB1pNV_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/IMG_8749.jpg" alt="nRF9160 Feather Top Side"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v7w5UlhN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/IMG_8750.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v7w5UlhN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/IMG_8750.jpg" alt="nRF9160 Feather Bottom Side"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Photo credit to the awesome folks at &lt;a href="https://www.groupgets.com"&gt;GroupGets&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webops</category>
      <category>showdev</category>
      <category>howto</category>
      <category>opensource</category>
    </item>
    <item>
      <title>My Latest Self Hosted Hugo Workflow</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Fri, 27 Mar 2020 20:01:46 +0000</pubDate>
      <link>https://dev.to/jaredwolff/my-latest-self-hosted-hugo-workflow-4oj</link>
      <guid>https://dev.to/jaredwolff/my-latest-self-hosted-hugo-workflow-4oj</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1u0H1b1h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Self_hosted_Hugo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1u0H1b1h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Self_hosted_Hugo.png" alt="images/Self_hosted_Hugo.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After hosting with Netlify for a few years, I decided to head back to self hosting. Theres a few reasons for that but the main reasoning was that I had more control over how things worked.&lt;/p&gt;

&lt;p&gt;In this post, i'll show you my workflow for deploying my &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; generated site (&lt;a href="https://www.jaredwolff.com"&gt;www.jaredwolff.com&lt;/a&gt;). Instead of using what most people would go for, i'll be doing all of this using a FreeBSD Jails based server. Plus i'll show you some tricks i've learned over the years on bulk image resizing and more.&lt;/p&gt;

&lt;p&gt;Let's get to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to host?
&lt;/h2&gt;

&lt;p&gt;If you want to host your own service, you'll need a server. That's where a VPS provider like Digital Ocean or Vultr comes in. I've been a fan and have used Digital Ocean for a while now.&lt;/p&gt;

&lt;p&gt;To set up a new server here are some steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login to Digital Ocean. If you don’t have Digital Ocean and would like to support this blog click &lt;a href="https://m.do.co/c/9574d3846a29"&gt;here&lt;/a&gt; to create an account.&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Account Settings&lt;/code&gt; -&amp;gt; &lt;code&gt;Security&lt;/code&gt; and make sure you have an SSH key setup.&lt;/li&gt;
&lt;li&gt;Create a new FreeBSD droplet. Make sure you use the UFS version &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V-NWI5ey--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Screen_Shot_2020-04-15_at_9.41.21_AM.png" alt="Create Droplet"&gt; &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Unjj8g6T--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Screen_Shot_2020-04-15_at_9.43.21_AM.png" alt="Choose FreeBSD 12.1 UFS"&gt;
&lt;/li&gt;
&lt;li&gt;Make sure you select the $5 a month plan. For simple installs, this is more than enough! &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pDMcSMUs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Screen_Shot_2020-04-15_at_9.44.13_AM.png" alt="$5 Plan"&gt;
&lt;/li&gt;
&lt;li&gt;Make sure your SSH key is selected &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M8TyDQw8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Screen_Shot_2020-04-15_at_9.45.26_AM.png" alt="Select SSH key"&gt;
&lt;/li&gt;
&lt;li&gt;Finally click that green &lt;strong&gt;Create Droplet&lt;/strong&gt; button! &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n9iYD2XO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Screen_Shot_2020-04-15_at_9.45.24_AM.png" alt="Create droplet"&gt;
&lt;/li&gt;
&lt;li&gt;SSH in once you’re done: &lt;code&gt;ssh root@&amp;lt;yourserverip&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Setting up your FreeBSD server with Bastille
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8msdofC_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/bastille.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8msdofC_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/bastille.png" alt="images/bastille.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Up until recently everything was running on a Docker based platform using &lt;a href="https://github.com/exoframejs/exoframe"&gt;Exoframe&lt;/a&gt;. It was easy and almost brainless. The unfortunate part of this is the fact that Docker takes up wayyyy to many resources. Plus managing files within a Docker container is as much or more work than hosting it natively. Oh and have you checked how much space Docker has been using on your machine lately? On my development machine it's was about 19GB of space. 😬&lt;/p&gt;

&lt;p&gt;So what's the alternative?&lt;/p&gt;

&lt;p&gt;FreeBSD Jails using Bastille.&lt;/p&gt;

&lt;p&gt;I've been playing with Bastille for a few months now. The more I use it, the more it makes 100% sense.&lt;/p&gt;

&lt;p&gt;Bastille allows you to create a (now) portable lightweight FreeBSD based jails. These jails are "containers" that have virtually no overhead. There's no daemon (the operating system is the "daemon"!). Plus, Jails are secure compared to the can of worms that Docker is. Yes, you may have to compile and port some utilities. Most though are already supported in FreeBSD's package manager &lt;code&gt;pkg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this section you'll learn how to get a jail running with &lt;code&gt;caddy&lt;/code&gt; so you can securely host your site.&lt;/p&gt;

&lt;p&gt;Let's keep the momentum going!&lt;/p&gt;

&lt;p&gt;Once you get the IP address for your server you should login:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh root@123.456.789.10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You should get a MOTD message and a &lt;code&gt;sh&lt;/code&gt; prompt. Woo!&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FreeBSD 12.1-RELEASE-p2 GENERIC

Welcome to FreeBSD!
...

#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Let's install a few important bits using &lt;code&gt;pkg&lt;/code&gt; (FreeBSD's package manager)&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pkg install restic rsync bastille
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We'll be using &lt;code&gt;restic&lt;/code&gt; for backups, &lt;code&gt;rsync&lt;/code&gt; for transferring files and &lt;code&gt;bastille&lt;/code&gt; for jail setup.&lt;/p&gt;

&lt;p&gt;You also have to set up some static routes in your &lt;code&gt;pf.conf&lt;/code&gt;. Here's an example of mine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ext_if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"vtnet0"&lt;/span&gt;

&lt;span class="c"&gt;# Caddy related&lt;/span&gt;
&lt;span class="nv"&gt;caddy_addr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10.10.2.20

&lt;span class="nb"&gt;set &lt;/span&gt;block-policy &lt;span class="k"&gt;return
&lt;/span&gt;scrub &lt;span class="k"&gt;in &lt;/span&gt;on &lt;span class="nv"&gt;$ext_if&lt;/span&gt; all fragment reassemble
&lt;span class="nb"&gt;set &lt;/span&gt;skip on lo

table &amp;lt;jails&amp;gt; persist
nat on &lt;span class="nv"&gt;$ext_if&lt;/span&gt; from &amp;lt;jails&amp;gt; to any -&amp;gt; &lt;span class="nv"&gt;$ext_if&lt;/span&gt;

&lt;span class="c"&gt;# container routes&lt;/span&gt;
rdr pass inet proto tcp from any to port 80 -&amp;gt; &lt;span class="nv"&gt;$caddy_addr&lt;/span&gt; port 8880
rdr pass inet proto tcp from any to port 443 -&amp;gt; &lt;span class="nv"&gt;$caddy_addr&lt;/span&gt; port 4443

&lt;span class="c"&gt;# Enable dynamic rdr (see below)&lt;/span&gt;
rdr-anchor &lt;span class="s2"&gt;"rdr/*"&lt;/span&gt;

block &lt;span class="k"&gt;in &lt;/span&gt;all
pass out quick modulate state
antispoof &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;$ext_if&lt;/span&gt; inet
pass &lt;span class="k"&gt;in &lt;/span&gt;inet proto tcp from any to any port ssh flags S/SA keep state
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is a standard &lt;code&gt;pf.conf&lt;/code&gt; file for &lt;code&gt;bastille&lt;/code&gt;.  Make sure you edit &lt;code&gt;caddy_addr&lt;/code&gt; to the IP you chose.&lt;/p&gt;

&lt;p&gt;Now let's start the firewall. You will get kicked out of your &lt;code&gt;ssh&lt;/code&gt; session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sysrc pf_enable="YES"
service pf start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then let's get some &lt;code&gt;bastille&lt;/code&gt; configuration out of the way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# set up bastille networking
sysrc cloned_interfaces+=lo1
sysrc ifconfig_lo1_name="bastille0"
service netif cloneup

# bootstrap the base jail and start bastille
bastille bootstrap 12.1-RELEASE update
sysrc bastille_enable="YES"
service bastille start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will set up your networking, and fetch the latest default base jail you'll use later.&lt;/p&gt;

&lt;p&gt;Next, let's set up the jail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bastille create caddy 12.1-STABLE 10.10.2.20
bastille start caddy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then install &lt;code&gt;caddy&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#install the binary
fetch https://github.com/caddyserver/caddy/releases/download/v1.0.4/caddy_v1.0.4_freebsd_amd64.tar.gz
tar xvf caddy_v1.0.4_freebsd_amd64.tar.gz caddy
bastille cp caddy caddy /usr/local/bin/
rm caddy

#create the caddy user
bastille cmd caddy pw useradd caddy -m -s /usr/sbin/nologin

#install ca root file
bastille pkg caddy install ca_root_nss
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When installing &lt;code&gt;ca_root_nss&lt;/code&gt; , &lt;code&gt;pkg&lt;/code&gt; will have to initialize. Accept the prompts. Once you're done here we'll move on to the next step!&lt;/p&gt;

&lt;p&gt;Once install is complete, we should also configure &lt;code&gt;caddy&lt;/code&gt; to start on boot. The easiest way to do that is use this &lt;code&gt;rc.d&lt;/code&gt; script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="c"&gt;# $FreeBSD: head/net/caddy/files/caddy.in 452063 2017-10-14 12:58:24Z riggs $&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# PROVIDE: caddy&lt;/span&gt;
&lt;span class="c"&gt;# REQUIRE: LOGIN&lt;/span&gt;
&lt;span class="c"&gt;# KEYWORD: shutdown&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# Add the following lines to /etc/rc.conf.local or /etc/rc.conf&lt;/span&gt;
&lt;span class="c"&gt;# to enable this service:&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;# caddy_enable (bool):  Set to NO by default.&lt;/span&gt;
&lt;span class="c"&gt;#               Set it to YES to enable caddy.&lt;/span&gt;
&lt;span class="c"&gt;# caddy_user (user):        Set user to run caddy.&lt;/span&gt;
&lt;span class="c"&gt;#               Default is "caddy".&lt;/span&gt;
&lt;span class="c"&gt;# caddy_group (group):  Set group to run caddy.&lt;/span&gt;
&lt;span class="c"&gt;#               Default is "caddy".&lt;/span&gt;
&lt;span class="c"&gt;# caddy_conf (path):        Path to caddy configuration file.&lt;/span&gt;
&lt;span class="c"&gt;#               Default is /usr/local/etc/caddyfile.conf&lt;/span&gt;

&lt;span class="nb"&gt;.&lt;/span&gt; /etc/rc.subr

&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;caddy
&lt;span class="nv"&gt;rcvar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;caddy_enable

load_rc_config &lt;span class="nv"&gt;$name&lt;/span&gt;

: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_enable&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"NO"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_user&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"caddy"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_group&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"caddy"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_conf&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/etc/caddyfile.conf"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_log&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/caddy/caddy.log"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_env&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"CADDYPATH=/home/caddy/"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_https_port&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"4443"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_http_port&lt;/span&gt;:&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"8880"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;pidfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/run/caddy.pid"&lt;/span&gt;
&lt;span class="nv"&gt;procname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bin/caddy"&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/sbin/daemon"&lt;/span&gt;
&lt;span class="nv"&gt;command_args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-f -p &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pidfile&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; /usr/bin/env &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_env&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;procname&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -agree -http-port &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_http_port&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;  -https-port &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_https_port&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -conf=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_conf&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -log=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_log&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_args&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;extra_commands&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"reload"&lt;/span&gt;

&lt;span class="nv"&gt;start_precmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;caddy_startprecmd
&lt;span class="nv"&gt;reload_cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;caddy_reloadcmd

caddy_startprecmd&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pidfile&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
              &lt;/span&gt;&lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_user&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;caddy_group&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; /dev/null &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pidfile&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

caddy_reloadcmd&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; USR1 &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;pidfile&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

run_rc_command &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Remove the &lt;code&gt;caddy&lt;/code&gt; executable if you haven't already. Then create a new file with &lt;code&gt;vi&lt;/code&gt;. This will be your &lt;code&gt;rc.d&lt;/code&gt; script!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vi caddy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then paste the contents of the above script in there, save and exit.&lt;/p&gt;

&lt;p&gt;Then make sure the file is executable by using &lt;code&gt;chmod&lt;/code&gt; and copy to the Caddy container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x caddy
bastille cp caddy caddy /usr/local/etc/rc.d/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, we'll need a Caddyfile. Here's an example of one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stage.jaredwolff.com {
  tls hello@jaredwolff.com
  log /home/caddy/stage.jaredwolff.com.log
  root /var/www/stage.jaredwolff.com/
  gzip
  log stderr
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;log&lt;/code&gt; refers to this site specific access log.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;root&lt;/code&gt; refers to where the root &lt;code&gt;public&lt;/code&gt; folder is on your machine. In my case it's the common &lt;code&gt;/var/www/&amp;lt;name of site&amp;gt;&lt;/code&gt;. Set your paths and remember them. We'll need them later!&lt;/p&gt;

&lt;p&gt;To have Caddy generate certs for this subdomain, you'll have to set the &lt;em&gt;tls&lt;/em&gt; option. An email is all that's needed.&lt;/p&gt;

&lt;p&gt;For more on the Caddyfile structure &lt;a href="https://caddyserver.com/docs/caddyfile"&gt;check out the documentation.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make a file called &lt;code&gt;caddyfile.conf&lt;/code&gt; and copy it to &lt;code&gt;/usr/local/etc/&lt;/code&gt; in your Caddy container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vi caddyfile.conf
# Paste your caddyfile contents and save
bastille cp caddy caddyfile.conf /usr/local/etc/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You should now redirect your DNS to the server IP. That way Caddy can generate/fetch the correct certificates. Then you can start Caddy with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bastille service caddy caddy start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can check the log at &lt;code&gt;/usr/home/caddy/caddy.log&lt;/code&gt; to make sure that your domain provisioned correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Side note:&lt;/em&gt;&lt;/strong&gt; Getting setup with SSL certs is tough at first. (Especially if you're migrating from another server.) Your site will have to go down for a little bit while you switch your DNS settings and start &lt;code&gt;caddy&lt;/code&gt;. (That's if you're using standard &lt;code&gt;caddy&lt;/code&gt; 1.0. You can also use the DNS provider &lt;a href="https://github.com/caddyserver/dnsproviders"&gt;plugins here&lt;/a&gt; which make things a little easier.)&lt;/p&gt;

&lt;p&gt;Now that we have &lt;code&gt;caddy&lt;/code&gt; up and running it's time to copy our &lt;code&gt;hugo&lt;/code&gt; generated assets over using &lt;code&gt;rsync&lt;/code&gt;. We're off to the next step!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Make&lt;/em&gt; building and deploying easy
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XEgWAdvp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/make.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XEgWAdvp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/make.png" alt="images/make.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I spend a ton of time programming C code and that means I spend tons of time using Makefiles. For many &lt;code&gt;make&lt;/code&gt; (or &lt;code&gt;gmake&lt;/code&gt; for GNU make) is the bane of our existences. For building and deploying, &lt;code&gt;make&lt;/code&gt; makes it easy to create reusable recipes. That way you know you can deploy with confidence every time.&lt;/p&gt;

&lt;p&gt;My Makefile borrows from the one that &lt;a href="https://victoria.dev/blog/a-portable-makefile-for-continuous-delivery-with-hugo-and-github-pages/"&gt;Victoria Drake had posted&lt;/a&gt; not too long ago. I changed it up a bit to match my needs. Let's take a tour and see what's inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.POSIX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;

&lt;span class="nv"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; 0.66.0

&lt;span class="nv"&gt;OPTIMIZED_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; optimized
&lt;span class="nv"&gt;CONTENT_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; content
&lt;span class="nv"&gt;DEST_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; public

&lt;span class="nv"&gt;SERVER&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; 123.456.789.10
&lt;span class="nv"&gt;USER&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; user
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The first section contains all the variables that I use to tell the functions later on what to do. It also has a reference to the &lt;code&gt;.POSIX&lt;/code&gt; target. This means that the Makefile will be as portable between different versions of &lt;code&gt;make&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, I popped in some logic to determine whether i'm deploying to &lt;em&gt;stage&lt;/em&gt; or &lt;em&gt;production:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set the place where it's deployed to.
&lt;/span&gt;&lt;span class="k"&gt;ifdef&lt;/span&gt; &lt;span class="nv"&gt;PRODUCTION&lt;/span&gt;
&lt;span class="err"&gt;$(info&lt;/span&gt; &lt;span class="err"&gt;Building&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;production.&lt;/span&gt; &lt;span class="err"&gt;🚀)&lt;/span&gt;
&lt;span class="nv"&gt;TARGET&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; www
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="err"&gt;$(info&lt;/span&gt; &lt;span class="err"&gt;Building&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;development.&lt;/span&gt; &lt;span class="err"&gt;🚀)&lt;/span&gt;
&lt;span class="nv"&gt;BASEURL&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nt"&gt;--baseURL&lt;/span&gt; &lt;span class="s2"&gt;"https://stage.jaredwolff.com"&lt;/span&gt;
&lt;span class="nv"&gt;TARGET&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; stage
&lt;span class="k"&gt;endif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By default, recipes below will use the development workflow. To use the production workflow, you can invoke &lt;code&gt;make&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;PRODUCTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 make build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This does add some extra friction to the deploy process. It's a good step though. That way you're sure the deploy is going to the right place!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Full path
&lt;/span&gt;&lt;span class="nv"&gt;DEPLOY_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; /usr/local/bastille/jails/caddy/root/path/to/&lt;span class="nv"&gt;$(TARGET)&lt;/span&gt;.jaredwolff.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using the &lt;code&gt;TARGET&lt;/code&gt; variable above, I then define the path to my server assets. I'm using Bastille to organize my jails so the path is extra lengthly long. (yea, lengthly long) This allows us to use &lt;code&gt;rsync&lt;/code&gt; to deploy the files with ease.&lt;/p&gt;

&lt;p&gt;Now here come the fun bits. To do a full bulk resize, i'm using the &lt;code&gt;wildcard&lt;/code&gt; functionality of the Makefile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;IMAGES&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.jpg&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.JPG&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.jpeg&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.png&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.jpg&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.jpeg&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.png&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.JPG&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this case it will create a huge space delimited list of every image that is within my content directory. The biggest drawback of this method is that it's not space tolerant. An easy fix to this is to make sure that all my photos do not have spaces.&lt;/p&gt;

&lt;p&gt;Here's a quick and dirty bash command. You can use to rename files that have spaces and replace them with '_' characters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;f&lt;/span&gt; &lt;span class="err"&gt;in&lt;/span&gt; &lt;span class="err"&gt;*\&lt;/span&gt; &lt;span class="err"&gt;*;&lt;/span&gt; &lt;span class="err"&gt;do&lt;/span&gt; &lt;span class="err"&gt;mv&lt;/span&gt; &lt;span class="s2"&gt;"$f"&lt;/span&gt; &lt;span class="s2"&gt;"${f// /_}"&lt;/span&gt;&lt;span class="err"&gt;;&lt;/span&gt; &lt;span class="err"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, we rename these entries so the prefix is now the target directory. This will be useful when we want to resize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;OPTIMIZED_IMAGES&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;subst&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/,&lt;span class="nv"&gt;$(OPTIMIZED_DIR)&lt;/span&gt;/,&lt;span class="nv"&gt;$(IMAGES)&lt;/span&gt;&lt;span class="nf"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now check out the &lt;code&gt;optimize&lt;/code&gt; recipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;optimize&lt;/span&gt;
&lt;span class="nl"&gt;optimize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build $(OPTIMIZED_IMAGES)&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="s2"&gt;"🧂 Optimizing images"&lt;/span&gt;
&lt;span class="err"&gt;rsync&lt;/span&gt; &lt;span class="err"&gt;-r&lt;/span&gt; &lt;span class="err"&gt;$(OPTIMIZED_DIR)/&lt;/span&gt; &lt;span class="err"&gt;$(DEST_DIR)/&lt;/span&gt;
&lt;span class="err"&gt;du&lt;/span&gt; &lt;span class="err"&gt;-sh&lt;/span&gt; &lt;span class="err"&gt;$(CONTENT_DIR)/&lt;/span&gt;
&lt;span class="err"&gt;du&lt;/span&gt; &lt;span class="err"&gt;-sh&lt;/span&gt; &lt;span class="err"&gt;$(DEST_DIR)/&lt;/span&gt;

&lt;span class="nl"&gt;$(OPTIMIZED_IMAGES)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;convert&lt;/span&gt; &lt;span class="err"&gt;-strip&lt;/span&gt; &lt;span class="err"&gt;-compress&lt;/span&gt; &lt;span class="err"&gt;JPEG&lt;/span&gt; &lt;span class="err"&gt;-resize&lt;/span&gt; &lt;span class="s1"&gt;'730&amp;gt;'&lt;/span&gt; &lt;span class="err"&gt;$(subst&lt;/span&gt; &lt;span class="err"&gt;$(OPTIMIZED_DIR)/,$(CONTENT_DIR)/,$@)&lt;/span&gt; &lt;span class="err"&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It first calls the &lt;code&gt;build&lt;/code&gt; recipe and then also the &lt;code&gt;$(OPTIMIZED_IMAGES)&lt;/code&gt; recipe. The later will optimize the image using the &lt;code&gt;convert&lt;/code&gt; command from &lt;a href="https://imagemagick.org/script/convert.php"&gt;Imagemagick&lt;/a&gt;. In this case i'm only resizing files that are larger than 730px wide. Change your accordingly so you can reap the benefits of an &lt;a href="https://www.jaredwolff.com/seven-ways-to-optimize-your-site-for-speed/"&gt;optimized site.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After resizing, the recipe uses &lt;code&gt;rsync&lt;/code&gt; to copy the files from the &lt;code&gt;OPTIMIZED_DIR&lt;/code&gt; to &lt;code&gt;DEST_DIR.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If we take a look at the &lt;code&gt;build&lt;/code&gt; recipe, I first building the assets. Then, I copy the photos from the &lt;code&gt;content&lt;/code&gt; dir to &lt;code&gt;optimized&lt;/code&gt; dir. The nice thing is that &lt;code&gt;rsync&lt;/code&gt; will only move files that have changed. Thus it doesn't have to copy the files over and over and over again every time you build.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;deploy&lt;/code&gt; recipe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;deploy&lt;/span&gt;
&lt;span class="nl"&gt;deploy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="err"&gt;rsync&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;$(DEPLOY_DIR)&lt;/span&gt;
&lt;span class="nl"&gt;@rsync -r --del public/ $(USER)@$(SERVER)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;$(DEPLOY_DIR)/&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="err"&gt;making&lt;/span&gt; &lt;span class="err"&gt;restic&lt;/span&gt; &lt;span class="err"&gt;snapshot&lt;/span&gt;
&lt;span class="nl"&gt;@scp scripts/backup.sh $(USER)@$(SERVER)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;/root/backup.sh&lt;/span&gt;
&lt;span class="err"&gt;@ssh&lt;/span&gt; &lt;span class="err"&gt;$(USER)@$(SERVER)&lt;/span&gt; &lt;span class="err"&gt;sh&lt;/span&gt; &lt;span class="err"&gt;/root/backup.sh&lt;/span&gt; &lt;span class="err"&gt;$(DEPLOY_DIR)&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Site is deployed!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can see again that i'm using rsync to sync the contents of &lt;code&gt;public/&lt;/code&gt; to the server. Make sure you set the &lt;code&gt;USER&lt;/code&gt; , &lt;code&gt;SERVER&lt;/code&gt; and &lt;code&gt;DEPLOY_DIR&lt;/code&gt;. In my case &lt;code&gt;DEPLOY_DIR&lt;/code&gt; comes out to &lt;code&gt;/usr/local/bastille/jails/caddy/root/var/www/www.jaredwolff.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When you do finally get a successful deploy you can double check everything is in the correct place. Then once everything looks good you can start up your caddy server using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bastille service caddy caddy start
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;deploy&lt;/code&gt; will also do something extra handy here. It will deploy my &lt;code&gt;restic&lt;/code&gt; backup script and run it. I'll talk about this more in the backup section.&lt;/p&gt;

&lt;p&gt;All in all, here's the full Makefile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.POSIX&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;

&lt;span class="nv"&gt;HUGO_VERSION&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; 0.66.0

&lt;span class="nv"&gt;OPTIMIZED_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; optimized
&lt;span class="nv"&gt;CONTENT_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; content
&lt;span class="nv"&gt;DEST_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; public

&lt;span class="nv"&gt;SERVER&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; 155.138.230.8
&lt;span class="nv"&gt;USER&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; root

&lt;span class="c"&gt;# Set the place where it's deployed to.
&lt;/span&gt;&lt;span class="k"&gt;ifdef&lt;/span&gt; &lt;span class="nv"&gt;PRODUCTION&lt;/span&gt;
&lt;span class="err"&gt;$(info&lt;/span&gt; &lt;span class="err"&gt;Building&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;production.&lt;/span&gt; &lt;span class="err"&gt;🚀)&lt;/span&gt;
&lt;span class="nv"&gt;TARGET&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; www
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="err"&gt;$(info&lt;/span&gt; &lt;span class="err"&gt;Building&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;development.&lt;/span&gt; &lt;span class="err"&gt;🚀)&lt;/span&gt;
&lt;span class="nv"&gt;BASEURL&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nt"&gt;--baseURL&lt;/span&gt; &lt;span class="s2"&gt;"https://stage.jaredwolff.com"&lt;/span&gt;
&lt;span class="nv"&gt;TARGET&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; stage
&lt;span class="k"&gt;endif&lt;/span&gt;

&lt;span class="c"&gt;# Full path
&lt;/span&gt;&lt;span class="nv"&gt;DEPLOY_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; /usr/local/bastille/jails/caddy/root/var/www/&lt;span class="nv"&gt;$(TARGET)&lt;/span&gt;.jaredwolff.com

&lt;span class="nv"&gt;IMAGES&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.jpg&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.JPG&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.jpeg&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.png&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.jpg&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.jpeg&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.png&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wildcard&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;/images/&lt;span class="k"&gt;*&lt;/span&gt;.JPG&lt;span class="nf"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;

&lt;span class="nv"&gt;OPTIMIZED_IMAGES&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nf"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;subst&lt;/span&gt; &lt;span class="nv"&gt;$(CONTENT_DIR)&lt;/span&gt;/,&lt;span class="nv"&gt;$(OPTIMIZED_DIR)&lt;/span&gt;/,&lt;span class="nv"&gt;$(IMAGES)&lt;/span&gt;&lt;span class="nf"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;
&lt;span class="nl"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build optimize&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;clean&lt;/span&gt;
&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;rm&lt;/span&gt; &lt;span class="err"&gt;-rf&lt;/span&gt; &lt;span class="err"&gt;public/&lt;/span&gt;
&lt;span class="err"&gt;rm&lt;/span&gt; &lt;span class="err"&gt;-rf&lt;/span&gt; &lt;span class="err"&gt;optimized/&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;serve&lt;/span&gt;
&lt;span class="nl"&gt;serve&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;@hugo&lt;/span&gt; &lt;span class="err"&gt;serve&lt;/span&gt; &lt;span class="err"&gt;-D&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;ssh&lt;/span&gt;
&lt;span class="nl"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;@ssh&lt;/span&gt; &lt;span class="err"&gt;$(USER)@$(SERVER)&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;
&lt;span class="nl"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="s2"&gt;"🍳 Generating site"&lt;/span&gt;
&lt;span class="err"&gt;hugo&lt;/span&gt; &lt;span class="err"&gt;--gc&lt;/span&gt; &lt;span class="err"&gt;--minify&lt;/span&gt; &lt;span class="err"&gt;-d&lt;/span&gt; &lt;span class="err"&gt;$(DEST_DIR)&lt;/span&gt; &lt;span class="err"&gt;$(BASEURL)&lt;/span&gt;
&lt;span class="err"&gt;rsync&lt;/span&gt; &lt;span class="err"&gt;-av&lt;/span&gt; &lt;span class="err"&gt;--del&lt;/span&gt; &lt;span class="err"&gt;-f&lt;/span&gt;&lt;span class="s2"&gt;"+ */"&lt;/span&gt; &lt;span class="err"&gt;-f&lt;/span&gt;&lt;span class="s2"&gt;"- *"&lt;/span&gt; &lt;span class="err"&gt;$(CONTENT_DIR)/&lt;/span&gt; &lt;span class="err"&gt;$(OPTIMIZED_DIR)/&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;optimize&lt;/span&gt;
&lt;span class="nl"&gt;optimize&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build $(OPTIMIZED_IMAGES)&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="s2"&gt;"🧂 Optimizing images"&lt;/span&gt;
&lt;span class="err"&gt;rsync&lt;/span&gt; &lt;span class="err"&gt;-r&lt;/span&gt; &lt;span class="err"&gt;$(OPTIMIZED_DIR)/&lt;/span&gt; &lt;span class="err"&gt;$(DEST_DIR)/&lt;/span&gt;
&lt;span class="err"&gt;du&lt;/span&gt; &lt;span class="err"&gt;-sh&lt;/span&gt; &lt;span class="err"&gt;$(CONTENT_DIR)/&lt;/span&gt;
&lt;span class="err"&gt;du&lt;/span&gt; &lt;span class="err"&gt;-sh&lt;/span&gt; &lt;span class="err"&gt;$(DEST_DIR)/&lt;/span&gt;

&lt;span class="nl"&gt;$(OPTIMIZED_IMAGES)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;convert&lt;/span&gt; &lt;span class="err"&gt;-strip&lt;/span&gt; &lt;span class="err"&gt;-compress&lt;/span&gt; &lt;span class="err"&gt;JPEG&lt;/span&gt; &lt;span class="err"&gt;-resize&lt;/span&gt; &lt;span class="s1"&gt;'730&amp;gt;'&lt;/span&gt; &lt;span class="err"&gt;$(subst&lt;/span&gt; &lt;span class="err"&gt;$(OPTIMIZED_DIR)/,$(CONTENT_DIR)/,$@)&lt;/span&gt; &lt;span class="err"&gt;$@&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;deploy&lt;/span&gt;
&lt;span class="nl"&gt;deploy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="err"&gt;rsync&lt;/span&gt; &lt;span class="err"&gt;to&lt;/span&gt; &lt;span class="err"&gt;$(DEPLOY_DIR)&lt;/span&gt;
&lt;span class="nl"&gt;@rsync -r --del public/ $(USER)@$(SERVER)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;$(DEPLOY_DIR)/&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="err"&gt;making&lt;/span&gt; &lt;span class="err"&gt;restic&lt;/span&gt; &lt;span class="err"&gt;snapshot&lt;/span&gt;
&lt;span class="nl"&gt;@scp scripts/backup.sh $(USER)@$(SERVER)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;/root/backup.sh&lt;/span&gt;
&lt;span class="err"&gt;@ssh&lt;/span&gt; &lt;span class="err"&gt;$(USER)@$(SERVER)&lt;/span&gt; &lt;span class="err"&gt;sh&lt;/span&gt; &lt;span class="err"&gt;/root/backup.sh&lt;/span&gt; &lt;span class="err"&gt;$(DEPLOY_DIR)&lt;/span&gt;
&lt;span class="err"&gt;@echo&lt;/span&gt; &lt;span class="s2"&gt;"🚀 Site is deployed!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There are a few other handy nuggets in there you may want to use.  &lt;code&gt;clean&lt;/code&gt;, &lt;code&gt;serve&lt;/code&gt; and &lt;code&gt;ssh&lt;/code&gt; have been very helpful when testing and connecting.&lt;/p&gt;

&lt;p&gt;In the end you'll have a two step deploy process. The first generates your site with optimized images. The second is deploying to a server for static hosting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incremental Backup
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TULOcLwH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Backup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TULOcLwH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/my-latest-self-hosted-hugo-workflow/images/Backup.png" alt="images/Backup.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After discovering &lt;a href="https://restic.net"&gt;Restic&lt;/a&gt; i've been sold on how handy it has been for all my incremental backup needs. In the case of my server, i'm using to back up the root folder of my site. That way, if I need to roll back, I can do so with a few short steps.&lt;/p&gt;

&lt;p&gt;Here's how you can set up a local &lt;code&gt;restic&lt;/code&gt; repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting it up
&lt;/h3&gt;

&lt;p&gt;Initializing the repo is simple. The most important part is making sure you &lt;strong&gt;don't lose/forget your password!&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    # restic init -r /root/backups
    enter password for new repository:
    enter password again:
    created restic repository 32e14c7052 at /root/backups

    Please note that knowledge of your password is required to access
    the repository. Losing your password means that your data is
    irrecoverably lost.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Set the &lt;code&gt;RESTIC_PASSWORD&lt;/code&gt; environment variable to avoid entering your password. To make it permanent you'll have to place &lt;code&gt;export RESTIC_PASSWORD="Your password here!"&lt;/code&gt; within the &lt;code&gt;.profile&lt;/code&gt; file in &lt;code&gt;/root/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backing Up
&lt;/h3&gt;

&lt;p&gt;Invoking &lt;code&gt;restic&lt;/code&gt; over SSH is tough. So our next best bet?&lt;/p&gt;

&lt;p&gt;Transfer a (very brief) shell script to the server and run it after a deploy. Here's the contents of what I'm using today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RESTIC_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Your password here!"&lt;/span&gt;
restic backup &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /root/backups/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;Side note:&lt;/em&gt;&lt;/strong&gt; As I sit here and look at this script, for security reasons you can replace "Your password here!" with $2 which is the second argument to the script. That way you don't need to commit/push the password stored in a static file!&lt;/p&gt;

&lt;p&gt;This first sets your backup password. Then it runs &lt;code&gt;restic&lt;/code&gt; using the first command line argument as the path. So, to run a backup with this script, it would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./backup.sh /path/to/your/public/folder/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; you do need to initialize your &lt;code&gt;restic&lt;/code&gt; backup &lt;em&gt;before&lt;/em&gt; you start backing up. It will barf at you otherwise!&lt;/p&gt;

&lt;p&gt;In my case i'm placing the incremental backups on a different folder of my machine. That way they're easily accessible and &lt;em&gt;fast&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Viewing your backups
&lt;/h3&gt;

&lt;p&gt;To view your backups you can run the following command:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# restic snapshots -r /root/backups -g paths -c
enter password for repository:
repository e140b5e4 opened successfully, password is correct
snapshots for (paths [/usr/local/bastille/jails/caddy/root/var/www/www.jaredwolff.com]):
ID        Time                 Host         Tags
--------------------------------------------------
d3328066  2020-03-10 00:30:58  vultr.guest
f3360819  2020-03-10 04:03:03  vultr.guest
231dd134  2020-03-10 04:44:00  vultr.guest
3c1be26a  2020-03-10 04:56:19  vultr.guest
e96c947c  2020-03-10 05:03:00  vultr.guest
34c3682a  2020-03-10 14:01:37  vultr.guest
fbccdb8c  2020-03-10 14:04:26  vultr.guest
9ce11146  2020-03-10 15:38:49  vultr.guest
046b3da3  2020-03-10 15:47:06  vultr.guest
9c28d4bc  2020-03-10 15:48:25  vultr.guest
469dc228  2020-03-10 15:48:54  vultr.guest
6f78af72  2020-03-10 17:00:21  vultr.guest
29ad17b2  2020-03-10 20:18:23  vultr.guest
ed22ce1f  2020-03-10 20:20:24  vultr.guest
9c8c1b03  2020-03-11 13:56:40  vultr.guest
b6cfcfec  2020-03-11 14:08:14  vultr.guest
e8546005  2020-03-11 14:27:22  vultr.guest
49a134fe  2020-03-17 00:47:58  vultr.guest
c0beb283  2020-03-18 20:44:52  vultr.guest
--------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can use this list to determine if you need to roll back a deploy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Restoring
&lt;/h3&gt;

&lt;p&gt;Restoring from a backup, especially in a live environment needs to be quick. After viewing your backups you can restore a specific backup by using its &lt;em&gt;ID&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;restic restore d3328066
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will restore the files back to the backup made on &lt;em&gt;2020-03-10 00:30:58.&lt;/em&gt; Awesome. Plus it won't overwrite every single. It will only apply the differences from the current state and the stored state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've covered a ton of ground in this post. You've learned how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy your own server using Vultr&lt;/li&gt;
&lt;li&gt;Use Bastille to create Container-like Jails&lt;/li&gt;
&lt;li&gt;Set up Caddy to serve static file assets with TLS&lt;/li&gt;
&lt;li&gt;Deploy the files using a fairly simple Makefile and &lt;code&gt;rsync&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Back up after every deploy using &lt;code&gt;restic&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end we have a robust, secure and simple platform for hosting static files and services. Stay tuned as there are more posts like this coming your way soon! In the meantime check out my &lt;a href="https://www.jaredwolff.com/blog/"&gt;other posts.&lt;/a&gt; Thanks for reading and see you next time! 👍&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>freebsd</category>
    </item>
    <item>
      <title>Meet Pyrinas a New Way to Use Your Xenon</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Tue, 10 Mar 2020 19:29:49 +0000</pubDate>
      <link>https://dev.to/jaredwolff/meet-pyrinas-a-new-way-to-use-your-xenon-27dm</link>
      <guid>https://dev.to/jaredwolff/meet-pyrinas-a-new-way-to-use-your-xenon-27dm</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cnTaoRgV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Copy_of_Particle_Mesh_App_Updates.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cnTaoRgV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Copy_of_Particle_Mesh_App_Updates.jpg" alt="images/Copy_of_Particle_Mesh_App_Updates.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post is a bit lengthy. If you prefer, &lt;a href="https://www.jaredwolff.com/files/pyrinas-intro/"&gt;signup to get the full PDF here.&lt;/a&gt; 📖&lt;/p&gt;

&lt;p&gt;Talk about how &lt;a href="https://www.jaredwolff.com/particle-mesh-deprecation-livestream/"&gt;Particle's Mesh deprecation announcement&lt;/a&gt;, many have been left to figure out how to deploy their low power sensor networks. There was always the option of using &lt;a href="https://www.jaredwolff.com/how-to-use-particles-powerful-bluetooth-api/"&gt;Particle's Built in Bluetooth stack&lt;/a&gt; but as it stands today it's not secure.&lt;/p&gt;

&lt;p&gt;Previously I had helped form a very simple nRF SDK based hub and spoke IoT deployment. Unfortunately, it was closed source and the company is no longer around.&lt;/p&gt;

&lt;p&gt;So what's a guy to do?&lt;/p&gt;

&lt;p&gt;Build another one and make it &lt;em&gt;open. (BSD licensed to be exact!)&lt;/em&gt; Open and free for anyone to use adopt and improve upon. Plus if you're building a product that's using the code, &lt;strong&gt;you don't have to share your improvements or proprietary code with anyone.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this post i'll be talking about how to get started with Pyrinas. It uses Nordic's time tested SDK as a basis for the kernel of the system. The main concept of Pyrinas is to abstract as much IoT cruft away so you can focus on your application.&lt;/p&gt;

&lt;p&gt;So without further ado, let's chat about what Pyrinas is and what it isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Pyrinas is
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Is an embedded "kernel", written in C. It's open and permissive IoT development environment you can use for anything you want. Seriously. It's BSD licensed and can be used in closed source applications.&lt;/li&gt;
&lt;li&gt;Using the power of Bluetooth 5.0 Long Range, Pyrinas allows you to communicate with many peripheral devices at one time. Currently Pyrinas has been tested with 3 simultaneous peripheral connections. Theoretically, it can support up to 20 simultaneous connections. (Thanks to &lt;a href="https://cm.nordicsemi.com//Software-and-tools/Software/Bluetooth-Software"&gt;Nordic's S140 Softdevice&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Pyrinas transports its data in two ways

&lt;ul&gt;
&lt;li&gt;In a familiar string format used by Particle&lt;/li&gt;
&lt;li&gt;A custom Protocol Buffer for raw data transmission.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That way you have a &lt;strong&gt;choice&lt;/strong&gt; of how you want to process and publish your data!&lt;/p&gt;

&lt;h2&gt;
  
  
  What Pyrinas isn't
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pyrinas is not a RTOS (real time operating system). If you have a need to run multiple threaded applications on embedded, Pyrinas is not for you.&lt;/li&gt;
&lt;li&gt;Pyrinas, at this time, does not support Mesh.&lt;/li&gt;
&lt;li&gt;An OS for every single kind of Bluetooth SoC on the market. Due to the tight coupling with Nordic's nRF SDK, Pyrinas only works with Nordic's SoCs.&lt;/li&gt;
&lt;li&gt;A turnkey solution for IoT. Pyrinas is early on it it's development process. The aim is for it to become a viable option for anyone to develop and publish IoT applications &lt;em&gt;the way they want to.&lt;/em&gt; There's no vendor lock in. There's no surprises.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Things you'll need
&lt;/h2&gt;

&lt;p&gt;There are a few things you'll need in order to get going with Pyrinas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At least 2 Particle Xenons&lt;/li&gt;
&lt;li&gt;At least 1 nRF Development board or J-link programmer&lt;/li&gt;
&lt;li&gt;Associated USB cables&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started with an example
&lt;/h2&gt;

&lt;p&gt;Getting started with Pyrinas involves two repositories.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pyrinas-iot/pyrinas-os"&gt;The OS respository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pyrinas-iot/pyrinas-template"&gt;The template&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The OS directory has all the source, SDK dependencies and toolchain you need to use Pyrinas.&lt;/p&gt;

&lt;p&gt;The template is where you add all your application code goes. The template provides a starting point for you and your project.&lt;/p&gt;

&lt;p&gt;Here's how everything goes together:&lt;/p&gt;

&lt;p&gt;Clone the OS directory to some place on your machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/pyrinas-iot/pyrinas-os.git &lt;span class="nt"&gt;--recurse-submodules&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once complete, change directories to &lt;code&gt;pyrinas-os&lt;/code&gt; and run &lt;code&gt;make setup&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;pyrinas-os
make setup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will download your toolchain and SDK dependencies.&lt;/p&gt;

&lt;p&gt;In order to use OTA DFU, you will need to also generate the DFU key for the process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;make gen_key
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The files generated by this process will be used later.&lt;/p&gt;

&lt;p&gt;Next, we'll want to use the template to make two new projects. In this example we'll have one "hub" and one "sensor." Simply navigate to the &lt;a href="https://github.com/pyrinas-iot/pyrinas-template"&gt;template repository&lt;/a&gt; and click the &lt;strong&gt;Use this template&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PmemNMul--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Screen_Shot_2020-03-09_at_5.12.21_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PmemNMul--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Screen_Shot_2020-03-09_at_5.12.21_PM.png" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.12.21_PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then name your new repository. Click the &lt;strong&gt;Create repository from template&lt;/strong&gt; button when you're happy with everything.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aQFoE_mR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Screen_Shot_2020-03-09_at_5.20.15_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aQFoE_mR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Screen_Shot_2020-03-09_at_5.20.15_PM.png" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.20.15_PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then clone your repository to the same directory as &lt;code&gt;pyrinas-os&lt;/code&gt;. Make sure you replace &lt;code&gt;&amp;lt;your username&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;repo name&amp;gt;&lt;/code&gt; with your own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
git clone https://github.com/&amp;lt;your username&amp;gt;/&amp;lt;repo name&amp;gt;.git hub
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After this is done. Go back and create a new repository from the template. We'll be using this one for the &lt;em&gt;sensor&lt;/em&gt; node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0RoY6XaX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Screen_Shot_2020-03-09_at_5.24.15_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0RoY6XaX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Screen_Shot_2020-03-09_at_5.24.15_PM.png" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.24.15_PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clone this repository once you're done setting it up in the same place as your &lt;code&gt;hub&lt;/code&gt; and &lt;code&gt;pyrinas-os&lt;/code&gt; repositories.&lt;/p&gt;

&lt;p&gt;Now that we have all our repositories, let's start with our sensor node.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the sensor node repository
&lt;/h3&gt;

&lt;p&gt;Open up the sensor repository using a program like Microsofts VS Code. If you have the command line shortcuts you can use &lt;code&gt;code&lt;/code&gt; to open it from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;code sensor
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Before we do anything we'll need to set up the symbolic link to &lt;code&gt;pyrinas-os&lt;/code&gt;. Make sure you're in the &lt;code&gt;sensor&lt;/code&gt; directory and then run &lt;code&gt;ln -s ../pyrinas-os/&lt;/code&gt; using the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;sensor
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; ../pyrinas-os/ &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This allows your project to use all the code, SDK and toolchains within the &lt;code&gt;pyrinas-os&lt;/code&gt; repository! As an added bonus you can do this as many times as you want. Have multiple Pyrinas projects? No problem.&lt;/p&gt;

&lt;p&gt;Alright! Now, let's check out the Makefile. You'll want to customize some of the definitions within the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start: Your configuration!
&lt;/span&gt;
&lt;span class="c"&gt;# Set this to the directory of pyrinas-os
# If you used a symbolic link this points to
# the `pyrinas-os` folder in this repository
&lt;/span&gt;&lt;span class="nv"&gt;OS_DIR&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; pyrinas-os

&lt;span class="c"&gt;# This should be the serial number of your Jlink programmer.
# To find simply run `jlinkexe`
&lt;/span&gt;&lt;span class="nv"&gt;PROG_SERIAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1234678

&lt;span class="c"&gt;# This is your debugger port for Jlink's RTT. If you
# have mulitple, you will have to change this on each app
# your're using
&lt;/span&gt;&lt;span class="nv"&gt;PROG_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;19021

&lt;span class="c"&gt;# This is where you set your board type. Here are the supported boards:
# xenon - Particle Xenon
&lt;/span&gt;&lt;span class="nv"&gt;BOARD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xenon

&lt;span class="c"&gt;# This is where you can name your app something. Make it specific
&lt;/span&gt;&lt;span class="nv"&gt;APP_FILENAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pyrinas-template

&lt;span class="c"&gt;# This determines whether or not you're using debug mode
# Comment this out or change to false
&lt;/span&gt;&lt;span class="nv"&gt;DEBUG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# End: Your Configuration
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;For example, you may want to setup your programmer serial. This allows you to use  multiple programmers at the same time. (Very helpful in debugging both devices at the same time) To get your programmer's serial plug in your development board and run &lt;code&gt;jlinkexe&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jlinkexe
SEGGER J-Link Commander V6.62a (Compiled Jan 31 2020 12:59:22)
DLL version V6.62a, compiled Jan 31 2020 12:59:05

Connecting to J-Link via USB...O.K.
Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Jan 21 2020 17:30:48
Hardware version: V1.00
S/N: 581758669
License(s): RDI, FlashBP, FlashDL, JFlash, GDB
VTref=3.300V


Type "connect" to establish a target connection, '?' for help
J-Link&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Find the &lt;strong&gt;S/N&lt;/strong&gt; area. This is your serial number!&lt;/p&gt;

&lt;p&gt;Alternatively you can look at the sticker on your development kit. It will contain the serial number for your device.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;PROG_PORT&lt;/code&gt; you want to use different ports for each device you're simultaneously debugging. I've found &lt;strong&gt;19021&lt;/strong&gt; and &lt;strong&gt;19020&lt;/strong&gt; are perfectly good options for most two-device debugging sessions.&lt;/p&gt;

&lt;p&gt;The Makefile also includes the ability to choose a board. In our case there's only one option: &lt;code&gt;xenon&lt;/code&gt;. Future revisions of Pyrinas will have multiple options.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;APP_FILENAME&lt;/code&gt; is the name of your app. We'll rename ours to &lt;code&gt;pyrinas-sensor&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;DEBUG&lt;/code&gt; is used to either halt execution on an error or restart. For production this should be commented out or set to &lt;code&gt;false&lt;/code&gt;. We can leave it as is for now.&lt;/p&gt;

&lt;p&gt;The Makefile is also the source for some of the most important commands you'll need during development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;make build&lt;/code&gt; - builds your app.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make clean&lt;/code&gt; - cleans all remnants of your app.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make debug&lt;/code&gt; - opens up the &lt;code&gt;jlinkexe&lt;/code&gt; debugger console.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make erase&lt;/code&gt; - erases the chip attached to your programmer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make flash&lt;/code&gt; - flashes the current app to your connected device.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make flash_softdevice&lt;/code&gt; - flashes the soft_device&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make rtt&lt;/code&gt; - opens up the debug console.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make ota&lt;/code&gt; - generates a zip file used for BLE DFU&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Basic sensor node code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jds4ibqK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Copy_of_Particle_Mesh_App_Updates-3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jds4ibqK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Copy_of_Particle_Mesh_App_Updates-3.jpg" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-3.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have some of the basics out of the way, let's create a very simple application that publishes on a set interval. If you look at &lt;code&gt;app.c&lt;/code&gt;, you'll see some code in the &lt;code&gt;setup()&lt;/code&gt; function. Let's delete all the commented out code. (We'll use it later for the hub though)&lt;/p&gt;

&lt;p&gt;Your code should now look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include "app.h"
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;BLE_STACK_PERIPH_DEF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Configuration for ble stack&lt;/span&gt;
  &lt;span class="n"&gt;ble_stack_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Start advertising&lt;/span&gt;
  &lt;span class="n"&gt;advertising_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now let's create a timer that we'll use to publish on a set interval. Under &lt;code&gt;#include "app.h"&lt;/code&gt; create a new timer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include "app.h"
&lt;/span&gt;
&lt;span class="n"&gt;timer_define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m_sensor_timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We also need to set it up in the &lt;code&gt;setup()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Sensor timer&lt;/span&gt;
&lt;span class="n"&gt;timer_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m_sensor_timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TIMER_REPEATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sensor_timer_evt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll notice that &lt;code&gt;timer_create&lt;/code&gt; is referring to a event callback called &lt;code&gt;sensor_timer_evt&lt;/code&gt;. We'll need to create that guy as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sensor_timer_evt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// We'll come back in a sec&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The last thing is to start the timer. Let's do that underneath &lt;code&gt;timer_create&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Start&lt;/span&gt;
&lt;span class="n"&gt;timer_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m_sensor_timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will start our repeating timer on a 1 second interval.  (&lt;code&gt;timer_start&lt;/code&gt; is configured using milliseconds)&lt;/p&gt;

&lt;p&gt;Now, inside &lt;code&gt;sensor_timer_evt&lt;/code&gt; we'll publish some data. First though we need to make sure that Bluetooth is connected using &lt;code&gt;ble_is_connected&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;sensor_timer_evt&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check if we're connected&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ble_is_connected&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Sends "ping" with the event name of "data"&lt;/span&gt;
    &lt;span class="n"&gt;ble_publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ping"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Inside the if statement, we'll use &lt;code&gt;ble_publish&lt;/code&gt;. The first argument is the name of the event and the second is the value.&lt;/p&gt;

&lt;p&gt;Next, in order to receive messages from the other end we'll need to setup a callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Configuration for ble stack&lt;/span&gt;
&lt;span class="n"&gt;ble_stack_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Setup BLE callback&lt;/span&gt;
&lt;span class="n"&gt;ble_subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ble_evt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll define &lt;code&gt;ble_evt&lt;/code&gt; at the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ble_evt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;NRF_LOG_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this case we'll use &lt;code&gt;NRF_LOG_INFO&lt;/code&gt; to print out the message from the hub.&lt;/p&gt;

&lt;p&gt;Finally, in order to get the MAC address easily, we'll have to add a call to print it out in &lt;code&gt;setup()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Print the address&lt;/span&gt;
&lt;span class="n"&gt;util_print_device_address&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the end your file should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include "app.h"
&lt;/span&gt;
&lt;span class="n"&gt;timer_define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m_sensor_timer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Catch events sent over Bluetooth&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ble_evt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;NRF_LOG_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sensor_timer_evt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check if we're connected&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ble_is_connected&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Sends "ping" with the event name of "data"&lt;/span&gt;
    &lt;span class="n"&gt;ble_publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ping"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;BLE_STACK_PERIPH_DEF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Configuration for ble stack&lt;/span&gt;
  &lt;span class="n"&gt;ble_stack_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Setup BLE callback&lt;/span&gt;
  &lt;span class="n"&gt;ble_subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ble_evt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Start advertising&lt;/span&gt;
  &lt;span class="n"&gt;advertising_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Sensor sensor timer.&lt;/span&gt;
  &lt;span class="n"&gt;timer_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m_sensor_timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TIMER_REPEATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sensor_timer_evt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Start&lt;/span&gt;
  &lt;span class="n"&gt;timer_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m_sensor_timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Print the address&lt;/span&gt;
  &lt;span class="n"&gt;util_print_device_address&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next, we'll program it to some hardware!&lt;/p&gt;

&lt;h3&gt;
  
  
  Flashing the basic sensor code:
&lt;/h3&gt;

&lt;p&gt;For this step you'll need to have a Xenon handy. You'll also need a programmer, programming cable and two Micro-B USB cables. Here's a picture of everything connected:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D8PKr7k0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/IMG_4586.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D8PKr7k0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/IMG_4586.jpg" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//IMG_4586.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once connected and powered run these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;make erase
make flash_softdevice
make flash
make debug
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then in a separate terminal window run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;make rtt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;make debug&lt;/code&gt; and &lt;code&gt;make rtt&lt;/code&gt; will create a debugging session. You can issue commands in the &lt;code&gt;make debug&lt;/code&gt; terminal to control the device as well. For instance, &lt;code&gt;r&lt;/code&gt; followed by &lt;code&gt;Enter&lt;/code&gt; will restart the device. (By far my most common use case).&lt;/p&gt;

&lt;p&gt;If you've flashed everything successfully, your device should start blinking green. That's a good sign!&lt;/p&gt;

&lt;p&gt;Additionally, if you take a look at the &lt;code&gt;make rtt&lt;/code&gt; side your output should be similar to this:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;###RTT Client: Connecting to J-Link RTT Server via localhost:19021 ...
###RTT Client: Connected.

SEGGER J-Link V6.62a - Real time terminal output
J-Link OB-SAM3U128-V2-NordicSemi compiled Jan 21 2020 17:30:48 V1.0, SN=581758669
Process: JLinkExe
&amp;lt;info&amp;gt; app_timer: RTC: initialized.
&amp;lt;info&amp;gt; app: Boot count: 4
&amp;lt;info&amp;gt; app: Pyrinas started.
&amp;lt;info&amp;gt; app: Address: 11:22:33:44:55:66
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Take note of the address displayed above. We'll need that for the hub code!&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the hub repository
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_MfVKBju--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Copy_of_Particle_Mesh_App_Updates-2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_MfVKBju--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Copy_of_Particle_Mesh_App_Updates-2.jpg" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-2.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you haven't already, clone your hub repository locally. We'll want to do some of the same steps as we did with the sensor repo like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up the symbolic link&lt;/li&gt;
&lt;li&gt;Updating the Makefile

&lt;ul&gt;
&lt;li&gt;Setting your &lt;code&gt;PROG_SERIAL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Setting &lt;code&gt;PROG_PORT&lt;/code&gt; to a port not used by the sensor setup. &lt;code&gt;19020&lt;/code&gt; in this case is fine.&lt;/li&gt;
&lt;li&gt;Setting &lt;code&gt;APP_FILENAME&lt;/code&gt; to &lt;code&gt;pyrinas-hub&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need a reminder how any of these steps work, go back and review the earlier section.&lt;/p&gt;

&lt;p&gt;Next, we'll want to open &lt;code&gt;app.c&lt;/code&gt; and uncomment the central/hub based code. Plus you'll want to remove the default un-commented code. Your &lt;code&gt;setup()&lt;/code&gt; should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Default config for central mode&lt;/span&gt;
  &lt;span class="n"&gt;BLE_STACK_CENTRAL_DEF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Add an addresses to scan for&lt;/span&gt;
  &lt;span class="n"&gt;ble_gap_addr_t&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_GAP_ADDR_TYPE_RANDOM_STATIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x81&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x4C&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x7D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xC0&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
  &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;ble_gap_addr_t&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_GAP_ADDR_TYPE_RANDOM_STATIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x7c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x84&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x9d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xe4&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
  &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Increment the device_count&lt;/span&gt;
  &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Configuration for ble stack&lt;/span&gt;
  &lt;span class="n"&gt;ble_stack_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Start scanning.&lt;/span&gt;
  &lt;span class="n"&gt;scan_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll notice immediately that there are two clients/devices defined here. Let's remove the second one. Should you, in the future, want to connect more devices this is an example of how to do it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reminder:&lt;/strong&gt; also make sure that you change the &lt;code&gt;init.config.device_count&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, you'll want to update the &lt;code&gt;.addr&lt;/code&gt; field in &lt;code&gt;ble_gap_addr_t first&lt;/code&gt; to match the address we got earlier from &lt;code&gt;make rtt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Add an addresses to scan for&lt;/span&gt;
&lt;span class="n"&gt;ble_gap_addr_t&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_GAP_ADDR_TYPE_RANDOM_STATIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mh"&gt;0x66&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The address field uses raw bytes, so it has to be represented that way. Remove the &lt;code&gt;:&lt;/code&gt; and place &lt;code&gt;0x&lt;/code&gt; in front of each byte. We end up going from &lt;code&gt;11:22:33:44:55:66&lt;/code&gt; to &lt;code&gt;{0x11,0x22,0x33,0x44,0x55,0x66}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now before we flash anything, let's also set up the Bluetooth event handler. As with earlier we'll use &lt;code&gt;ble_subscribe&lt;/code&gt; to attach an event handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Setup BLE callback&lt;/span&gt;
&lt;span class="n"&gt;ble_subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ble_evt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Then&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;

&lt;span class="c1"&gt;// Catch events sent over Bluetooth&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ble_evt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;NRF_LOG_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;ble_publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pong"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll notice we're printing out the message using &lt;code&gt;NRF_LOG_INFO&lt;/code&gt;. We're also sending a message &lt;em&gt;back&lt;/em&gt; to the sensor in the form of &lt;code&gt;ble_publish("data","pong");&lt;/code&gt; In other-words we're playing a game of ping-pong between the two devices!&lt;/p&gt;

&lt;p&gt;In the end your code should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include "app.h"
&lt;/span&gt;
&lt;span class="c1"&gt;// Catch events sent over Bluetooth&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ble_evt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;NRF_LOG_INFO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;ble_publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pong"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Default config for central mode&lt;/span&gt;
  &lt;span class="n"&gt;BLE_STACK_CENTRAL_DEF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Add an addresses to scan for&lt;/span&gt;
  &lt;span class="n"&gt;ble_gap_addr_t&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BLE_GAP_ADDR_TYPE_RANDOM_STATIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mh"&gt;0x11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x44&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x66&lt;/span&gt;&lt;span class="p"&gt;}};&lt;/span&gt;
  &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Increment the device_count&lt;/span&gt;
  &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;device_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Configuration for ble stack&lt;/span&gt;
  &lt;span class="n"&gt;ble_stack_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Setup BLE callback&lt;/span&gt;
  &lt;span class="n"&gt;ble_subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ble_evt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Start scanning.&lt;/span&gt;
  &lt;span class="n"&gt;scan_start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reminder:&lt;/strong&gt; make sure you set &lt;code&gt;ble_gap_addr_t first&lt;/code&gt; or the two devices will not connect!&lt;/p&gt;

&lt;p&gt;To program, connect the Xenon to program as you did before. We'll flash it using the same methods as before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;make erase
make flash_softdevice
make flash
make debug
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then in a separate terminal window run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;make rtt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then take a look at each of the &lt;code&gt;make rtt&lt;/code&gt; screens. There should be some output! For the hub it should look something like this:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process: JLinkExe
&amp;lt;info&amp;gt; app: Boot count: 4
&amp;lt;info&amp;gt; app: Pyrinas started.
&amp;lt;info&amp;gt; ble_m_central: Connected to handle 0x0
&amp;lt;info&amp;gt; ble_m_central: Protobuf Service discovered
&amp;lt;info&amp;gt; app: data: ping
&amp;lt;info&amp;gt; app: data: ping
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And the sensor side like this:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process: JLinkExe
&amp;lt;info&amp;gt; app_timer: RTC: initialized.
&amp;lt;info&amp;gt; app: Boot count: 4
&amp;lt;info&amp;gt; app: Pyrinas started.
&amp;lt;info&amp;gt; app: Address: 11:22:33:44:55:66
&amp;lt;info&amp;gt; ble_m_periph: Notifications enabled!
&amp;lt;info&amp;gt; app: data: pong
&amp;lt;info&amp;gt; app: data: pong
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The pin and pong messages should continue until you stop them. Awesome! If you get any warnings like this one:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unable to write. Notifications not enabled!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Use the reset button on either of the devices. This should fix the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; the pairing process for Bluetooth is inherently &lt;strong&gt;&lt;em&gt;insecure&lt;/em&gt;&lt;/strong&gt;. Once the pairing process is complete though, the devices are secure. (With the caveat that no one was sniffing the pairing process!) There may be improvements on security in the future.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Congrats! 🎉&lt;/strong&gt;If you've made it this far, you've deployed your first Pyrinas hub and sensor client! For more information about what Pyrinas can do you should checkout the header files under &lt;code&gt;pyrinas-os/include/&lt;/code&gt;. Also, Pyrinas can do anything that you'd normally be able to do with Nordic's SDK. &lt;a href="https://infocenter.nordicsemi.com/topic/struct_sdk/struct/sdk_nrf5_latest.html?cp=7_1"&gt;Nordic's Infocenter&lt;/a&gt; is a great resource for learning more about what the SDK has to offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the future hold for Pyrinas?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u0yzCna---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Copy_of_Particle_Mesh_App_Updates-5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u0yzCna---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images/Copy_of_Particle_Mesh_App_Updates-5.jpg" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-5.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All future tasks for Pyrinas is shared openly on the &lt;a href="https://github.com/pyrinas-iot/pyrinas-os/projects"&gt;Github Repo.&lt;/a&gt; Here are some of the high level improvements on the roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Particle Boron + LTE support - That's right! Cellular will be coming to Pyrinas. As of this writing, the first board to support Pyrinas LTE will be Particle's Boron.&lt;/li&gt;
&lt;li&gt;MQTT (over TLS) and HTTPS support - Once we have cellular, we need something to communicate with. That's where MQTT and HTTPS come in. They're some of the most popular protocols for IoT today.&lt;/li&gt;
&lt;li&gt;Built in remote OTA support - As it stands today, devices programmed with Pyrinas uses Nordic's Secure Bootloader. That means they can be updated over the air by a computer or cellphone nearby. This isn't sustainable for long term deployments though!
Instead, you will be able to push updates to Pyrinas devices over the Cloud. Yup. No reason to get off your couch, you can deploy your updates from anywhere.&lt;/li&gt;
&lt;li&gt;Dynamic configuration and management - adding and removing devices from a Pyrinas system currently takes some effort. In future revisions, it will be easier to add and remove devices on the fly. This allows for remote device management with zero headaches.&lt;/li&gt;
&lt;li&gt;Support for pre-certified modules and other development boards based on Nordic's nRF52840. Currently the Xenon is the only supported board. Development boards aren't great for full production though. Stay tuned for support for pre-certified modules from vendors like &lt;a href="https://www.fanstel.com/bluenor-summaries"&gt;Fanstel&lt;/a&gt; and more..&lt;/li&gt;
&lt;li&gt;Support for more development environments. Currently Pyrinas supports Mac &lt;em&gt;only&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Star and Watch!
&lt;/h3&gt;

&lt;p&gt;This is only the tip of the iceberg! Stay tuned for more updates and make you you star and watch &lt;a href="https://github.com/pyrinas-iot/pyrinas-os"&gt;the repository&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Or, better yet, looking to help out? Contributions are welcome!&lt;/p&gt;

</description>
      <category>particle</category>
      <category>tutorial</category>
      <category>embedded</category>
      <category>iot</category>
    </item>
    <item>
      <title>How to Develop Particle Iot Apps Using NativeScript</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Sun, 26 Jan 2020 14:32:20 +0000</pubDate>
      <link>https://dev.to/jaredwolff/how-to-develop-particle-iot-apps-using-nativescript-24nd</link>
      <guid>https://dev.to/jaredwolff/how-to-develop-particle-iot-apps-using-nativescript-24nd</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xdMfHMp5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-develop-particle-iot-apps-using-nativescript/images/NativeScript__Particle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xdMfHMp5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-develop-particle-iot-apps-using-nativescript/images/NativeScript__Particle.png" alt="Title image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're developing any type of IoT product, inevitably you'll need some type of mobile app. While &lt;a href="https://www.jaredwolff.com/create-a-cross-platform-app-using-blynk/"&gt;there are easy ways&lt;/a&gt;, they're not for production use.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll talk about the basics of Particle app development. You'll learn about some of the many app frameworks you can take advantage of. Plus there's libraries, tricks and tools along the way to make your life a ton easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  App Frameworks
&lt;/h2&gt;

&lt;p&gt;Sometimes it's dang near irritating to program multiple applications natively. You see, Swift (or Objective C 🤮) and Java aren't terrible at first glance (well, maybe except for Obj-C 🤮). But when you're resource constrained, you have to figure out a new game plan. That's where App Frameworks come in.&lt;/p&gt;

&lt;p&gt;These frameworks allow an app developer to write, build and test cross platform apps. In some cases, the frameworks convert your app into native code. That means that they run as fast and as well as one written in Swift or Java.&lt;/p&gt;

&lt;p&gt;I did the research and as of January 2020, here are some of the most supported frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/framework7io/framework7"&gt;Framework7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://flutter.dev/"&gt;Flutter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nativescript.org/"&gt;NativeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/facebook/react-native"&gt;ReactNative&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ionic-team/ionic"&gt;Ionic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cordova.apache.org/"&gt;Cordova&lt;/a&gt; / &lt;a href="https://phonegap.com/"&gt;PhoneGap&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/meteor/meteor"&gt;Meteor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/apps/xamarin"&gt;Xamarin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The list goes on for days.&lt;/p&gt;

&lt;p&gt;I've used a few of these frameworks in the past. I've built a Meteor app which (surprisingly) worked. In the end I had to pick one though. What did I go with?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NativeScript.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the most part, NativeScript's documentation and on-boarding experience is fantastic. Not only can you preview your app inside an emulator but you can load it directly to your phone too!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NHGB0Wi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-develop-particle-iot-apps-using-nativescript/images/Apple_iPhone_6s_Gold_-__status-b1ad9325-8e81-4ee0-b72a-687b62adec29.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NHGB0Wi5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-develop-particle-iot-apps-using-nativescript/images/Apple_iPhone_6s_Gold_-__status-b1ad9325-8e81-4ee0-b72a-687b62adec29.png" alt="images/Apple_iPhone_6s_Gold_-__status-b1ad9325-8e81-4ee0-b72a-687b62adec29.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the cool things about NativeScript is that it supports TypeScript. TypeScript is a superset of Javascript with some extra wiz-bang features. Unlike other languages, Javascript technically has no types. If you've done any Particle development you likely know what a type is. We're talking about &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt; and more. i.e. they're directives to to make sure your Javascript code stays consistent.&lt;/p&gt;

&lt;p&gt;NativeScript is also compatible with most major javascript web frameworks. This includes &lt;a href="https://vuejs.org/"&gt;Vue.Js&lt;/a&gt; and &lt;a href="https://angular.io/"&gt;Angular&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've only noticed one major drawback thus far. The mobile preview mode (&lt;code&gt;tns preview&lt;/code&gt; command) does not pay well with native libraries. If you have some native platform specific libraries, you'll have to use the emulator or a device (if you have one).&lt;/p&gt;

&lt;p&gt;If you're gung-ho and you &lt;em&gt;want&lt;/em&gt; to build multiple apps in their respective languages the more power to you. There is an advantage over the above frameworks: tried and true Particle SDKs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Available Libraries &amp;amp; SDKs
&lt;/h2&gt;

&lt;p&gt;Particle has gone out of their way to make app development a little easier. This is thanks to the massive development work that has gone into their own SDKs. Yup, no more are the days you have to write manual HTTP request handlers.&lt;/p&gt;

&lt;p&gt;Here's a link to both the iOS and Android SDKs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.particle.io/reference/SDKs/ios/"&gt;iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.particle.io/reference/SDKs/android/"&gt;Android&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Though we won't be covering them here, they reflect all the potential calls that you can make using the &lt;a href="https://docs.particle.io/reference/device-cloud/api/"&gt;Cloud API.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Speaking of Cloud API, Particle has also developed a &lt;a href="https://docs.particle.io/reference/device-cloud/api/"&gt;Node.js&lt;/a&gt; library as well. As you can imagine, you can use this for your server side code or Javascript based app frameworks. Sadly, it doesn't  work with NativeScript. Frameworks that use a &lt;a href="https://www.tutorialspoint.com/android/android_webview_layout.htm"&gt;WebView&lt;/a&gt; should be more compatible.&lt;/p&gt;

&lt;p&gt;In the case of this tutorial, we'll be mostly focusing on the Cloud API. This way you have a good understanding of the overall system. It may seem intimidating but if you do it right, you'll get the hang of it real fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making API Calls
&lt;/h2&gt;

&lt;p&gt;In NativeScript you can't use libraries like &lt;code&gt;[request](https://github.com/request/request)&lt;/code&gt;. (Which happens to be the library Particle's very own &lt;a href="https://github.com/dmiddlecamp"&gt;DMC&lt;/a&gt; used in the &lt;a href="https://github.com/particle-iot/particle-cli"&gt;CLI&lt;/a&gt; — DMC if you're reading this, Hi!) You'll have to use the provided &lt;a href="https://docs.nativescript.org/ns-framework-modules/http"&gt;HTTP&lt;/a&gt; module. If you scroll all the way to the &lt;a href="https://docs.nativescript.org/ns-framework-modules/http#http-post"&gt;bottom of that page&lt;/a&gt;, you'll see a fully fledged &lt;code&gt;POST&lt;/code&gt; example.  I'll reproduce it here but with some Particle specific changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create form post data&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;update&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;It's hammer time!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;access_token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Configure the httpModule&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;httpModule&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://api.particle.io/v1/devices/events`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toJSON&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The above is an example of what's equivalent to &lt;code&gt;Particle.publish&lt;/code&gt; in DeviceOS. Let's break down the parts.&lt;/p&gt;

&lt;p&gt;First of all, one of the main gotchas of Particle's Web API is the data format. I has first expected that they use JSON but I was sorely wrong. After actually &lt;em&gt;reading&lt;/em&gt; the documentation I realized that most POST requests were actually &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;. That means when you submit data, it's the equivalent to hitting the submit button on an HTML form.&lt;/p&gt;

&lt;p&gt;Fortunately, there is an easy way to assemble form data in Node/Javascript. We can use the &lt;code&gt;FormData()&lt;/code&gt; object. Take a look at the above. There should be some familiar parameter names in the &lt;code&gt;data.append&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"name"&lt;/code&gt; refers to the name of the event your publishing to.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"data"&lt;/code&gt; refers to the string formatted data that you're publishing.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"private"&lt;/code&gt; dictates whether or not you want to broadcast this data to the whole Particle world, or just your little corner of it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"access_token"&lt;/code&gt; is a token that you can generate in order to make these API calls. Without a token though, you're dead in the water.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting a Token
&lt;/h3&gt;

&lt;p&gt;Where do we get this elusive &lt;code&gt;access_token&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;At first I had no idea.&lt;/p&gt;

&lt;p&gt;I created an OAuth user and secret in the console. That lead to a dead end. Fiddled around with different API calls and settings. Nothing. Then it hit me like a ton of bricks. There's an &lt;code&gt;access_token&lt;/code&gt; attached to the curl request on every device page!&lt;/p&gt;

&lt;p&gt;Open up any device, click the little console button near &lt;em&gt;Events.&lt;/em&gt; A popup with instructions an a URL will pop up. Copy the text after &lt;code&gt;access_token=&lt;/code&gt;. That is your &lt;code&gt;access_token&lt;/code&gt;! See below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s5XdaOoY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-develop-particle-iot-apps-using-nativescript/images/Screen_Shot_2020-01-25_at_8.55.21_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s5XdaOoY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-develop-particle-iot-apps-using-nativescript/images/Screen_Shot_2020-01-25_at_8.55.21_AM.png" alt="images/Screen_Shot_2020-01-25_at_8.55.21_AM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use this token to make calls to the Particle API. This can be to subscribe, publish, write to a function, read variables and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Through the command line
&lt;/h3&gt;

&lt;p&gt;That's nice and everything but how the heck can you &lt;em&gt;programmatically&lt;/em&gt; generate one? One way is with the command line.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;particle token create&lt;/code&gt; is the name of the command you need to know about. When you run it, you'll be prompted to login. (Also enter your Authenticator code if you use one) Then the command line will spit out a shiny new &lt;code&gt;access_token&lt;/code&gt; you can use with the API!&lt;/p&gt;

&lt;h3&gt;
  
  
  Through the API itself
&lt;/h3&gt;

&lt;p&gt;If you couldn't guess, &lt;code&gt;particle token create&lt;/code&gt; is a &lt;a href="https://github.com/particle-iot/particle-cli/blob/20d02afc7b72ade0e79d4f4ec724ec6cce9fff1b/src/lib/api-client.js#L192"&gt;frontend to a raw API call&lt;/a&gt;. You can make these API calls directly too. Here's what it looks like in NativeScript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create form post data&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jaredwolff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is not my password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grant_type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;client_name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;client_secret&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;client_secret_here&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Configure the httpModule&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;httpModule&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://api.particle.io/v1/oauth/token`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toJSON&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This call &lt;em&gt;may&lt;/em&gt; get more complicated. Mostly in the case if you have two factor authorization setup. It's well worth it when you figure it all out. After all, no one wants to manually create auth tokens if they don't have to!&lt;/p&gt;

&lt;p&gt;Now you're ready to write and read from your devices. There's one thing though that may trip you up. Subscribing to events can be troublesome with a regular HTTP client. So much so that if you try to do it with NativeScript's HTTP client, it will lock up and never return. Luckily there is a way to handle these special HTTP calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server Sent What?
&lt;/h2&gt;

&lt;p&gt;Server Sent Events (SSE for short) is an HTTP/S subscription functionality. It allows you to connect to a SSE end point and continuously listen for updates. It's a similar web technology to what companies use for push notifications. It does require some extra functionality under the hood though...&lt;/p&gt;

&lt;h3&gt;
  
  
  SSE Library
&lt;/h3&gt;

&lt;p&gt;After much head scratching and searching I stumbled upon &lt;code&gt;nativescript-sse&lt;/code&gt;. It looked simple enough that I could start using immediately. More problems arose when I tried to use it though.&lt;/p&gt;

&lt;p&gt;First, it turns out you can't use the library in &lt;code&gt;tns preview&lt;/code&gt; mode. The alternative is to use &lt;code&gt;tns run ios --emulator&lt;/code&gt; or use &lt;code&gt;tns run ios&lt;/code&gt; with your iPhone connected to your computer. The non-emulator command will automatically deliver your prototype app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; I had already set up my phone in Xcode. You may have to do this yourself before &lt;code&gt;tns run ios&lt;/code&gt; is able to find and deploy to your phone.&lt;/p&gt;

&lt;p&gt;Secondly, once I got the library working, I noticed I would get some very nasty errors. The errors seemed to happen whenever a new message from Particle came along. Turns out the underlying Swift library for iOS &lt;a href="https://github.com/inaka/EventSource/issues/89"&gt;had fixed this last year.&lt;/a&gt; So I took it upon myself to figure out how to upgrade the NativeScript plugin. I'll save you the time to say that it can be a pain and there is a learning curve!&lt;/p&gt;

&lt;p&gt;Fortunately after some hacking I got something working. More instructions on how to compile the plugin is in the &lt;a href="https://github.com/jaredwolff/nativescript-sse"&gt;README&lt;/a&gt;. Alternatively, you can download a pre-built one on the &lt;a href="https://github.com/jaredwolff/nativescript-sse/releases/tag/v4.0.3"&gt;Release page of the repository.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Download the &lt;code&gt;.tgz&lt;/code&gt; file to wherever you like. Then, you can add it using &lt;code&gt;tns plugin add&lt;/code&gt;. The full command looks like this:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tns plugin add path/to/plugin/file.tgz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can check to make sure the library is installed by running &lt;code&gt;tns plugin list&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**jaredwolff$ tns plugin list
Dependencies:
┌─────────────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Plugin              │ Version                                                                          │
│ @nativescript/theme │ ~2.2.1                                                                           │
│ nativescript-sse    │ file:../../Downloads/nativescript-sse/publish/package/nativescript-sse-4.0.3.tgz │
│ tns-core-modules    │ ~6.3.0                                                                           │
└─────────────────────┴──────────────────────────────────────────────────────────────────────────────────┘
Dev Dependencies:
┌──────────────────────────┬─────────┐
│ Plugin                   │ Version │
│ nativescript-dev-webpack │ ~1.4.0  │
│ typescript               │ ~3.5.3  │
└──────────────────────────┴─────────┘
NOTE:
If you want to check the dependencies of installed plugin use npm view &amp;lt;pluginName&amp;gt; grep dependencies
If you want to check the dev dependencies of installed plugin use npm view &amp;lt;pluginName&amp;gt; grep devDependencies**
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Once installed, invoking the library takes a few steps. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SSE&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nativescript-sse&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;sse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SSE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.particle.io/v1/events/blob?access_token=&amp;lt;your access token&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// Add event listener&lt;/span&gt;
&lt;span class="nx"&gt;sse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blob&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Add callback&lt;/span&gt;
&lt;span class="nx"&gt;sse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO: do stuff with your event data here!&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Connect if not already&lt;/span&gt;
&lt;span class="nx"&gt;sse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;First you need to import and create an instance of the library. When you create the instance, you will have to enter the URL that you want to use. In this case we'll be doing the equivalent of &lt;code&gt;Particle.subscribe()&lt;/code&gt;. It should look something similar to the above: &lt;code&gt;https://api.particle.io/v1/events/&amp;lt;your event name&amp;gt;?access_token=&amp;lt;your access token&amp;gt;&lt;/code&gt;. Replace &lt;code&gt;&amp;lt;your event name&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;your access token&amp;gt;&lt;/code&gt; with the name of your event and your freshly created token!&lt;/p&gt;

&lt;p&gt;Then you set up the library to listen for the event you care about. In this case &lt;code&gt;blob&lt;/code&gt; is the event I most care about.&lt;/p&gt;

&lt;p&gt;Then make sure you configure a callback! That way you can get access to the data when &lt;code&gt;blob&lt;/code&gt; does come along. I've made a &lt;code&gt;TODO&lt;/code&gt; note where you can access said data.&lt;/p&gt;

&lt;p&gt;Finally, you can connect using the &lt;code&gt;.connect()&lt;/code&gt; method. If you don't connect, SSE will not open a session and you'll get no data from Particle.&lt;/p&gt;

&lt;p&gt;Placement of the code is up to you but from the examples it looks like within the &lt;code&gt;[constructor()&lt;/code&gt; of your model is a good place.](&lt;a href="https://github.com/jaredwolff/nativescript-sse/blob/master/demo/app/main-view-model.ts"&gt;https://github.com/jaredwolff/nativescript-sse/blob/master/demo/app/main-view-model.ts&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Examples
&lt;/h3&gt;

&lt;p&gt;If you're curious how to use SSE in other places I have another great example: Particle's CLI.&lt;/p&gt;

&lt;p&gt;Particle uses the &lt;code&gt;[request](https://github.com/request/request)&lt;/code&gt; library to handle SSE events in the app. Whenever you call &lt;code&gt;particle subscribe blob&lt;/code&gt; it invokes a &lt;code&gt;getStreamEvent&lt;/code&gt; further inside the code.  You can &lt;a href="https://github.com/particle-iot/particle-cli/blob/master/src/lib/api-client.js#L862"&gt;check it out here.&lt;/a&gt; The &lt;code&gt;request&lt;/code&gt; library has more information on streaming &lt;a href="https://github.com/request/request#streaming"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  More resources
&lt;/h2&gt;

&lt;p&gt;This is but the tip of the iceberg when it comes to connecting with Particle's API. Particle has some great documentation (as always) you can check out. Here are some important links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.particle.io/reference/device-cloud/api/"&gt;API documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.particle.io/reference/SDKs/javascript/"&gt;Javascript SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.particle.io/reference/SDKs/ios/"&gt;iOS SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.particle.io/reference/SDKs/android/"&gt;Android SDK&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post we've talked about app frameworks, NativeScript, NativeScript plugins and Server Sent Events. Plus all the Particle related things so you can connect your NativeScript app to Particle's API. I hope you've found this quick tutorial useful. If you have any questions feel free to leave a comment or &lt;a href="https://www.jaredwolff.com/contact/"&gt;send me a message&lt;/a&gt;. Also be sure to check out my &lt;a href="https://www.jaredwolff.com/the-ultimate-guide-to-particle-mesh/"&gt;newly released guide&lt;/a&gt;. It has content just like this all about Particle's ecosystem.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>particle</category>
      <category>nativescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Better Battery Life for Delightful Particle Deployments</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Tue, 24 Sep 2019 21:23:57 +0000</pubDate>
      <link>https://dev.to/jaredwolff/better-battery-life-for-delightful-particle-deployments-5afm</link>
      <guid>https://dev.to/jaredwolff/better-battery-life-for-delightful-particle-deployments-5afm</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fpost.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fpost.png" alt="Post image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IoT devices (almost) always need batteries. With those batteries some important things to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How long your device will last.&lt;/li&gt;
&lt;li&gt;How your device will perform in the environment.&lt;/li&gt;
&lt;li&gt;How you'll re-charge or replace the batteries when they die&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, you'll learn how I optimized every microamp used by a Particle Xenon based motion sensor. You'll learn about the the roadblocks I encountered, the fixes and how get your designs low power too!&lt;/p&gt;

&lt;p&gt;Ready?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Noodling Begins
&lt;/h2&gt;

&lt;p&gt;After noodling around on the project's direction, I decided to keep it simple. Only one sensor. The sensor? PIR.&lt;/p&gt;

&lt;p&gt;PIR stands for Passive Infrared Sensor. Ever walk into a building to see a blinking box in the upper corner? That, my friend, is a PIR sensor. They're used for motion detection for security systems and light control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fblack-88039_1920.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fblack-88039_1920.jpg" alt="motion sensor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if we can evolve this sensor and give it some more capabilities. Say, connect it to the internet? That opens a few more doors.&lt;/p&gt;

&lt;p&gt;If you've read any of my other recent articles, you know that i'm a big fan of Particle. Especially what they're doing with their mesh platform. So much so that &lt;a href="https://www.jaredwolff.com/the-ultimate-guide-to-particle-mesh/" rel="noopener noreferrer"&gt;I'm writing a guide about it.&lt;/a&gt; In this project, we'll use the Particle Xenon to make our motion sensor smart. That way you can have instantaneous motion alerts anywhere.&lt;/p&gt;

&lt;p&gt;The final design of the motion sensor board uses a TI TLV8544PWR. It's an ultra-ultra low-power rail-to-rail op amp. Its primary purpose is signal processing of the PIR sensor. In a typical case, it draws about 1.2µA (!!) when all the op-amps are active.&lt;/p&gt;

&lt;p&gt;The design also sports a PCF85063ATL/1 which is a standalone low-power real time clock. This device, along with the PIR, can wake up the Particle Mesh device from sleep. For the purposes of this project, it's used to limit the message count when a new PIR event occurs. i.e. a PIR interrupt is only allowed every 2 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FUntitled_Diagram-203613a0-4223-4a38-8468-0eb6a6a77159.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FUntitled_Diagram-203613a0-4223-4a38-8468-0eb6a6a77159.jpg" alt="Motion Sensor Block Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are other various parts in the mix but the above block diagram is this board at it's essence.&lt;/p&gt;

&lt;h3&gt;
  
  
  Assembling
&lt;/h3&gt;

&lt;p&gt;The main problem came when I had to actually assemble the device. I was abroad and had zero tools. Luckily there was a &lt;a href="https://www.fablabs.io/labs/fablabtaipei" rel="noopener noreferrer"&gt;FabLab&lt;/a&gt; nearby that allowed me to use their tools. (And boy, was I grateful)&lt;/p&gt;

&lt;p&gt;Here's a few pictures from the assembly process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01555-3a9254be-baf0-4669-b586-a2f360434abd.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01555-3a9254be-baf0-4669-b586-a2f360434abd.jpeg" alt="Unpopulated circuit board ready to go."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also had no jig to hold the boards. So, a quick sketch in Fusion360 and some time on the laser cutter yielded some fantastic results!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FIMG_2289-5321b6fb-78a8-42b5-b387-6a126b675130.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FIMG_2289-5321b6fb-78a8-42b5-b387-6a126b675130.jpeg" alt="Laser cut jig taped down + Unpopulated circuit board"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This board also happened to be the first that I used a metal stencil on. Oh man was it a dream. It's much more consistent compared to Kapton based stencils.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FIMG_2292-9e960972-83a8-4cf8-8f8a-56a61472eecf.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FIMG_2292-9e960972-83a8-4cf8-8f8a-56a61472eecf.jpeg" alt="Post solder paste application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what the boards look like after assembly. I had some issues with bridging around the op-amp but that was easily addressed with some flux and a hot iron.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01582-688cd2f3-b8d5-4cb9-8ea5-03b7107b37f7.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01582-688cd2f3-b8d5-4cb9-8ea5-03b7107b37f7.jpeg" alt="Back Side of Motion Board"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's the front of the design. Nothing but a PIR!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01583-27e958a7-6387-4651-ba60-0c025c6a1fa9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01583-27e958a7-6387-4651-ba60-0c025c6a1fa9.jpeg" alt="Motion Board Top Side"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you do ever find yourself assembling a board like this one here are a few tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most PIR sensors are extremely sensitive to heat. That means you must be careful when applying your soldering iron. The data sheet states that the temperature must be 350°C and you shouldn't apply the tip for more than 3 seconds.&lt;/li&gt;
&lt;li&gt;In this design there are some small parts. Normally you can use a microscope to assemble. If you're out of town, like I was when I assembled these boards, you can use your cell phone. Use the camera's pinch-zoom feature to check part orientations and  shorts between pins.&lt;/li&gt;
&lt;li&gt;In this design, there's &lt;strong&gt;a ton&lt;/strong&gt; of bulk capacitance on the input to the PIR sensor. This takes a while to charge especially if you're using a large 100µf capacitor. In the next design, the motion board will enable monitoring of this voltage. That way the Particle Mesh device will know when the PIR is stable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have some hardware, it's time to figure out how to get it all working and as low power as possible. We'll talk about some of the ways we can do that in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sleepy Time
&lt;/h2&gt;

&lt;p&gt;Sleep mode allows you to place your device in a low power state. It is often used when your Particle Mesh is idle. The primary goal is to conserve as much battery power as possible.&lt;/p&gt;

&lt;p&gt;There are two sleep modes with a few distinct differences. We'll discuss them below:&lt;/p&gt;

&lt;h3&gt;
  
  
  Stop mode sleep
&lt;/h3&gt;

&lt;p&gt;"Stop mode" is the standard sleep for any Particle Mesh device. It shuts down the network and, when configured, can wake up from an interrupt. Once the device wakes up, it will continue execution where it left off.&lt;/p&gt;

&lt;p&gt;You can trigger sleep mode in several different ways. The first way is by setting up a pin interrupt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RISING&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will put the device into stop mode and wait for a rising interrupt on &lt;code&gt;D2&lt;/code&gt;. You can connect a sensor IC that toggles &lt;code&gt;D2&lt;/code&gt; when it's done with a sensor reading. Not only will that alert the device a reading is available, but it will also wake the device from sleep!&lt;/p&gt;

&lt;p&gt;The Particle Mesh device can use a wakeup timeout when you specify a third argument. Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RISING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The third argument represents the time, in seconds, the device will remain in sleep. If no pin interrupt occurs, the device will wake back up after that interval. In this case, the device is set to wake up after 60 seconds if no event occurs on &lt;code&gt;D2&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deep sleep
&lt;/h3&gt;

&lt;p&gt;There is another lower power option:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;deep sleep.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Like stop mode, deep sleep mode will disconnect your device, and not retain the RAM. When the device wakes up, start execution from the beginning of your application. Any variables in memory will get reset. Data in non-volatile memory, like EEPROM, is not affected.&lt;/p&gt;

&lt;p&gt;It also has a 'wakeup pin' but unlike stop mode, you can't choose the pin. (The default is &lt;code&gt;D8&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;You can enter deep sleep by running executing the following function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;    &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SLEEP_MODE_DEEP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Measurements
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FIMG_2128-a908d417-5f58-41af-b93a-c53fc9700cfe.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FIMG_2128-a908d417-5f58-41af-b93a-c53fc9700cfe.jpeg" alt="Measuring current on Xenon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, what's the actual difference between all these modes? Here's a table of all the Particle Mesh devices and the two modes discussed above.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Li @ 3.4V (mA)&lt;/th&gt;
&lt;th&gt;Deep Sleep (uA)&lt;/th&gt;
&lt;th&gt;Stop Mode (uA)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Xenon&lt;/td&gt;
&lt;td&gt;834&lt;/td&gt;
&lt;td&gt;846&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boron&lt;/td&gt;
&lt;td&gt;1387&lt;/td&gt;
&lt;td&gt;1978&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Argon&lt;/td&gt;
&lt;td&gt;846&lt;/td&gt;
&lt;td&gt;1333&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; these measurements are from Agilent 6611C power supply set to 3.4V. The 6611C  has a low current measurement mode. In other words, it can measure the current without an ammeter in series.&lt;/p&gt;

&lt;p&gt;The results? Not particularly impressive. 0.834mA in deep sleep mode will last about 11.24 days on a 225mAh battery cell. There had to be a better way.&lt;/p&gt;

&lt;p&gt;Knowing that the NRF52 Series sips current in sleep mode, the solution was somewhere else. So after perusing the schematic, the culprit became clear. It was all the power management circuitry (highlighted below)! 😬&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FUntitled-893d00be-e26b-47c1-9d13-5d49985b4961.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FUntitled-893d00be-e26b-47c1-9d13-5d49985b4961.png" alt="Particle Mesh Xenon Schematic with power circuitry highlighted"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a way around this though. The solution?&lt;/p&gt;

&lt;p&gt;Bypass it all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bypass Bypass Bypass
&lt;/h2&gt;

&lt;p&gt;The main risk of bypassing was the potential for back powering those circuits. This could lead to unintended operation or even high quiescent currents. In my case though, there was none of that. I saw a &lt;strong&gt;noticeable&lt;/strong&gt; improvement.&lt;/p&gt;

&lt;p&gt;When bypassing, the Xenon went from 846µA to 46µA. Thats a &lt;strong&gt;factor of 17x!&lt;/strong&gt; With the motion sense board, it was about 49µA. That means with a 225mAh battery this setup would last about 0.52 years.&lt;/p&gt;

&lt;p&gt;Bypassing though came with some extra concerns. Like, "will the board support the voltage of a coin cell which varies from 3.3V down to 2.6V?" It was time to do some research.&lt;/p&gt;

&lt;h3&gt;
  
  
  Research
&lt;/h3&gt;

&lt;p&gt;So, we're bypassing all the regulators. That means that 3.3V can, over time, turn into 2.6V. Can all the circuitry on the Particle Mesh board handle 3.3V down to 2.6V?&lt;/p&gt;

&lt;p&gt;Let's look at the schematic to see what the devices of concern are.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fxenon-v1-6d65d0d2-0c20-4b77-aed7-89676e69bec3.00-schematic.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fxenon-v1-6d65d0d2-0c20-4b77-aed7-89676e69bec3.00-schematic.jpg" alt="Particle Mesh Xenon Schematic with circles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The circled devices are the ones we care about.&lt;/p&gt;

&lt;p&gt;This gives us a list of&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NRF52840&lt;/li&gt;
&lt;li&gt;SKY13351-378LF&lt;/li&gt;
&lt;li&gt;MX25L1606E&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we can then go and research each of them to understand the power range they can operate in. In this case every device must work at 3.3V and down to 2.6V.&lt;/p&gt;

&lt;p&gt;So, where's the best source of how these parts operate? The data sheets.&lt;/p&gt;

&lt;p&gt;Here's the one for the flash part:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FMX25L1606E_Voltage_Requirements-b33c1b63-a2ad-4db0-9b5f-32a05c498bb6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FMX25L1606E_Voltage_Requirements-b33c1b63-a2ad-4db0-9b5f-32a05c498bb6.jpg" alt="MX25L1606E Flash Chip Spec"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Nordic's NRF52840,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FNRF52840-d794e377-5617-44b8-87c9-367e7baf491f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FNRF52840-d794e377-5617-44b8-87c9-367e7baf491f.jpg" alt="NRF52840 Product Spec"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And for the antenna switch:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Frf_switch-9fc65ce2-4085-4704-8d10-09186111c667.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Frf_switch-9fc65ce2-4085-4704-8d10-09186111c667.jpg" alt="Skyworks RF Switch Datasheet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The search took a few minutes and 3 round trips to DuckDuckGo but we've found the information we've needed. &lt;em&gt;All devices can operate in the range we're looking for. (With a slight exception of the flash chip. In this example, it's not used so we can safely ignore it)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So, with that hurdle out of the way, it was time to write some firmware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it Smart
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FApple_iPhone_6s_Gold_-_Alerts-c20439a2-d898-4ee4-b56e-12340cdbdf6f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FApple_iPhone_6s_Gold_-_Alerts-c20439a2-d898-4ee4-b56e-12340cdbdf6f.png" alt="Alets on iPhone 6"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The last step in the process was to get the firmware as optimized as possible. i.e. to keep the device in sleep mode most of the time.&lt;/p&gt;

&lt;p&gt;For this first round of testing &lt;em&gt;stop sleep mode&lt;/em&gt; was used*.*&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Sleep differently depending on the situtation&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pirReady&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FALLING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;rtcReadyForMotion&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;A5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;D2&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CHANGE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FALLING&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FALLING&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Furthermore, the state of the device, dictated how sleep mode worked. For instance, the device would ignore PIR input if it wasn't ready for motion interrupts. This would prevent the device from waking up until it was time to.&lt;/p&gt;

&lt;p&gt;Additionally, the idea was to use &lt;code&gt;Particle.publish()&lt;/code&gt; to the cloud. Unfortunately, early complications made that tough. Sometimes the device would connect immediately. Sometimes it would sit there and do nothing. In the end, I changed the &lt;code&gt;Particle.publish()&lt;/code&gt; to &lt;code&gt;Mesh.publish()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This, in turn, required another device in the mesh network to subscribe to the message and forward it on to the cloud. An Argon running as an edge router is great for this purpose.&lt;/p&gt;

&lt;p&gt;Interestingly, I did notice that when &lt;code&gt;Mesh.connect()&lt;/code&gt; is called, &lt;code&gt;Mesh.publish()&lt;/code&gt; would work some of the time. I added some delay after &lt;code&gt;Mesh.connect()&lt;/code&gt; and the setup has been rock solid ever since. The code looks something like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// If A5 is high then we have an event.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pirEventOccurred&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;pirReady&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

      &lt;span class="c1"&gt;// Reset variable&lt;/span&gt;
      &lt;span class="n"&gt;pirEventOccurred&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;publishInProgress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Start connection&lt;/span&gt;
      &lt;span class="c1"&gt;// TODO: fix this. This is a hack. I'm getting unreliable messages *without a delay*. :|&lt;/span&gt;
      &lt;span class="n"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// Blink RED led&lt;/span&gt;
      &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"event occurred"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Side note:&lt;/strong&gt; &lt;code&gt;System.sleep&lt;/code&gt; causes the Particle Board to disconnect. So, you need to reconnect to the mesh network when you're ready to publish.&lt;/p&gt;

&lt;p&gt;Also, in the sprit of designing low power firmware, using the RGB LED was out. Especially since you can't see when it's inside the enclosure! You can disable it by turning on manual control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Turn off RGB Led&lt;/span&gt;
    &lt;span class="n"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the firmware seemed to be in a good place, it was time to put everything together. Unfortunately the sensor + Particle Mesh combination wasn't ready to cooperate just yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mysterious Restarts
&lt;/h3&gt;

&lt;p&gt;After some testing, I noticed that the device stopped working. I removed it from the plastics and found the RGB LED was blinking white. My best guess? The processor was continuously restarting.&lt;/p&gt;

&lt;p&gt;So after some head scratching, I attached the battery to the oscilloscope. I watched as the board started up and attempted to connect to the mesh network. As it did, the battery voltage would promptly drop. This is typical for any type of battery based design.The unexpected part? The Xenon would do a full hardware reset whenever the battery voltage would reach below 2.8V.&lt;/p&gt;

&lt;p&gt;What caused these resets? Was it another part? (Like the flash part unable to communicate?) Was it the NRF52?&lt;/p&gt;

&lt;p&gt;After searching the answer in data sheets and schematics, it turned out to be the NRF52. More specifically, &lt;strong&gt;the power supply supervisor.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FUntitled-aaef396e-78ff-4de3-b595-d192ad0386ae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FUntitled-aaef396e-78ff-4de3-b595-d192ad0386ae.png" alt="Power Supply Supervisor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the DeviceOS code, the power supply supervisor is set to restart the NRF52 if it goes below 2.8V. The exact call inside &lt;code&gt;hw_config.c&lt;/code&gt; under &lt;code&gt;platform/mcu/nRF52840/src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FScreen_Shot_2019-09-23_at_6-eb212e99-848e-4d50-9564-3bb23e7b8467.20.41_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FScreen_Shot_2019-09-23_at_6-eb212e99-848e-4d50-9564-3bb23e7b8467.20.41_PM.png" alt="Screenshot of code location"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And Bingo was his name-o.&lt;/p&gt;

&lt;p&gt;The solution was to use the &lt;code&gt;STARTUP&lt;/code&gt; macro and run &lt;code&gt;sd_power_pof_threshold_set&lt;/code&gt;. Then using one of the many options to set the new threshold:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;    &lt;span class="cm"&gt;/** @brief Power failure comparator thresholds. */&lt;/span&gt;
    &lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V21&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V21&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.1&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V23&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.3&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V25&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.5&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V27&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V27&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.7&amp;amp;nbsp;V */&lt;/span&gt;
    &lt;span class="cp"&gt;#if defined(POWER_POFCON_THRESHOLD_V17) || defined(__NRFX_DOXYGEN__)
&lt;/span&gt;        &lt;span class="n"&gt;NRF_POWER_POFTHR_V17&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V17&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 1.7&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V18&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 1.8&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V19&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V19&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 1.9&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V20&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.0&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V22&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.2&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V24&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.4&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V26&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.6&amp;amp;nbsp;V */&lt;/span&gt;
        &lt;span class="n"&gt;NRF_POWER_POFTHR_V28&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;POWER_POFCON_THRESHOLD_V28&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/**&amp;lt; Set threshold to 2.8&amp;amp;nbsp;V */&lt;/span&gt;
    &lt;span class="cp"&gt;#endif // defined(POWER_POFCON_THRESHOLD_V17) || defined(__NRFX_DOXYGEN__)
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;nrf_power_pof_thr_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case I set it to &lt;code&gt;NRF_POWER_THRESHOLD_V20&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; STARTUP(sd_power_pof_threshold_set(NRF_POWER_THRESHOLD_V20));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It was only after implementing this fix did the full device operate longer than a couple minutes! Now that it's working it's time to pack everything away into a polished enclosure. We'll talk about that step in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Enclosure
&lt;/h2&gt;

&lt;p&gt;Instead of sticking raw circuit boards to the wall, the first step was to find or create an enclosure. Unfortunately, I'm no product designer. Designing an enclosure takes a chunk of time that I didn't quite have. So what's the next best bet?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Using a pre-existing enclosure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Digikey is full of enclosures of all shapes and sizes. Luckily I had a few extra one in my box-of-stuff that seemed to fit the bill.&lt;/p&gt;

&lt;p&gt;The box is a Bud Industries CU-1941. It's 83.01mm x 54.00mm with a height of 30.51mm. Which is the perfect size to hold a circuit board plus a Particle Mesh board mushed together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fhbcu1941_v1-7e09cf3a-cac9-4f96-8e46-5713cdcb5ce7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fhbcu1941_v1-7e09cf3a-cac9-4f96-8e46-5713cdcb5ce7.png" alt="3D Render of the Project Box"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also happens to be the same enclosure used to house a modified version of the &lt;a href="https://oshpark.com/shared_projects/z10WdMjx" rel="noopener noreferrer"&gt;uCurrent Gold&lt;/a&gt;. Here's a picture of the one I built in the not to distant past:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01227-e31112b5-c712-489c-be28-d8e28e98ad0f.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01227-e31112b5-c712-489c-be28-d8e28e98ad0f.jpeg" alt="uCurrent Gold"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I contacted Bud and asked them for the STEP files. They obliged and while developing the electronics, I was able to mock up the design in Fusion360.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fsecurity-motion-assembly_v18-67ed8059-87a8-4293-92da-05a50a340a74.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fsecurity-motion-assembly_v18-67ed8059-87a8-4293-92da-05a50a340a74.png" alt="Digital mockup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Whenever you're using an off-the-shelf design, you need to do your due diligence. That means checking for fit in all directions. My main concern was making sure that the mated height of the boards was small enough. That way they could fit into the height of the enclosure.&lt;/p&gt;

&lt;p&gt;Here's a sectional analysis of the highest part of the motion sense board:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fsecurity-motion-assembly_v17-36e6dec3-e730-4331-93e9-706dba0dd1bf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2Fsecurity-motion-assembly_v17-36e6dec3-e730-4331-93e9-706dba0dd1bf.png" alt="Section Analysis of the Assembly"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it fit like a glove! (with some extra space to boot) I could even stick a much much larger battery in there and still have room. Here's what it looks like when everything is mated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01585.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01585.jpeg" alt="Mated"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This process takes some time but it's well worth it. It's especially gratifying when you have  assembled circuitboards that slide into place. Despite how well everything came together there's always improvements for the next version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Finished Product?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01577-1f727b60-f3b1-4e49-9f7d-e3e87100143e.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FDSC01577-1f727b60-f3b1-4e49-9f7d-e3e87100143e.jpeg" alt="Motion Board in Plastics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite some miscellaneous mechanical tweaks (drilling out holes a little big larger, etc), the project came together. Plus, it looks like something you'd by from some spy shop on the internet to boot!&lt;/p&gt;

&lt;p&gt;Despite how polished it looks there's one glaring factor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Power&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The device draws 49µA. Replacing a battery every half year though is rough on the wallet and the landfill.&lt;/p&gt;

&lt;p&gt;How do we get that current even lower?&lt;/p&gt;

&lt;p&gt;As you know by now, the Xenon board takes up 94% of the sleep power. So, why not take it completely out of the picture?&lt;/p&gt;

&lt;p&gt;Removing the processor from the mix is as simple as removing the power from the device while in sleep mode. We can do that by gating the power to the Particle Mesh board through the 3.3V pin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FUntitled_Diagram-e1f54fa0-d628-40d2-853d-62a394487883.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fbetter-battery-life-for-delightful-particle-deployments%2Fimages%2FUntitled_Diagram-e1f54fa0-d628-40d2-853d-62a394487883.png" alt="Flow of energy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This turns our 49µA of sleep current into 3µA. Thats a difference of 0.5 year and 8 years of standby current!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Creating low power hardware to save battery life can be frustrating at times. As long as you stay creative, you can eke every hour of battery life out of your deployed devices.&lt;/p&gt;

&lt;p&gt;Want to learn more? I have more content like this coming out soon. Plus, if you haven't already, you can sign up for updates on my upcoming &lt;a href="https://www.jaredwolff.com/the-ultimate-guide-to-particle-mesh/" rel="noopener noreferrer"&gt;Ultimate Guide to Particle Mesh&lt;/a&gt;. I'm sharing some insider content and list only exclusives you don't want to miss out on!&lt;/p&gt;

&lt;p&gt;Additionally, learn how to send push notifications to your phone using this &lt;a href="https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/#configuring-webhook" rel="noopener noreferrer"&gt;previous tutorial.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want one of these motion sensor boards?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jaredwolff.com/motion-sense-board/" rel="noopener noreferrer"&gt;Join the waiting list here&lt;/a&gt; and you'll be the first to know when they're available on my site. &lt;em&gt;This particular version will only work with Xenons.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Finally, thanks for reading this post. It's always great to hear from you guys about what projects you're building. It's and inspiration and keeps me going.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ti.com/product/TLV8544" rel="noopener noreferrer"&gt;TI TLV8544PWR&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nxp.com/docs/en/data-sheet/PCF85063A.pdf" rel="noopener noreferrer"&gt;PCF85063ATL/1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.particle.io/datasheets/mesh/xenon-datasheet/" rel="noopener noreferrer"&gt;Particle Xenon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.digikey.com/product-detail/en/bud-industries/CU-1941/377-2068-ND/439228" rel="noopener noreferrer"&gt;Bud Industries CU-1941&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>particle</category>
    </item>
    <item>
      <title>How to Presence Tracking Using Particle Argon</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Sun, 08 Sep 2019 13:15:15 +0000</pubDate>
      <link>https://dev.to/jaredwolff/how-to-presence-tracking-using-particle-argon-4flf</link>
      <guid>https://dev.to/jaredwolff/how-to-presence-tracking-using-particle-argon-4flf</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JjK-s735--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Copy_of_Copy_of_Flow-bdfad00f-8d42-4481-87b4-16662e984a85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JjK-s735--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Copy_of_Copy_of_Flow-bdfad00f-8d42-4481-87b4-16662e984a85.png" alt="Main post image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ever want to add presence or location tracking to a project? Frustrated by the solutions (or lack thereof)?&lt;/p&gt;

&lt;p&gt;Do not worry, you're not the only one!&lt;/p&gt;

&lt;p&gt;In this post you'll learn how to implement a very basic tracking and notification application. We'll be using a Particle Argon and a Tile Mate.&lt;/p&gt;

&lt;p&gt;By the end you'll be able to tell when the Tile is present or not. Plus we'll use Pushover to send push notifications to the devices of your choosing.&lt;/p&gt;

&lt;p&gt;Let's get going!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; before we get started, this post is &lt;strong&gt;lengthy&lt;/strong&gt;. &lt;a href="https://www.jaredwolff.com/files/how-to-location-tracking-using-particle-mesh-pdf/"&gt;You can download the PDF version so you can download it and view it later.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial investigation
&lt;/h2&gt;

&lt;p&gt;The idea of using a Tile wasn't obvious at first glance. Ideally, using a phone seemed to make more sense. Unfortunately, this wasn't as a viable option. It would require some more research and the creation of a Bluetooth iOS app.&lt;/p&gt;

&lt;p&gt;So, the idea of using a phone was out.&lt;/p&gt;

&lt;p&gt;Then I thought, "What devices &lt;em&gt;do&lt;/em&gt; advertise all the time?"&lt;/p&gt;

&lt;p&gt;That is what led me down the path of a tracker like Tile.&lt;/p&gt;

&lt;p&gt;After it arrived there was some customary testing. First stop, the Tile application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--74UiiLsb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/iphone7gold_portrait-2-2b6bcb7b-0f91-4578-a2db-034853565b09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--74UiiLsb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/iphone7gold_portrait-2-2b6bcb7b-0f91-4578-a2db-034853565b09.png" alt="Screenshot of Tile App"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was able to connect and use the device. I even made it play a catchy tune. 🎶&lt;/p&gt;

&lt;p&gt;Then, I moved on to using one of the Bluetooth scanner apps. I scrolled through all the results and Bingo. There was the Tile!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eAKWJhYQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/iphone7gold_portrait-466803ef-57d9-498c-8656-2c274bf7fe8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eAKWJhYQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/iphone7gold_portrait-466803ef-57d9-498c-8656-2c274bf7fe8d.png" alt="NRFConnect Scan Results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I even waited a few hours and checked it again. I wanted to make sure it didn't go to sleep after a while. Turns out, it's always advertising. As far as I can tell, about every 8 seconds.&lt;/p&gt;

&lt;p&gt;All of this testing lead to one conclusion: it could be easily used for presence detection.&lt;/p&gt;

&lt;p&gt;The next step in the process was trying to figure out how to get it working with an Argon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advertising
&lt;/h2&gt;

&lt;p&gt;As we had gathered in the previous step, we know that the Tile is advertising about every 8 seconds. That means it should be easily scanned for using any device including an Argon, Zenon or Boron.&lt;/p&gt;

&lt;p&gt;For this example I suggest you use an Argon. This is because Bluetooth and Mesh share the same radio. When scanning for the Tile, the Xenon connected to Mesh would often miss the advertising packets. This would lead to false negatives (and frustration!).&lt;/p&gt;

&lt;p&gt;Along the same lines, you'll want to make &lt;strong&gt;sure your Argon is connected to no mesh network.&lt;/strong&gt; You can remove it using the CLI. Connect your device to your computer and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;particle mesh remove &amp;lt;device name/ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Make sure that you replace &lt;strong&gt;&lt;/strong&gt; with your device's name or ID.&lt;/p&gt;

&lt;p&gt;Alright, back to the good stuff.&lt;/p&gt;

&lt;p&gt;Advertising can have a few different purposes in Bluetooth. Typically though, it marks the beginning of the pairing phase. That way other devices know that the advertising device is available.&lt;/p&gt;

&lt;p&gt;Additionally, the advertising device will indicate what services it has. We can use this knowledge to  filter out devices that don't match.&lt;/p&gt;

&lt;p&gt;For example, here's a screenshot of the services available on the Tile device:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lOj3wjM1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Apple_iPhone_6s_Gold-c1f1d0da-7aba-410a-97b4-3cbebc933208.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lOj3wjM1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Apple_iPhone_6s_Gold-c1f1d0da-7aba-410a-97b4-3cbebc933208.png" alt="Service information using Light Blue"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When scanning we'll double check that the device we're connecting to has the service UUID of &lt;code&gt;0xfeed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before we get deep into Bluetooth land though, let's set up our app for debugging using the Logger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging
&lt;/h2&gt;

&lt;p&gt;In this tutorial we'll be using the Logger. It allows you to display log messages from your app using &lt;code&gt;particle serial monitor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One of the cooler features about the logger is the idea of message hierarchy. This allows you, the designer, to selectively mute messages that may not be necessary.&lt;/p&gt;

&lt;p&gt;For example, if you have messages used for debugging. You could remove them or comment them out. Or, you could increase the &lt;code&gt;LOG_LEVEL&lt;/code&gt; so they're effectively ignored.&lt;/p&gt;

&lt;p&gt;Here are the logging levels which are available in &lt;code&gt;logging.h&lt;/code&gt; in Particle's device-os repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Log level. Ensure log_level_name() is updated for newly added levels&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LOG_LEVEL_ALL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Log all messages&lt;/span&gt;
    &lt;span class="n"&gt;LOG_LEVEL_TRACE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LOG_LEVEL_INFO&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LOG_LEVEL_WARN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LOG_LEVEL_ERROR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LOG_LEVEL_PANIC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LOG_LEVEL_NONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Do not log any messages&lt;/span&gt;
    &lt;span class="c1"&gt;// Compatibility levels&lt;/span&gt;
    &lt;span class="n"&gt;DEFAULT_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ALL_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_ALL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TRACE_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_TRACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LOG_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_TRACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Deprecated&lt;/span&gt;
    &lt;span class="n"&gt;DEBUG_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_TRACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Deprecated&lt;/span&gt;
    &lt;span class="n"&gt;INFO_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;WARN_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_WARN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ERROR_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PANIC_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_PANIC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;NO_LOG_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_NONE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Cool, log levels. But how do we use them?&lt;/p&gt;

&lt;p&gt;We can use them by invoking one of these functions:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Log.trace&lt;/code&gt;, &lt;code&gt;Log.info&lt;/code&gt;, &lt;code&gt;Log.warn&lt;/code&gt;, &lt;code&gt;Log.error&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a TRACE message."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If we set the log level to &lt;code&gt;LOG_LEVEL_INFO&lt;/code&gt; we'll only see messages from &lt;code&gt;Log.info&lt;/code&gt;, &lt;code&gt;Log.warn&lt;/code&gt;, and &lt;code&gt;Log.error&lt;/code&gt;. &lt;code&gt;LOG_LEVEL_WARN&lt;/code&gt;? Only &lt;code&gt;Log.warn&lt;/code&gt; and &lt;code&gt;Log.error&lt;/code&gt; will show up. (Hopefully you get the idea.)&lt;/p&gt;

&lt;p&gt;To set it up, we'll set the default level to &lt;code&gt;LOG_LEVEL_ERROR&lt;/code&gt;. We'll also set the app specific &lt;code&gt;LOG_LEVEL&lt;/code&gt; to &lt;code&gt;LOG_LEVEL_TRACE&lt;/code&gt;. The end result should look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// For logging&lt;/span&gt;
&lt;span class="n"&gt;SerialLogHandler&lt;/span&gt; &lt;span class="nf"&gt;logHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;115200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOG_LEVEL_TRACE&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// enable all app messages&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This way we don't get spammed with DeviceOS log messages. Plus, we get all the applicable messages from the app itself.&lt;/p&gt;

&lt;p&gt;By the way, if you want to set your device to a single &lt;code&gt;LOG_LEVEL&lt;/code&gt; you can set it up like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;SerialLogHandler&lt;/span&gt; &lt;span class="nf"&gt;logHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOG_LEVEL_INFO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you continue your journey using Particle's DeviceOS you'll soon realize how handy it can be. Now, let's move on to the good stuff!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting it up
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3Rg4QTQY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_9-a04e6802-ec22-4036-8ace-6150a532979b.50.41_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3Rg4QTQY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_9-a04e6802-ec22-4036-8ace-6150a532979b.50.41_PM.png" alt="Device-os Release Page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we'll want to make sure we're using the correct version of DeviceOS. Any version after 1.3 will have Bluetooth. You can get the &lt;a href="https://www.jaredwolff.com/how-to-upgrade-particle-mesh-device-os/"&gt;instructions here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next we'll want to start scanning for the Tile. We'll want do do this in the &lt;code&gt;loop()&lt;/code&gt; function at a specified interval. We'll use a &lt;code&gt;millis()&lt;/code&gt; timer in this case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Scan for devices&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;TILE_RE_CHECK_MS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="n"&gt;BLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scanResultCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Make sure you define &lt;code&gt;lastSeen&lt;/code&gt; at the top of the file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;system_tick_t&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll use it to track the last time the Tile has been "seen". i.e. when the last time the Argon saw an advertising packet from the Tile.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TILE_RE_CHECK_MS&lt;/code&gt; can be defined as&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define TILE_RE_CHECK_MS 7500
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This way we're checking, at the very minimum, every 7.5 seconds for advertising packets.&lt;/p&gt;

&lt;p&gt;In order to find the Tile device we'll use &lt;code&gt;BLE.scan&lt;/code&gt;. When we call it, It will start the scanning process.  As devices are found &lt;code&gt;scanResultCallback&lt;/code&gt; will fire.&lt;/p&gt;

&lt;p&gt;For now, we can define &lt;code&gt;scanResultCallback&lt;/code&gt; at the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;scanResultCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BleScanResult&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;scanResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You notice that it includes a &lt;code&gt;BleScanResult&lt;/code&gt;. This will contain the address, RSSI and device name (if available) and available service information. This will come in handy later when we're looking for our Tile device!&lt;/p&gt;

&lt;p&gt;Remember, that &lt;code&gt;BLE.scan&lt;/code&gt; does not return until scanning has been completed. The default timeout for scanning is 5 seconds. You can change that value using &lt;code&gt;BLE.setScanTimeout()&lt;/code&gt;. &lt;code&gt;setScanTimeout&lt;/code&gt; takes units in 10ms increments. So, for a 500ms timeout would require a value of 50.&lt;/p&gt;

&lt;p&gt;For the case of this app, I'd recommend using a value of 8s (8000ms). You can set it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;BLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setScanTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In this case, the device will scan for as long as it takes the Tile to advertise. That way it's less likely to miss an advertising packet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Scan Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JnSdxbtD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-fa1f684f-246c-474a-8397-e7387692e39b.05.19_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JnSdxbtD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-fa1f684f-246c-474a-8397-e7387692e39b.05.19_PM.png" alt="All const definitions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have &lt;code&gt;scanResultCallback&lt;/code&gt; lets define what's going on inside.&lt;/p&gt;

&lt;p&gt;We first want to get the service information inside the advertising data. The best way is to use &lt;code&gt;scanResult-&amp;gt;advertisingData.serviceUUID&lt;/code&gt;. We'll pass in an array of UUIDs what will be copied for our use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;BleUuid&lt;/span&gt; &lt;span class="n"&gt;uuids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;uuidsAvail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scanResult&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;advertisingData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;serviceUUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uuids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BleUuid&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will populate &lt;code&gt;uuids&lt;/code&gt; that way you can iterate over them. &lt;code&gt;uuidsAvail&lt;/code&gt; will equal the amount of available UUIDs.&lt;/p&gt;

&lt;p&gt;On our case we're looking for a particular UUID. We'll define it a the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define TILE_UUID 0xfeed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Normally UUIDs are &lt;strong&gt;much&lt;/strong&gt; longer. A short UUID like this means it has been reserved or is part of the Bluetooth specification. In either case we'll be checking for it in the same way we would check a 32bit or 128bit version.&lt;/p&gt;

&lt;p&gt;For diagnostic reasons we can also print out the device information. In this case the RSSI and the device MAC address is handy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Print out mac info&lt;/span&gt;
&lt;span class="n"&gt;BleAddress&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scanResult&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MAC: %02X:%02X:%02X:%02X:%02X:%02X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RSSI: %dBm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scanResult&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rssi&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally let's set up a loop to see if the device found has the UUID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Loop over all available UUIDs&lt;/span&gt;
&lt;span class="c1"&gt;// For tile devices there should only be one&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;uuidsAvail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;

    &lt;span class="c1"&gt;// Print out the UUID we're looking for&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;uuids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;shorted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TILE_UUID&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UUID: %x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uuids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;shorted&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="c1"&gt;// Stop scanning&lt;/span&gt;
        &lt;span class="n"&gt;BLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stopScanning&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can easily compare the "shorted" version of the UUID with &lt;code&gt;TILE_UUID&lt;/code&gt;. It's a simple integer so no complicated memory compare operations are necessary. So, using &lt;code&gt;if( uuids[i].shorted() == TILE_UUID )&lt;/code&gt; works just fine.&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;Log.trace&lt;/code&gt; to print out diagnostic information. In this case we're using it to print out the &lt;code&gt;shorted()&lt;/code&gt; version of the UUID.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test It!
&lt;/h3&gt;

&lt;p&gt;Let's test what we have so far!&lt;/p&gt;

&lt;p&gt;Program the app to your Argon. Open the terminal and run &lt;code&gt;particle serial monitor&lt;/code&gt; to view the debug messages. Heres an example of what you may see:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0000005825 [app] TRACE: MAC: 65:C7:B3:AF:73:5C
0000005827 [app] TRACE: RSSI: -37Bm
0000005954 [app] TRACE: MAC: B3:D9:F1:F0:5D:7E
0000005955 [app] TRACE: RSSI: -62Bm
0000006069 [app] TRACE: MAC: C5:F0:74:3D:13:77
0000006071 [app] TRACE: RSSI: -62Bm
0000006217 [app] TRACE: MAC: 65:C7:B3:AF:73:5C
0000006219 [app] TRACE: RSSI: -39Bm
0000006224 [app] TRACE: MAC: B3:D9:F1:F0:5D:7E
0000006225 [app] TRACE: RSSI: -62Bm
0000006296 [app] TRACE: MAC: D7:E7:FE:0C:A5:C0
0000006298 [app] TRACE: RSSI: -60Bm
0000006299 [app] TRACE: UUID: feed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice how the message includes &lt;code&gt;TRACE&lt;/code&gt; and also &lt;code&gt;[app]&lt;/code&gt;? That means it's a trace message originating from the application code. Handy right?&lt;/p&gt;

&lt;p&gt;This code does get spammy quick, especially if you're in an environment with lots of advertising Bluetooth devices. If you're Tile is on and running eventually you'll  see a message &lt;code&gt;UUID: feed&lt;/code&gt;. That means your Argon found the Tile!&lt;/p&gt;

&lt;p&gt;Next we'll use the onboard Mode button to "program" the Tile's address to memory. That way we can filter out all the devices we don't care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add Device On Button Push
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g-m2P7EC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-c1b2b96d-3a1b-45b5-bf5f-5fa7824c80d0.06.04_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g-m2P7EC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-c1b2b96d-3a1b-45b5-bf5f-5fa7824c80d0.06.04_PM.png" alt="System event handler"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First we need to figure out how to monitor the Mode button. The best bet, according to the documentation is to use &lt;code&gt;System.on&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;button_click&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;eventHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The first argument is the name of the system event. In our case it's &lt;code&gt;button_click&lt;/code&gt;. The second argument is an event handler function. We'll call it &lt;code&gt;eventHandler&lt;/code&gt; for now.&lt;/p&gt;

&lt;p&gt;Now let's create &lt;code&gt;eventHandler&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;eventHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;system_event_t&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; you can't use the &lt;code&gt;Log&lt;/code&gt; function inside &lt;code&gt;eventHandler&lt;/code&gt;. An easy way to test it is to toggle the LED on D7. Let's set it up!&lt;/p&gt;

&lt;p&gt;Initialize the LED in &lt;code&gt;setup()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set LED pin&lt;/span&gt;
&lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we can add this inside &lt;code&gt;eventHandler&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;button_click&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;digitalRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can then write to D7 (the onboard blue LED). We can even use &lt;code&gt;digitalRead&lt;/code&gt; to read what the state of the LED is. It will respond with &lt;code&gt;HIGH&lt;/code&gt; or &lt;code&gt;LOW&lt;/code&gt; depending on the situation.&lt;/p&gt;

&lt;p&gt;Load the firmware onto the device and we'll have nice control over the blue LED!&lt;/p&gt;

&lt;p&gt;In the next section, we'll use the Mode button to put the device into a "learning" mode. This will allow us to do a one touch setup with the target Tile device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing Address to EEPROM
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cwuYBVBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-8cf61301-8e85-4d65-a2aa-fd37175194f5.07.24_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cwuYBVBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-8cf61301-8e85-4d65-a2aa-fd37175194f5.07.24_PM.png" alt="Storing to EEPROM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this next step we'll store the address of the Tile into EEPROM. That way when the device is restarted or loses power we'll still be able to identify the Tile later on.&lt;/p&gt;

&lt;p&gt;There is one lingering question though. How do we get it to save the address in the first place?&lt;/p&gt;

&lt;p&gt;By monitoring the button press, we can put the device into a "learning" mode. The device will scan for a Tile, and save the address if it finds one.&lt;/p&gt;

&lt;p&gt;First let's add a conditional within &lt;code&gt;if( uuids[i].shorted() == TILE_UUID )&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// If we're in learning mode. Save to EEPROM&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;isLearningModeOn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;searchAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scanResult&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;EEPROM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TILE_EEPROM_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;searchAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;setLearningModeOff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll use the status of D7 as a way of knowing we're in "learning mode". We do this by reading D7 using &lt;code&gt;digitalRead(D7)&lt;/code&gt;. Let's create a function that makes this more clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;isLearningModeOn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digitalRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can also replace the &lt;code&gt;digitalWrite(D7,LOW);&lt;/code&gt; and &lt;code&gt;digitalWrite(D7,HIGH);&lt;/code&gt; with similar functions. That way it's more straight forward what we're doing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set "Learning mode" on&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setLearningModeOn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Set "Learning mode" off&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setLearningModeOff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;D7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, we assign a global variable &lt;code&gt;searchAddress&lt;/code&gt; as the scan result. We setup &lt;code&gt;searchAddress&lt;/code&gt; like this at the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;BleAddress&lt;/span&gt; &lt;span class="n"&gt;searchAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next we want to save it to non-volatile memory using &lt;code&gt;EEPROM.put&lt;/code&gt;. &lt;code&gt;TILE_EEPROM_ADDRESS&lt;/code&gt; is defined as &lt;code&gt;0xa&lt;/code&gt;. You can define  &lt;code&gt;TILE_EEPROM_ADDRESS&lt;/code&gt; to use whatever memory address tickles your fancy. Here's the full definition placed at the top of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define TILE_EEPROM_ADDRESS 0xa
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally, we turn off the LED and "learning mode" using &lt;code&gt;setLearningModeOff()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Every time a device is found we'll use &lt;code&gt;millis()&lt;/code&gt; to set &lt;code&gt;lastSeen&lt;/code&gt;. Additionally, we can track the last RSSI using &lt;code&gt;lastRSSI&lt;/code&gt;. It's a cheap way to to know approximately how close the device is. We'll use &lt;code&gt;scanResult-&amp;gt;rssi&lt;/code&gt; to get this information and set it to the &lt;code&gt;lastRSSI&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;Overall, your changes should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// Print out the UUID we're looking for&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;uuids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;shorted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TILE_UUID&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UUID: %x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uuids&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;shorted&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// If we're in learning mode. Save to EEprom&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;isLearningModeOn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;searchAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scanResult&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;EEPROM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TILE_EEPROM_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;searchAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;setLearningModeOff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Save info&lt;/span&gt;
    &lt;span class="n"&gt;lastSeen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;lastRSSI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scanResult&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rssi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Stop scanning&lt;/span&gt;
    &lt;span class="n"&gt;BLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stopScanning&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Before this function, we can filter out devices that don't match our &lt;code&gt;searchAddress&lt;/code&gt;. Add the following before &lt;code&gt;if( uuids[i].shorted() == TILE_UUID )&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// If device address doesn't match or we're not in "learning mode"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchAddress&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;scanResult&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;isLearningModeOn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will skip over devices that don't match. It will only proceed if the address matches or we're in "learning mode".&lt;/p&gt;

&lt;p&gt;Now, in order for us to load &lt;code&gt;searchAddress&lt;/code&gt; on startup, we'll have to load it from flash. Add this line to your &lt;code&gt;setup():&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;EEPROM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TILE_EEPROM_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;searchAddress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, check to make sure the address is valid. It won't be valid if all the bytes are &lt;code&gt;0xFF&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Warning about address&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;searchAddress&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;BleAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ff:ff:ff:ff:ff:ff"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Place this board into learning mode"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"and keep your Tile near by."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We should be able to "teach" our Argon the address of our Tile. Let's test it out!&lt;/p&gt;

&lt;h3&gt;
  
  
  Test it.
&lt;/h3&gt;

&lt;p&gt;Now if we compile and run the app, notice how there's no more log output? We have to "teach" the Tile address to the Particle Device. So, hit the mode button. The blue LED should turn on.&lt;/p&gt;

&lt;p&gt;Once your Tile has been found the LED will turn off and you'll see some output on the command line. Similar to what we've seen before:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0000006296 [app] TRACE: MAC: D7:E7:FE:0C:A5:C0
0000006298 [app] TRACE: RSSI: -60Bm
0000006299 [app] TRACE: UUID: feed
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The device has been committed to memory!&lt;/p&gt;

&lt;p&gt;You can also check if it's still saved after a reset. Hit the &lt;strong&gt;reset&lt;/strong&gt; button and check for the same output as above. If it's showing up, we're still good!&lt;/p&gt;

&lt;h2&gt;
  
  
  Update the Cloud
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K8DLukoO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-34eb3433-edb7-4b57-b3ca-3be935bba026.07.53_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K8DLukoO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-34eb3433-edb7-4b57-b3ca-3be935bba026.07.53_PM.png" alt="Publishing to the Particle cloud"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally let's set up a function called &lt;code&gt;checkTileStateChanged&lt;/code&gt;. We'll use it to check for changes to the state of the Tile on a regular interval.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;checkTileStateChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;TilePresenceType&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The main purpose of this function is to compare the &lt;code&gt;lastSeen&lt;/code&gt; variable with the "timeout" duration. In our case, our timeout duration is &lt;code&gt;TILE_NOT_HERE_MS&lt;/code&gt; which should be set to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define TILE_NOT_HERE_MS 30000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;near the top of your program. There's also two more conditions to look for. One where &lt;code&gt;lastSeen&lt;/code&gt; is equal to 0. This is usually because the app hasn't found the Tile yet after startup.&lt;/p&gt;

&lt;p&gt;The last case would be if the device has been seen and &lt;code&gt;lastSeen&lt;/code&gt; is not 0. So within &lt;code&gt;checkTileStateChanged&lt;/code&gt; let's put everything together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check to see if it's here.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;TILE_NOT_HERE_MS&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we only want this function to return true &lt;strong&gt;if the state has changed&lt;/strong&gt;. So we'll need to take advantage of the &lt;code&gt;TilePresenceType&lt;/code&gt; pointer in the agreement.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TilePresenceType&lt;/code&gt; is simply an enumeration of all the possible states. You can stick it at the top of your file as well. Here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;PresenceUnknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Here&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;NotHere&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;TilePresenceType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You'll also need a global variable that we can pass to the function. Set this at the top of your file as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Default status&lt;/span&gt;
&lt;span class="n"&gt;TilePresenceType&lt;/span&gt; &lt;span class="n"&gt;present&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PresenceUnknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, we can compare at each stage. Does it meet the criteria? Is the state different than the last one? If so, return true.&lt;/p&gt;

&lt;p&gt;Remember, we'll want to set &lt;code&gt;presence&lt;/code&gt; to the new updated value. So each condition should update the presence value. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NotHere&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here's what the fully flushed out function looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;checkTileStateChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;TilePresenceType&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Check to see if it's here.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;TILE_NOT_HERE_MS&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;NotHere&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NotHere&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"not here!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Case if we've just started up&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;PresenceUnknown&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PresenceUnknown&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"unknown!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Case if lastSeen is &amp;lt; TILE_NOT_HERE_MS&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;Here&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;presence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Here&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"here!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can now use this function in the main loop underneath the timer to start &lt;code&gt;Ble.scan()&lt;/code&gt;. We can use it to send a JSON payload. In this case we'll include important information like the Bluetooth Address, &lt;code&gt;lastSeen&lt;/code&gt; data, &lt;code&gt;lastRSSI&lt;/code&gt; data and a message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// If we have a change&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;checkTileStateChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We'll use an array of &lt;code&gt;char&lt;/code&gt; to get our address in a string format. You can chain together &lt;code&gt;toString()&lt;/code&gt; with &lt;code&gt;toCharArray&lt;/code&gt; to get what we need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get the address string&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;searchAddress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;toCharArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;An example payload string could look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create payload&lt;/span&gt;
&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;address&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;lastSeen&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:%d,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;lastRSSI&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:%i,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastRSSI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;status&lt;/code&gt; is simply a String defined at the top of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The payload going to the cloud&lt;/span&gt;
&lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You notice that there's also a variable called &lt;code&gt;messages&lt;/code&gt;. This is a static const array of strings. They're mapped to the values from the &lt;code&gt;TilePresenceType&lt;/code&gt;. Here's what it looks like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"unknown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"not here"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That way &lt;code&gt;PresenceUnknown&lt;/code&gt; matches to &lt;code&gt;"unknown"&lt;/code&gt;, &lt;code&gt;Here&lt;/code&gt; matches to &lt;code&gt;"here"&lt;/code&gt;, etc. It's a cheap easy way to associate a string with an enum.&lt;/p&gt;

&lt;p&gt;Finally we'll publish and process. This allows us to send the update immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Publish the RSSI and Device Info&lt;/span&gt;
&lt;span class="n"&gt;Particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PRIVATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WITH_ACK&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Process the publish event immediately&lt;/span&gt;
&lt;span class="n"&gt;Particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The overall function should look something like this in the end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// If we have a change&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;checkTileStateChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the address string&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;searchAddress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;toCharArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Create payload&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;address&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;lastSeen&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:%d,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;lastRSSI&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:%i,&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastSeen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lastRSSI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// Publish the RSSI and Device Info&lt;/span&gt;
    &lt;span class="n"&gt;Particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PRIVATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WITH_ACK&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Process the publish event immediately&lt;/span&gt;
    &lt;span class="n"&gt;Particle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, let's test it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing it!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a9gXUCtU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-97c0bda9-1ba1-4c75-8bd7-4db655d35d51.27.35_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a9gXUCtU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-06_at_11-97c0bda9-1ba1-4c75-8bd7-4db655d35d51.27.35_PM.png" alt="Test results in terminal window"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can test to make sure our Publish events are occurring without event leaving Particle Workbench. Open a new terminal by going to &lt;strong&gt;View → Terminal.&lt;/strong&gt; Then use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;particle subscribe &lt;span class="nt"&gt;--device&lt;/span&gt; &amp;lt;device_name&amp;gt; &amp;lt;event_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;&amp;lt;device_name&amp;gt;&lt;/code&gt; with the name or ID of your device.&lt;/p&gt;

&lt;p&gt;Replace &lt;code&gt;&amp;lt;event_name&amp;gt;&lt;/code&gt; with the name of the event. In our case it's &lt;code&gt;status&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can then test it all by removing the battery and waiting for the  "not here" alert. Plug the battery back in and you should get a "here" alert.&lt;/p&gt;

&lt;p&gt;Here's an example of the output&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; particle subscribe --device hamster_turkey status

&lt;p&gt;Subscribing to "status" from hamster_turkey's stream&lt;br&gt;
Listening to: /v1/devices/hamster_turkey/events/status&lt;br&gt;
{"name":"status","data":"{\"address\":\"C0:A5:0C:FE:E7:D7\",\"lastSeen\":40154002,\"lastRSSI\":-82,\"status\":\"not here\"}","ttl":60,"published_at":"2019-09-07T02:29:42.232Z","coreid":"e00fce68d36c42ef433428eb"}&lt;br&gt;
{"name":"status","data":"{\"address\":\"C0:A5:0C:FE:E7:D7\",\"lastSeen\":40193547,\"lastRSSI\":-83,\"status\":\"here\"}","ttl":60,"published_at":"2019-09-07T02:29:50.352Z","coreid":"e00fce68d36c42ef433428eb"}&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Configuring Webhook&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;In the last part of this tutorial we'll set up push notifications using a webhook. As mentioned before, we'll use Pushover and their handy API to send push notification(s) to the device(s) of your choice.&lt;/p&gt;

&lt;p&gt;Pushover has a fantastically easy API to use. Their application is a Swiss army knife for situations where you don't want to code an app to send push notifications.&lt;/p&gt;

&lt;p&gt;The first thing that you'll have to take note is your &lt;strong&gt;user key.&lt;/strong&gt; You can get that by logging into Pushover. Note: you'll need to set up an account first if you haven't already.&lt;/p&gt;

&lt;p&gt;It should look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MXsHfodL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-e71c60fd-ef57-4e5f-a7a5-e836b36af15e.39.36_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MXsHfodL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-e71c60fd-ef57-4e5f-a7a5-e836b36af15e.39.36_PM.png" alt="Pushover main screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're logged in and don't see this page, click on the &lt;strong&gt;Pushover logo&lt;/strong&gt; and that should bring you back.&lt;/p&gt;

&lt;p&gt;Next we'll want to create an application. Click on the &lt;strong&gt;Apps &amp;amp; Plugins&lt;/strong&gt; at the top of the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nkieUOiQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-3144b865-133a-4778-b349-37ebb333ede7.39.42_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nkieUOiQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-3144b865-133a-4778-b349-37ebb333ede7.39.42_PM.png" alt="App/Plugins screen in Pushover"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should then click &lt;strong&gt;Create a New Application.&lt;/strong&gt; This will allow us to get an &lt;strong&gt;API Token&lt;/strong&gt; that will be needed in the Particle Webhook setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ELiTtJW_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-05_at_11-c5628ee5-3aa9-4e82-b302-051278631a6b.49.21_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ELiTtJW_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-05_at_11-c5628ee5-3aa9-4e82-b302-051278631a6b.49.21_AM.png" alt="Create a New Application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set a name as you see fit. Fill in the description if you want a reminder. &lt;strong&gt;Click the box&lt;/strong&gt; and then click &lt;strong&gt;Create Application.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should go to the next page. Copy and save the &lt;strong&gt;API Token/Key&lt;/strong&gt; we'll need this also in a few steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a8LQg_8i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-a9e88ba1-dcb8-4b4d-8f20-a67e70e84ff5.39.50_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a8LQg_8i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-a9e88ba1-dcb8-4b4d-8f20-a67e70e84ff5.39.50_PM.png" alt="Viewing Application with API key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's setup the Webhook. Jump over to &lt;a href="https://console.particle.io/"&gt;https://console.particle.io&lt;/a&gt; and create a new integration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dql9kKDi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-ef788a5e-710f-4457-9b04-133d4ecf5f94.41.55_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dql9kKDi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-ef788a5e-710f-4457-9b04-133d4ecf5f94.41.55_PM.png" alt="Particle console creating new Webhook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll set the &lt;strong&gt;Event Name&lt;/strong&gt; to &lt;strong&gt;status&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;URL&lt;/strong&gt; to &lt;strong&gt;&lt;a href="https://api.pushover.net/1/messages.json"&gt;https://api.pushover.net/1/messages.json&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Also, if you want to filter by a specific device make sure you select it in the &lt;strong&gt;Device dropdown.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Advanced Settings&lt;/strong&gt; we'll finish up by setting a few fields.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TzeRk7QT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-831e153a-bb8c-4d73-98c4-61a958c9c51a.42.01_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TzeRk7QT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Screen_Shot_2019-09-03_at_3-831e153a-bb8c-4d73-98c4-61a958c9c51a.42.01_PM.png" alt="Setting the token and api key in Particle Webhook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create the following fields: &lt;strong&gt;token,&lt;/strong&gt; &lt;strong&gt;user&lt;/strong&gt;, &lt;strong&gt;title&lt;/strong&gt;, and &lt;strong&gt;message&lt;/strong&gt;. Then set token to the &lt;strong&gt;API Token&lt;/strong&gt; we got earlier. Do the same for the &lt;strong&gt;User Key.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;title&lt;/strong&gt; will show up as the title of your message. Make it whatever makes sense for you.&lt;/p&gt;

&lt;p&gt;You can set the &lt;strong&gt;message&lt;/strong&gt; as &lt;code&gt;The Tile is currently {{{status}}}. RSSI: {{{lastRSSI}}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are using mustache templates here. They allow you to use the data in the published payload and reformat it to your liking. In our case, we're using them to "fill in the blanks." The &lt;strong&gt;message&lt;/strong&gt; once processed would look something like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;The Tile is currently here. RSSI: -77&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As a side note, i'll be talking more about these templates in &lt;a href="https://www.jaredwolff.com/the-ultimate-guide-to-particle-mesh/"&gt;my guide&lt;/a&gt;. So stay tuned for that!&lt;/p&gt;

&lt;h3&gt;
  
  
  Test it
&lt;/h3&gt;

&lt;p&gt;Once your integration is in place, you can test doing what we did in the earlier step. Remove the battery, and wait for the "not here" message. Put it back and wait for the "here" message.&lt;/p&gt;

&lt;p&gt;Here's what it would look like on an iPhone:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dauMFq-Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Pushover-58cbd968-ffbe-4aa5-9087-de6f98708715.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dauMFq-Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/images/Pushover-58cbd968-ffbe-4aa5-9087-de6f98708715.png" alt="Pushover messages from Particle Cloud"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, I tested it a bunch! 😬&lt;/p&gt;

&lt;p&gt;If you've made it this far and everything is working, great work. You now have a Tile tracker for your house, office or wherever.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Looking for the finished code for this example? I would be too! It's &lt;a href="https://github.com/jaredwolff/particle-bluetooth-presence-detection"&gt;hosted on Github and is available here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As you can imagine, the techniques and technologies used in this article can be used in many ways. Let's summarize some of the key take aways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using Bluetooth Central to scan for and identify an off-the-shelf Tile device&lt;/li&gt;
&lt;li&gt;Storing the Tile identifying information to EEPROM. That way it can be retrieved on startup.&lt;/li&gt;
&lt;li&gt;Using our familiar &lt;code&gt;Particle.publish&lt;/code&gt; to push updates to the cloud.&lt;/li&gt;
&lt;li&gt;Using a Particle Integration Webhook to create push notifications on state change.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that you have it all working, expand on it, hack it and make it yours. Oh and don't forget to share! I'd love to hear from you. &lt;a href="mailto:hello@jaredwolff.com"&gt;hello@jaredwolff.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like this post? &lt;a href="http://twitter.com/share?text=Check%20out%20this%20handy%20guide%20for%20doing%20presence%20detection%20with%20Tile%20Mate%20and%20Particle%20Mesh!&amp;amp;url=https://www.jaredwolff.com/how-to-location-tracking-using-particle-mesh/&amp;amp;hashtags=iot"&gt;Share it with the world!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Interested in learning more? I'm writing a guide on how to get the most out of the Particle Platform. &lt;a href="https://www.jaredwolff.com/the-ultimate-guide-to-particle-mesh/"&gt;Learn more about it here.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>particle</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Ultimate How-to: Bluetooth Swift With Hardware in 20 Minutes</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Sun, 11 Aug 2019 09:16:41 +0000</pubDate>
      <link>https://dev.to/jaredwolff/ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes-2bmn</link>
      <guid>https://dev.to/jaredwolff/ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes-2bmn</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FCopy_of_Copy_of_Particle_Cloud_to_Blynk-60e2538b-ba8b-4d8d-9b88-c80f8cd2fb2e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FCopy_of_Copy_of_Particle_Cloud_to_Blynk-60e2538b-ba8b-4d8d-9b88-c80f8cd2fb2e.jpg" alt="Main image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a &lt;a href="https://www.jaredwolff.com/how-to-use-particles-powerful-bluetooth-api/" rel="noopener noreferrer"&gt;previous tutorial&lt;/a&gt;, you learned how to add Bluetooth to a Particle Xenon application. That way you could control the onboard RGB LED from a test app like nRF Connect or Light Blue Explorer.&lt;/p&gt;

&lt;p&gt;In this post, we're going to take it one step further. We're going to develop a Swift app to control a Particle Mesh RGB led. If all goes well, you should have a working app in about 20 minutes!&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't have time right now to read the full article?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.jaredwolff.com/files/the-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes/" rel="noopener noreferrer"&gt;Download the PDF version here.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting set up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Install Xcode. &lt;a href="https://developer.apple.com/xcode/resources/" rel="noopener noreferrer"&gt;You can download it from the App store here.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You'll also need an Apple login. I use my iCloud email. You can create a new account within Xcode if you don't have one yet.&lt;/li&gt;
&lt;li&gt;Install the &lt;a href="https://www.jaredwolff.com/how-to-use-particles-powerful-bluetooth-api/#final-code" rel="noopener noreferrer"&gt;RGB example code&lt;/a&gt; on a Particle Mesh board.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create the project
&lt;/h2&gt;

&lt;p&gt;Once everything is installed, let's get to the fun stuff!&lt;/p&gt;

&lt;p&gt;Open Xcode and go to &lt;strong&gt;File → New Project.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_3-7ef4de80-050c-4fc3-9cf2-8581e16ffe18.10.57_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_3-7ef4de80-050c-4fc3-9cf2-8581e16ffe18.10.57_PM.png" alt="Xcode New Project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Single View App.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_3-ef953954-312b-4320-be30-5da186d0902e.11.14_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_3-ef953954-312b-4320-be30-5da186d0902e.11.14_PM.png" alt="New Project Info"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then update the &lt;strong&gt;Project Name&lt;/strong&gt; to be to your liking. I've also changed my organization identifier to &lt;code&gt;com.jaredwolff&lt;/code&gt;. Modify it as you see fit!&lt;/p&gt;

&lt;p&gt;Select a location to save it.&lt;/p&gt;

&lt;p&gt;Next find your &lt;strong&gt;Info.plist.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_3-27439ca7-68c5-4890-902d-6c2ee7f31829.13.26_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_3-27439ca7-68c5-4890-902d-6c2ee7f31829.13.26_PM.png" alt="Info.plist in Xcocde"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;info.plist&lt;/code&gt; by adding &lt;code&gt;Privacy - Bluetooth Peripheral Usage Description&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The description I ended up using was &lt;code&gt;App uses Bluetooth to connect to the Particle Xenon RGB Example&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This allows you to use Bluetooth in your app if you ever want to release it.&lt;/p&gt;

&lt;p&gt;Now, let's get everything minimally functional!&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimally functional
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FCopy_of_Flow-5e62bf38-b399-4bca-9b5b-c63f9716af33.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FCopy_of_Flow-5e62bf38-b399-4bca-9b5b-c63f9716af33.jpg" alt="New section image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we'll get a minimally functional app to connect and do a services discovery. Most of the action will happen in the &lt;code&gt;ViewController.swift&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Lets first import &lt;code&gt;CoreBluetooth&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;CoreBluetooth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to control the Bluetooth Low Energy functionality in iOS. Then let's add both the &lt;code&gt;CBPeripheralDelegate&lt;/code&gt; and &lt;code&gt;CBCentralManagerDelegate&lt;/code&gt; to the &lt;code&gt;ViewController&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;ViewController&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UIViewController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheralDelegate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;CBCentralManagerDelegate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's now create local private variables to store the actual central manager and peripheral. We'll set them up further momentarily.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Properties&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;centralManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCentralManager&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheral&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your &lt;code&gt;viewDidLoad&lt;/code&gt; function, let's init the &lt;code&gt;centralManager&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="n"&gt;centralManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CBCentralManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting &lt;code&gt;delegate: self&lt;/code&gt; is important. Otherwise the central state never changes on startup.&lt;/p&gt;

&lt;p&gt;Before we get further, let's create a separate file and call it &lt;code&gt;ParticlePeripheral.swift&lt;/code&gt;. It can be placed anywhere but I placed it in a separate 'group' called &lt;strong&gt;Models&lt;/strong&gt; for later.&lt;/p&gt;

&lt;p&gt;Inside we'll create some public variables which contain the UUIDs for our Particle Board. They should look familiar!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;UIKit&lt;/span&gt;
    &lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;CoreBluetooth&lt;/span&gt;

    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;/// MARK: - Particle LED services and charcteristics Identifiers&lt;/span&gt;

        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;particleLEDServiceUUID&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CBUUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"b4250400-fb4b-4746-b2b0-93f0e61122c6"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;redLEDCharacteristicUUID&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CBUUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"b4250401-fb4b-4746-b2b0-93f0e61122c6"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;greenLEDCharacteristicUUID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CBUUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"b4250402-fb4b-4746-b2b0-93f0e61122c6"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;blueLEDCharacteristicUUID&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CBUUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"b4250403-fb4b-4746-b2b0-93f0e61122c6"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back in &lt;code&gt;ViewController.swift&lt;/code&gt; let's piece together the Bluetooth bits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bluetooth bits
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FFlow-3cb1d250-f9d5-4757-a244-be29bed9dcf6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FFlow-3cb1d250-f9d5-4757-a244-be29bed9dcf6.jpg" alt="Flow diagram for Bluetooth Swift in iOS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything to do with Bluetooth is event based. We'll be defining several functions that handle these events. Here are the important ones:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;centralManagerDidUpdateState&lt;/code&gt; updates when the Bluetooth Peripheral is switched on or off. It will fire when an app first starts so you know the state of Bluetooth. We also start scanning here.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;centralManager&lt;/code&gt; &lt;code&gt;didDiscover&lt;/code&gt; event occurs when you receive scan results. We'll use this to start a connection.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;centralManager&lt;/code&gt; &lt;code&gt;didConnect&lt;/code&gt; event fires once the device is connected. We'll start the device discovery here. &lt;strong&gt;Note:&lt;/strong&gt; Device discovery is the way we determine what services and characteristics are available. This is a good way to confirm what type of device we're connected to.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;peripheral&lt;/code&gt; &lt;code&gt;didDiscoverServices&lt;/code&gt; event first once all the services have been discovered. Notice that we've switched from &lt;code&gt;centralManager&lt;/code&gt; to &lt;code&gt;peripheral&lt;/code&gt; now that we're connected. We'll start the characteristic discovery here. We'll be using the RGB service UUID as the target.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;peripheral&lt;/code&gt; &lt;code&gt;didDiscoverCharacteristicsFor&lt;/code&gt; event will provide all the characteristics using the provided service UUID. This is the last step in the chain of doing a full device discovery. It's hairy but it only has to be done once during the connection phase!&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining all the Bluetooth functions.
&lt;/h3&gt;

&lt;p&gt;Now that we know what the functions events that get triggered. We'll define them in the logical order that they happen during a connection cycle.&lt;/p&gt;

&lt;p&gt;First, we'll define &lt;code&gt;centralManagerDidUpdateState&lt;/code&gt; to start scanning for a device with our Particle RGB LED Service. If Bluetooth is not enabled, it will not do anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// If we're powered on, start scanning&lt;/span&gt;
        &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;centralManagerDidUpdateState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;central&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCentralManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Central state update"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;central&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;poweredOn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Central is not powered on"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Central scanning for"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;particleLEDServiceUUID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;centralManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scanForPeripherals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;withServices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;particleLEDServiceUUID&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                                  &lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;CBCentralManagerScanOptionAllowDuplicatesKey&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Defining the &lt;code&gt;centralManager&lt;/code&gt; &lt;code&gt;didDiscover&lt;/code&gt; is our next step in the process. We know we've found a device if this event has occurred.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Handles the result of the scan&lt;/span&gt;
        &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;centralManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;central&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCentralManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didDiscover&lt;/span&gt; &lt;span class="nv"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;advertisementData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;rssi&lt;/span&gt; &lt;span class="kt"&gt;RSSI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NSNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="c1"&gt;// We've found it so stop scan&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;centralManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopScan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="c1"&gt;// Copy the peripheral instance&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peripheral&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;peripheral&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delegate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;

            &lt;span class="c1"&gt;// Connect!&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;centralManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we stop scanning using &lt;code&gt;self.centralManager.stopScan()&lt;/code&gt;. We set the &lt;code&gt;peripheral&lt;/code&gt; so it persists through the app. Then we connect to that device using &lt;code&gt;self.centralManager.connect&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once connected, we need to double check if we're working with the right device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// The handler if we do connect succesfully&lt;/span&gt;
        &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;centralManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;central&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCentralManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didConnect&lt;/span&gt; &lt;span class="nv"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheral&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;peripheral&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peripheral&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Connected to your Particle Board"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;peripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discoverServices&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;particleLEDServiceUUID&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By comparing the two peripherals we'll know its the device we found earlier. We'll kick off a services discovery using &lt;code&gt;peripheral.discoverService&lt;/code&gt;. We can use &lt;code&gt;ParticlePeripheral.particleLEDServiceUUID&lt;/code&gt; as a parameter. That way we don't pick up any services we don't care about.&lt;/p&gt;

&lt;p&gt;Once we finish the discovering services, we'll get a &lt;code&gt;didDiscoverServices&lt;/code&gt; event. We iterate through all the "available" services. (Though there will only be one!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Handles discovery event&lt;/span&gt;
        &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didDiscoverServices&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;services&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;peripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;particleLEDServiceUUID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LED service found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="c1"&gt;//Now kick off discovery of characteristics&lt;/span&gt;
                        &lt;span class="n"&gt;peripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discoverCharacteristics&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redLEDCharacteristicUUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                                 &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;greenLEDCharacteristicUUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                                                 &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blueLEDCharacteristicUUID&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By this point this is the third time we're checking to make sure we have the correct service. This becomes more handy later when there are many characteristics and many services.&lt;/p&gt;

&lt;p&gt;We call &lt;code&gt;peripheral.discoverCharacteristics&lt;/code&gt; with an array of UUIDs for the characteristics we're looking for. They're all the UUIDs that we defined in &lt;code&gt;ParticlePeripheral.swift&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we handle the &lt;code&gt;didDiscoverCharacteriscsFor&lt;/code&gt; event. We iterate through all the available characteristics. As we iterate we compare with the ones we're looking for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Handling discovery of characteristics&lt;/span&gt;
        &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didDiscoverCharacteristicsFor&lt;/span&gt; &lt;span class="nv"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;characteristics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;characteristics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;characteristics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redLEDCharacteristicUUID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Red LED characteristic found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;greenLEDCharacteristicUUID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Green LED characteristic found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blueLEDCharacteristicUUID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Blue LED characteristic found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we're ready to do a full device discovery of our Particle Mesh device. In the next section we'll test what we have to make sure things are working ok.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing our minimal example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FFlow-6-744bf855-fd7c-403b-b07f-dfc0c191b6af.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FFlow-6-744bf855-fd7c-403b-b07f-dfc0c191b6af.jpg" alt="Section image about testing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we get started, if you run into trouble I've put some troubleshooting steps in the &lt;a href="https://www.jaredwolff.com/the-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes/#troubleshooting" rel="noopener noreferrer"&gt;footnotes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To test, you'll have to have an iPhone with Bluetooth Low Energy.&lt;/strong&gt; Most modern iPhones have it. The last iPhone not to have it I believe was either the iPhone 4 or 3Gs. (so you're likely good)&lt;/p&gt;

&lt;p&gt;First, plug it into your computer.&lt;/p&gt;

&lt;p&gt;Go to the top by the play and stop buttons. Select your target device. In my case I chose my phone (&lt;strong&gt;Jared's iPhone&lt;/strong&gt;). You can also use an iPad.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-09_at_4-d04de709-6000-4161-bd12-7347a70d6e1e.37.27_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-09_at_4-d04de709-6000-4161-bd12-7347a70d6e1e.37.27_PM.png" alt="Select device type"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you can hit &lt;strong&gt;Command + R&lt;/strong&gt; or hit that &lt;strong&gt;Play button&lt;/strong&gt; to load the app to your phone.&lt;/p&gt;

&lt;p&gt;Make sure you have your log tab open. Enable it by clicking the bottom pane button in the top right corner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-09_at_4-8b83ea0a-274f-4a41-a5ff-e80717b41977.38.57_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-09_at_4-8b83ea0a-274f-4a41-a5ff-e80717b41977.38.57_PM.png" alt="Bottom pane in Xcode for logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure you have a mesh device setup and running the example code. You can go to &lt;a href="https://www.jaredwolff.com/how-to-use-particles-powerful-bluetooth-api/#final-code" rel="noopener noreferrer"&gt;this post&lt;/a&gt; to get it. Remember your Particle Mesh board needs to be running device OS 1.3.0 or greater for Bluetooth to work!&lt;/p&gt;

&lt;p&gt;Once both the firmware and app is loaded, let's check the log output.&lt;/p&gt;

&lt;p&gt;It should look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;View loaded
Central state update
Central scanning for B4250400-FB4B-4746-B2B0-93F0E61122C6
Connected to your Particle Board
LED service found
Red LED characteristic found
Green LED characteristic found
Blue LED characteristic found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This means that your Phone has connected, found the LED service! The characteristics also being discovered is important here. Without those we wouldn't be able to send data to the mesh device.&lt;/p&gt;

&lt;p&gt;Next step is to create some sliders so we can update the RGB values on the fly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slide to the left. Slide to the right.
&lt;/h2&gt;

&lt;p&gt;Next we're going to add some elements to our &lt;code&gt;Main.storyboard&lt;/code&gt;. Open &lt;code&gt;Main.storyboard&lt;/code&gt; and click on the &lt;strong&gt;View&lt;/strong&gt; underneath &lt;strong&gt;View Controller.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-7919e3c2-cb72-4297-b9e0-06139a064fec.57.37_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-7919e3c2-cb72-4297-b9e0-06139a064fec.57.37_PM.png" alt="Updating view in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click on the &lt;strong&gt;Library&lt;/strong&gt; button. (It looks like the old art Apple used for the home button)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-895d247a-6706-4c39-be04-0bda41195ca6.58.02_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-895d247a-6706-4c39-be04-0bda41195ca6.58.02_PM.png" alt="Library button in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll get a pop-up with all the choices that you can insert into your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-8b531053-621e-4c14-8086-2ac0ef6e52de.58.31_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-8b531053-621e-4c14-8086-2ac0ef6e52de.58.31_PM.png" alt="Library pane in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Drag three &lt;strong&gt;Labels&lt;/strong&gt; and copy three &lt;strong&gt;Sliders&lt;/strong&gt; to your view.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-9c8c6167-2279-4cb7-a37b-c24b8cdd275d.59.39_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-9c8c6167-2279-4cb7-a37b-c24b8cdd275d.59.39_PM.png" alt="Dragging Labels to Xcode View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can double click on the labels and rename them as you go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-72573a66-1201-4d08-902d-ea905c91b2bb.59.57_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_1-72573a66-1201-4d08-902d-ea905c91b2bb.59.57_PM.png" alt="Dragging Slider to Xcode View"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click and hold, some handy alignment tools will popup. They'll even snap to center!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-4c7b1627-c55e-4eba-b29a-32915ba41867.00.17_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-4c7b1627-c55e-4eba-b29a-32915ba41867.00.17_PM.png" alt="Alignment tools in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also select them all and move them together. We'll align them vertically and horizontally.&lt;/p&gt;

&lt;p&gt;In order for them to stay in the middle, let's remove the autoresizing property. Click the &lt;strong&gt;Ruler icon&lt;/strong&gt; on the top right. Then click each of the &lt;strong&gt;red bars&lt;/strong&gt;. This will ensure that your labels and sliders stay on the screen!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-f207fae8-7292-4e38-9985-c97444ab4e55.09.39_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-f207fae8-7292-4e38-9985-c97444ab4e55.09.39_PM.png" alt="Ruler pane in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next let's click the &lt;strong&gt;Show Assistant Editor&lt;/strong&gt; button. (Looks like a Venn diagram)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-c52c52b8-70b3-426b-9cbe-9df1042f5fb1.00.59_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-c52c52b8-70b3-426b-9cbe-9df1042f5fb1.00.59_PM.png" alt="Show Assistant Editor button in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; make sure that &lt;strong&gt;ViewController.swift&lt;/strong&gt; is open in your Assistant Editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-655a4468-4da8-4841-a2dc-1cf8706592e7.17.35_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-655a4468-4da8-4841-a2dc-1cf8706592e7.17.35_PM.png" alt="Automatic option in Assistant Editor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then underneath the &lt;code&gt;/properties&lt;/code&gt; section, &lt;strong&gt;Control-click and drag&lt;/strong&gt; &lt;strong&gt;the Red Slider&lt;/strong&gt; into your code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-39480e58-fd92-4ec7-a2db-9e8524019755.01.43_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-39480e58-fd92-4ec7-a2db-9e8524019755.01.43_PM.png" alt="Drag slider to code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat with all the other ones. Make sure you name them something different. Your code should look like this when you're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;        &lt;span class="c1"&gt;// Properties&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;centralManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCentralManager&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheral&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

        &lt;span class="c1"&gt;// Sliders&lt;/span&gt;
        &lt;span class="kd"&gt;@IBOutlet&lt;/span&gt; &lt;span class="k"&gt;weak&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;redSlider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UISlider&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
        &lt;span class="kd"&gt;@IBOutlet&lt;/span&gt; &lt;span class="k"&gt;weak&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;greenSlider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UISlider&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
        &lt;span class="kd"&gt;@IBOutlet&lt;/span&gt; &lt;span class="k"&gt;weak&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;blueSlider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UISlider&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allow us to access the value of the sliders.&lt;/p&gt;

&lt;p&gt;Next, let's attach the &lt;strong&gt;Value Changed&lt;/strong&gt; event to each of the sliders. &lt;strong&gt;Right click&lt;/strong&gt; on the &lt;strong&gt;Red Slider in the folder view.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-b2ffe0a6-709e-433d-9d4c-0f8028bd096c.03.44_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-b2ffe0a6-709e-433d-9d4c-0f8028bd096c.03.44_PM.png" alt="Drag value changed event to code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It should give you some options for events. Click and drag the &lt;strong&gt;Value Changed&lt;/strong&gt; event to your code. Make sure you name it something that makes sense. I used &lt;strong&gt;RedSliderChanged&lt;/strong&gt; for the Red Slider.&lt;/p&gt;

&lt;p&gt;Repeat two more times. Your code should look like this at the end of this step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;        &lt;span class="kd"&gt;@IBAction&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="kt"&gt;RedSliderChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;@IBAction&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="kt"&gt;GreenSliderChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;@IBAction&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="kt"&gt;BlueSliderChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've also selected each of the sliders to and &lt;strong&gt;un-checked Enabled&lt;/strong&gt;. That way you can't move them. We'll enable them later on in code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-e7e0aca9-224c-4bfb-b482-1d9e58b37947.21.21_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-e7e0aca9-224c-4bfb-b482-1d9e58b37947.21.21_PM.png" alt="Disable slider by default"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, this is a great time to change the &lt;strong&gt;maximum value to 255&lt;/strong&gt;. Also set the default &lt;strong&gt;value from 0.5 to 0.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-9bf5618b-f6ac-4aea-9888-7f93a8c75414.55.38_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FScreen_Shot_2019-08-11_at_2-9bf5618b-f6ac-4aea-9888-7f93a8c75414.55.38_PM.png" alt="Set default value and max value of slider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back at the top of the file. Let's create some local variables for each of the characteristics. We'll use these so we can write the slider variables to the Particle Mesh board.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;        &lt;span class="c1"&gt;// Characteristics&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;redChar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCharacteristic&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;greenChar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCharacteristic&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
        &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;blueChar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCharacteristic&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's tie everything together!&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;didDiscoverCharacteristicsFor&lt;/code&gt; callback function. Let's assign those characteristics. For example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redLEDCharacteristicUUID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Red LED characteristic found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;redChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we find each characteristic, we can also enable each of the sliders in the same spot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;            &lt;span class="c1"&gt;// Unmask red slider&lt;/span&gt;
            &lt;span class="n"&gt;redSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end your &lt;code&gt;didDiscoverCharacteristicsFor&lt;/code&gt; should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Handling discovery of characteristics&lt;/span&gt;
        &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didDiscoverCharacteristicsFor&lt;/span&gt; &lt;span class="nv"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;characteristics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;characteristics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;characteristics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;redLEDCharacteristicUUID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Red LED characteristic found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                        &lt;span class="n"&gt;redChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;
                        &lt;span class="n"&gt;redSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;greenLEDCharacteristicUUID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Green LED characteristic found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                        &lt;span class="n"&gt;greenChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;
                        &lt;span class="n"&gt;greenSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blueLEDCharacteristicUUID&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Blue LED characteristic found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                        &lt;span class="n"&gt;blueChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;
                        &lt;span class="n"&gt;blueSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's update the &lt;code&gt;RedSliderChanged&lt;/code&gt; &lt;code&gt;GreenSliderChanged&lt;/code&gt; and &lt;code&gt;BlueSliderChanged&lt;/code&gt; functions. What we want to do here is update the characteristic associated with the &lt;code&gt;Changed&lt;/code&gt; function. I created a separate function called &lt;code&gt;writeLEDValueToChar&lt;/code&gt;. We'll pass in the characteristic and the data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;writeLEDValueToChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;withCharacteristic&lt;/span&gt; &lt;span class="nv"&gt;characteristic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCharacteristic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;withValue&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="c1"&gt;// Check if it has the write property&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeWithoutResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;peripheral&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                &lt;span class="n"&gt;peripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;characteristic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withoutResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add a call to &lt;code&gt;writeLEDValueToChar&lt;/code&gt; to each of the &lt;code&gt;Changed&lt;/code&gt; functions. You will have to cast the value to a &lt;code&gt;Uint8&lt;/code&gt;. (The Particle Mesh device expects an unsigned 8-bit number.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;            &lt;span class="kd"&gt;@IBAction&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="kt"&gt;RedSliderChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"red:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;redSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;writeLEDValueToChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;withCharacteristic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;redChar&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;withValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;

        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repeat this for &lt;code&gt;GreenSliderChanged&lt;/code&gt; and &lt;code&gt;BlueSliderChanged&lt;/code&gt;. Make sure you changed &lt;code&gt;red&lt;/code&gt; to &lt;code&gt;green&lt;/code&gt; and &lt;code&gt;blue&lt;/code&gt; for each!&lt;/p&gt;

&lt;p&gt;Finally, to keep things clean, i've also added a function that handles Bluetooth disconnects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;centralManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;central&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBCentralManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;didDisconnectPeripheral&lt;/span&gt; &lt;span class="nv"&gt;peripheral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CBPeripheral&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside, we should reset the state of the sliders to 0 and disable them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;peripheral&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peripheral&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Disconnected"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;redSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
                &lt;span class="n"&gt;greenSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
                &lt;span class="n"&gt;blueSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

                &lt;span class="n"&gt;redSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="n"&gt;greenSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="n"&gt;blueSlider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a good idea to reset &lt;code&gt;self.peripheral&lt;/code&gt; to nil that way we're not ever trying to write to a phantom device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;                &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peripheral&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, because we've disconnected, start scanning again!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;                &lt;span class="c1"&gt;// Start scanning again&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Central scanning for"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;particleLEDServiceUUID&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;centralManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scanForPeripherals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;withServices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;ParticlePeripheral&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;particleLEDServiceUUID&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                                  &lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;CBCentralManagerScanOptionAllowDuplicatesKey&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright! We just about ready to test. Let's move on to the next (and final) step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the sliders.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FFlow-5-3af7db2e-8bf7-4561-98c2-a1b7ab0c685b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FFlow-5-3af7db2e-8bf7-4561-98c2-a1b7ab0c685b.jpg" alt="Next section test!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The hard work is done. Now it's time to play!&lt;/p&gt;

&lt;p&gt;The easiest way to test everything is to &lt;strong&gt;click the Play button&lt;/strong&gt; in the top left or the &lt;strong&gt;Command + R&lt;/strong&gt; keyboard shortcut. Xcode will load the app to your phone. You should see a white screen proceeded by a screen with your sliders!&lt;/p&gt;

&lt;p&gt;The sliders should stay greyed out until connected to your Particle Mesh board. You can check your log output if the connection has been established.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;View loaded
Central state update
Central scanning for B4250400-FB4B-4746-B2B0-93F0E61122C6
Connected to your Particle Board
LED service found
Red LED characteristic found
Green LED characteristic found
Blue LED characteristic found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;(Look familiar? We're connected!)&lt;/p&gt;

&lt;p&gt;If you followed everything perfectly, you should be able to move the sliders. Better yet, the RGB LED on the Particle Mesh board should change color.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FDSC01556-1fda5018-f1f5-4855-8705-f7d344ce3d78.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fthe-ultimate-how-to-bluetooth-swift-with-hardware-in-20-minutes%2Fimages%2FDSC01556-1fda5018-f1f5-4855-8705-f7d344ce3d78.jpeg" alt="Final test results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article you've learned how to connect your Particle Mesh board and iOS device over Bluetooth. We've learned how to connect to each of the available characteristics. Plus, on top of it all, make a clean interface to do it all in.&lt;/p&gt;

&lt;p&gt;As you can imagine, you can go down the rabbit hole with Bluetooth on iOS. There's more coming in my upcoming guide: &lt;strong&gt;The Ultimate Guide to Particle Mesh.&lt;/strong&gt; Subscribers to my list get access to pre-launch content and a discount when it comes out! &lt;a href="https://www.jaredwolff.com/the-ultimate-guide-to-particle-mesh/" rel="noopener noreferrer"&gt;Click here to get signed up.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;The full source code is available on &lt;a href="https://github.com/jaredwolff/swift-bluetooth-particle-rgb" rel="noopener noreferrer"&gt;Github.&lt;/a&gt; If you find it useful, hit the star button. ⭐️&lt;/p&gt;

</description>
      <category>bluetooth</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>2 Best Ways to Get Particle Mesh Working With Blynk</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Sun, 28 Jul 2019 13:22:16 +0000</pubDate>
      <link>https://dev.to/jaredwolff/2-best-ways-to-get-particle-mesh-working-with-blynk-2867</link>
      <guid>https://dev.to/jaredwolff/2-best-ways-to-get-particle-mesh-working-with-blynk-2867</guid>
      <description>&lt;p&gt;Writing an app takes time. It takes even more time to write one that works with hardware.&lt;/p&gt;

&lt;p&gt;Luckily there's a solution to this problem.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="http://blynk.io" rel="noopener noreferrer"&gt;Blynk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's an app that connects to your hardware. It has a drag and drop interface with pre built widgets. That means you can build an app in seconds. Then upload your device sensors readings within minutes.&lt;/p&gt;

&lt;p&gt;Blynk does work with Argon, Boron or ethernet connected Xenon. Unfortunately it doesn't work over a Particle Mesh network. In this article you'll learn some of the workarounds to get your mesh based projects up an running.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Particle Cloud to Blynk
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FCopy_of_Mesh_to_Blynk-33d60955-97f6-47d2-a78c-97724c502154.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FCopy_of_Mesh_to_Blynk-33d60955-97f6-47d2-a78c-97724c502154.png" alt="Particle Cloud to Blynk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start with the most simple use case: getting data from any Particle Device to Blynk.&lt;/p&gt;

&lt;p&gt;The Air Quality data from &lt;a href="http://jaredwolff.com/homemade-indoor-air-quality-sensor/" rel="noopener noreferrer"&gt;Particle Squared&lt;/a&gt; is perfect for this example. So, i'll be using that.&lt;/p&gt;

&lt;p&gt;First let's create a new Blynk Project&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2233-2a80b63a-b88b-4e58-a50b-5fc02923ae23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2233-2a80b63a-b88b-4e58-a50b-5fc02923ae23.png" alt="Create Project in Blynk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Grab the &lt;strong&gt;Auth Token&lt;/strong&gt; we'll need that in a bit. You can &lt;strong&gt;tap the Auth Token&lt;/strong&gt; to copy it to your clipboard.&lt;/p&gt;

&lt;p&gt;Next, let's add a &lt;strong&gt;SuperChart&lt;/strong&gt; for this example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2231-308d94bf-63f4-40a6-98e3-e49ced05c90a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2231-308d94bf-63f4-40a6-98e3-e49ced05c90a.png" alt="Add a SuperChart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Configure the SuperChart to use a Virtual Pin. We don't have access to the actual hardware pins on the device. &lt;strong&gt;V0&lt;/strong&gt; is a good choice.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2232-e53a0997-453e-47ee-b2d1-27a30dc12dfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2232-e53a0997-453e-47ee-b2d1-27a30dc12dfc.png" alt="Select Virtual Pin 0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To update values in Blynk, we'll have to connect somehow. The best way is to use an &lt;strong&gt;Integration&lt;/strong&gt; in the &lt;strong&gt;Particle Console.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Particle Console, click the icon below the terminal icon. Then click on &lt;strong&gt;New Integration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-27_at_10-33b85b6e-d685-4c1a-b639-423df7b2dd85.43.37_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-27_at_10-33b85b6e-d685-4c1a-b639-423df7b2dd85.43.37_AM.png" alt="Create new Integration in Particle Console"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Look at the example below to see how I filled everything out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-24_at_3-a5433944-8a2a-4b0d-81b6-faa4c038bd65.27.06_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-24_at_3-a5433944-8a2a-4b0d-81b6-faa4c038bd65.27.06_PM.png" alt="Enter all the information into the New Integration Screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Particle Squared uses the &lt;strong&gt;Event Name&lt;/strong&gt; as *&lt;strong&gt;*&lt;code&gt;blob&lt;/code&gt;. For other projects this may be different. **Remember:&lt;/strong&gt; your event name is the same as from &lt;code&gt;Particle.publish(eventName, data)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;URL&lt;/strong&gt; is set to use the &lt;code&gt;blink-cloud.com&lt;/code&gt; address. According to their API a sample URL looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-24_at_4-730022d3-f758-43b3-b4cc-bb3012d22d9e.44.06_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-24_at_4-730022d3-f758-43b3-b4cc-bb3012d22d9e.44.06_PM.png" alt="Blink cloud write pin API call"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'll also include it here so it's easier to copy&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://blynk-cloud.com/auth_token/update/pin?value=value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Replace &lt;code&gt;auth_token&lt;/code&gt; with the &lt;strong&gt;Auth Token&lt;/strong&gt; we got earlier.&lt;/p&gt;

&lt;p&gt;Replace &lt;code&gt;pin&lt;/code&gt; with the virtual pin we want to modify. In this case &lt;strong&gt;V0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Replace the &lt;code&gt;value&lt;/code&gt; with the value you want to use.&lt;/p&gt;

&lt;p&gt;We'll reference one of the values in the Particle Squared &lt;code&gt;blob&lt;/code&gt;. It's organized like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "temperature": 28.60,
  "humidity": 45.00,
  "sgp30_tvoc": 18,
  "sgp30_c02": 400,
  "bme680_temp": 27.36,
  "bme680_pres": 1012.43,
  "bme680_hum": 43.80,
  "bme680_iaq": 43.90,
  "bme680_temp_calc": 27.30,
  "bme680_hum_calc": 43.97
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Particle uses &lt;a href="http://mustache.github.io/mustache.5.html" rel="noopener noreferrer"&gt;mustache templates&lt;/a&gt;. As you can see in the screenshot above, you can set &lt;code&gt;value&lt;/code&gt; to &lt;code&gt;{{{temperature}}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're working on your own project, it's important to publish with JSON. As a reference the &lt;code&gt;Particle.publish&lt;/code&gt; command looks like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Publish data
Particle.publish("blob", String::format("{\"temperature\":%.2f,\"humidity\":%.2f}",si7021_data.temperature, si7021_data.humidity) , PRIVATE, WITH_ACK);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Click the &lt;strong&gt;big blue Save button&lt;/strong&gt; at the bottom of the screen. Then we can move on to the next step!&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Since creating our Particle Webhook Integration, it's been publishing data to Blynk. Let's go see if it's working.&lt;/p&gt;

&lt;p&gt;First, let's go back to the Blynk app. &lt;strong&gt;Hit the Play Button in the top Right&lt;/strong&gt; in Blynk screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2234-43cebeaa-2dd4-401c-84e5-b365953f4a66.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2234-43cebeaa-2dd4-401c-84e5-b365953f4a66.png" alt="Watch data come into Blynk app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your integration has been running for a while, you should see the graph populate with data! In the case you don't see anything, let's check the logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go back to your integration&lt;/strong&gt; and &lt;strong&gt;scroll towards the bottom&lt;/strong&gt;. We want to see if there are any errors.&lt;/p&gt;

&lt;p&gt;Not sure what that looks like? Here's an example of an integration with errors:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-24_at_4-47e820b4-1636-406e-8ba1-ff22cf899478.55.36_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-24_at_4-47e820b4-1636-406e-8ba1-ff22cf899478.55.36_PM.png" alt="Particle console integration errors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can scroll further down to investigate why the error has occurred.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-24_at_4-2e5843f3-1960-41a7-90b0-d80432d6ef2d.56.36_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-24_at_4-2e5843f3-1960-41a7-90b0-d80432d6ef2d.56.36_PM.png" alt="Investigate Particle Integration failure further"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the way at the bottom shows the response from the server. Depending on the service, they'll give you information why your API call failed. In my case, I was missing values for two fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Particle to Blynk is working!
&lt;/h3&gt;

&lt;p&gt;You now have a basic way of publishing to a virtual pin in Blynk. There are drawbacks though. Most importantly, you'll have to create an integration for every signal virtual pin. If you have eight readings, that means eight integrations.&lt;/p&gt;

&lt;p&gt;Bummer.&lt;/p&gt;

&lt;p&gt;In the next section, you'll learn a different way to configure Blynk. Let's go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Mesh Using Blynk Library
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FMesh_to_Blynk-0a66867b-a193-4b97-801a-b780c9a481e2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FMesh_to_Blynk-0a66867b-a193-4b97-801a-b780c9a481e2.png" alt="Particle Mesh to Blynk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unlike the first method, we'll be focusing on changing firmware only.&lt;/p&gt;

&lt;p&gt;We’ll use a Argon, Boron or Ethernet Connected Xenon and one regular Xenon. For the rest of this tutorial, we'll call these devices an “edge router”.&lt;/p&gt;

&lt;p&gt;The Xenon will run the Particle Squared code. Instead of using &lt;code&gt;Particle.publish&lt;/code&gt; we'll be using &lt;code&gt;Mesh.publish&lt;/code&gt;. This allows us to publish only to the local mesh network.&lt;/p&gt;

&lt;p&gt;Meanwhile the edge router is listening for the message. It collects the values and then uses the Blynk API to publish to the app.&lt;/p&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup our Edge Router
&lt;/h3&gt;

&lt;p&gt;Pull up the menu by pressing &lt;strong&gt;Cmd+Shift+P&lt;/strong&gt;. Type &lt;strong&gt;Install Library.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-28_at_4-f8149823-96fc-460f-b47e-2bcf56b670c2.50.29_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FScreen_Shot_2019-07-28_at_4-f8149823-96fc-460f-b47e-2bcf56b670c2.50.29_PM.png" alt="Install library in Visual Studio Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then enter &lt;strong&gt;blynk.&lt;/strong&gt; The library should download if you haven't already.&lt;/p&gt;

&lt;p&gt;Once installed you can include the library at the top of your &lt;code&gt;.ino&lt;/code&gt; file like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include &amp;lt;blynk.h&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our &lt;code&gt;setup()&lt;/code&gt; function let's init the Blynk library:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Put initialization like pinMode and begin functions here.
Blynk.begin(auth);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our &lt;code&gt;setup()&lt;/code&gt; function, subscribe to the &lt;code&gt;temperature&lt;/code&gt; event. The connected Xenon will generate this event.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Subscribe to temperature events
Mesh.subscribe("temperature",tempHandler);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Define &lt;code&gt;tempHandler&lt;/code&gt; like this for now:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Temperature event handler for mesh
void tempHandler(const char *event, const char *data){
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;loop()&lt;/code&gt; function make sure we have &lt;code&gt;Blynk.run();&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// loop() runs over and over again, as quickly as it can execute.
void loop() {
  // The core of your code will likely live here.
  Blynk.run();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally, for &lt;code&gt;tempHandler&lt;/code&gt; we can add a debug print to monitor events. I've used something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Serial.printlnf("event=%s data=%s", event, data ? data : "NULL");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Particle uses this in some of their examples. It's perfect for our purposes as well!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; make sure you have &lt;code&gt;Serial.begin()&lt;/code&gt; called in your &lt;code&gt;Setup()&lt;/code&gt; function!&lt;/p&gt;

&lt;p&gt;So now we have &lt;code&gt;tempHandler&lt;/code&gt; to receive data from the Xenon. The edge router can now take that data and upload it to Blynk. Let's use the &lt;code&gt;Blynk.virtualWrite&lt;/code&gt; function for this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Write the data
Blynk.virtualWrite(V0, data);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will write the temperature value from a Xenon to the &lt;code&gt;V0&lt;/code&gt; pin. If you used something other than V0, be sure to change that value here. (This is the same setup as the previous &lt;em&gt;Particle Cloud to Blynk&lt;/em&gt; example)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2232-e53a0997-453e-47ee-b2d1-27a30dc12dfc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FIMG_2232-e53a0997-453e-47ee-b2d1-27a30dc12dfc.png" alt="DataStream V0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final code for the edge router should look something like this. Compile a flash it to your device when you're ready!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
 * Project blynk-argon-forwarder
 * Description: Argon Blynk forwarder for Particle Mesh. Forwards data from mesh connected devices to Blynk.
 * Author: Jared Wolff
 * Date: 7/25/2019
 */

#include &amp;lt;blynk.h&amp;gt;

char auth[] = "&amp;lt;ENTER YOUR AUTH KEY&amp;gt;";

// Temperature event handler for mesh
void tempHandler(const char *event, const char *data){
  Serial.printlnf("event=%s data=%s", event, data ? data : "NULL");

  // Write the data
  Blynk.virtualWrite(V0, data);
}

// setup() runs once, when the device is first turned on.
void setup() {

  // Serial for debugging
  Serial.begin();

  // Put initialization like pinMode and begin functions here.
  Blynk.begin(auth);

  // Subscribe to temperature events
  Mesh.subscribe("temperature",tempHandler);

}

// loop() runs over and over again, as quickly as it can execute.
void loop() {
  // The core of your code will likely live here.
  Blynk.run();

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Remember to set &lt;code&gt;auth&lt;/code&gt; using the &lt;code&gt;AUTH TOKEN&lt;/code&gt; in the Blynk app!&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up a Xenon
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FCopy_of_Compose-a34be8da-a352-4a76-af8c-8c83e65efe50.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Ftwo-best-ways-to-get-particle-mesh-working-with-blynk%2Fimages%2FCopy_of_Compose-a34be8da-a352-4a76-af8c-8c83e65efe50.png" alt="Xenon!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create a new project. This time it will be for the Xenon capturing "temperature data."&lt;/p&gt;

&lt;p&gt;Let's add a variable called &lt;code&gt;time_millis&lt;/code&gt; to the top of the file. The type is &lt;code&gt;system_tick_t&lt;/code&gt;. We'll use it to create a simple delay timer for the temperature readings.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Global variable to track time (used for temp sensor readings)
system_tick_t time_millis;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For the interval, let's use a preprocessor define&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#define INTERVAL_MS 2000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now let's tie those together in the &lt;code&gt;loop()&lt;/code&gt; function. We'll use an &lt;code&gt;if&lt;/code&gt; statement to compare our current system time with that of the last event plus offset. If you ever need a simple timer, this is one of the best ways to do it!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Check if our interval &amp;gt; 2000ms
  if( millis() - time_millis &amp;gt; INTERVAL_MS ) {
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once we're inside, make sure you reset &lt;code&gt;timer_millis&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        //Set time to the 'current time' in millis
    time_millis = millis();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then, we'll generate the temperature value using the &lt;code&gt;random()&lt;/code&gt; function. We'll use the two parameter variant. That way we can set the minimum value and the maximum value:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Create a random number
    int rand = random(20,30);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally we'll &lt;code&gt;Mesh.publish&lt;/code&gt; the value:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Publish our "temperature" value
    Mesh.publish("temperature",String::format("%d",rand));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When this example runs, the temperature is broadcast to the mesh network. Then, the edge router receives it and forwards it on to Blynk!&lt;/p&gt;

&lt;p&gt;You can flash this firmware whenever you're ready. Here's the full code for the Xenon so you can cross compare:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
 * Project blynk-xenon-rgb
 * Description: Recieve RGB level from connected Edge Router. Sends simiulated temperature values via mesh to the Blynk cloud.
 * Author: Jared Wolff
 * Date: 7/25/2019
 */

// How often we update the temperature
#define INTERVAL_MS 2000

// Global variable to track time (used for temp sensor readings)
system_tick_t time_millis;
// setup() runs once, when the device is first turned on.
void setup() {

  // Set time to 0
  time_millis = 0;

}

// loop() runs over and over again, as quickly as it can execute.
void loop() {

  // Check if our interval &amp;gt; 2000ms
  if( millis() - time_millis &amp;gt; INTERVAL_MS ) {
    //Set time to the 'current time' in millis
    time_millis = millis();

    // Create a random number
    int rand = random(20,30);

    // Publish our "temperature" value
    Mesh.publish("temperature",String::format("%d",rand));

  }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Give it a test!
&lt;/h3&gt;

&lt;p&gt;Now that we've programmed both devices let's get them talking to each other.&lt;/p&gt;

&lt;p&gt;I've already set up the Argon with a mesh network called &lt;strong&gt;8f-9.&lt;/strong&gt; I'll explain how to get the Xenon connected with the CLI. You can also used the Particle App.&lt;/p&gt;

&lt;p&gt;First, let's connect the Xenon to USB and get it into Listening Mode. After connect, hold the &lt;strong&gt;Mode button&lt;/strong&gt; until &lt;strong&gt;blinking blue.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/C-0_m3GUupg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Then use the CLI to set up the mesh network. First let's get the device ID:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Jareds-MacBook-Pro:nrfjprog.sh jaredwolff$ particle identify
? Which device did you mean?
  /dev/tty.usbmodem146401 - Argon
❯ /dev/tty.usbmodem146101 - Xenon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you have multiple devices connect, make sure you select the right one! If prompted, select a device. Your output should look something like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your device id is e00fce682d9285fbf4412345
Your system firmware version is 1.3.0-rc.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We'll need the &lt;strong&gt;id&lt;/strong&gt; for the next step. Now, let's run the &lt;code&gt;particle mesh&lt;/code&gt; command.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;particle mesh add &amp;lt;xenon id&amp;gt; &amp;lt;id of your argon, boron, etc&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here's an example below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;particle mesh add e00fce682d9285fbf4412345 hamster_turkey
? Enter the network password [hidden]
▄ Adding the device to the network...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;At the end of it you'll see:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Done! The device should now connect to the cloud.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This process is not perfect. During the &lt;code&gt;Adding the device to the network...&lt;/code&gt; stage, I had to remove the Xenon using &lt;code&gt;particle mesh remove&lt;/code&gt;. Then re-run the &lt;code&gt;particle mesh add&lt;/code&gt; command after resetting the Argon.&lt;/p&gt;

&lt;p&gt;Now here comes to finale.&lt;/p&gt;

&lt;p&gt;Connect the two devices to serial using &lt;code&gt;particle serial monitor --follow&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you have the two devices connected, &lt;code&gt;particle serial monitor&lt;/code&gt; will prompt you to select:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Jareds-MacBook-Pro:blynk-xenon-rgb jaredwolff$ particle serial monitor --follow
Polling for available serial device...
? Which device did you mean? /dev/tty.usbmodem146101 - Xenon
Opening serial monitor for com port: "/dev/tty.usbmodem146101"
Serial monitor opened successfully:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Remember:&lt;/strong&gt; You have to run &lt;code&gt;particle serial monitor&lt;/code&gt; for each device you want to connect to.&lt;/p&gt;

&lt;p&gt;If all is working, you'll likely see some output from the edge router!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Serial monitor opened successfully:
event=temperature data=21
event=temperature data=28
event=temperature data=21
event=temperature data=27
event=temperature data=28
event=temperature data=26
event=temperature data=23
event=temperature data=26
event=temperature data=21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Looking at the app, the &lt;strong&gt;Super Chart&lt;/strong&gt; should be reacting to this new data.&lt;/p&gt;

&lt;p&gt;Compare the last value in the command line to the last on the chart? Do they match? If so, you made it to the end of this example!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial you've learned how to forward Particle Cloud data to Blynk. You've also learned how to do the same using a Particle Argon, Boron or ethernet connected Xenon. Awe yea. 😎👍&lt;/p&gt;

&lt;p&gt;Now that you have the tools to Blink-ify your Particle Mesh powered projects, it's time to get to work!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Like this post?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This post is an excerpt from my upcoming &lt;em&gt;Ultimate Guide to Particle Mesh&lt;/em&gt;. I'll be sharing more exclusive content with my mailing list as it get's closer to launch. &lt;a href="https://jaredwolff.com/the-ultimate-guide-to-particle-mesh/" rel="noopener noreferrer"&gt;You can sign up here for updates.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Still have questions?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Leave a comment or &lt;a href="https://www.jaredwolff.com/about" rel="noopener noreferrer"&gt;shoot me a line.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>particle</category>
      <category>blynk</category>
      <category>tutorial</category>
      <category>iot</category>
    </item>
    <item>
      <title>7 Effective Ways To Speed Up Your Site</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Mon, 22 Jul 2019 11:50:23 +0000</pubDate>
      <link>https://dev.to/jaredwolff/7-effective-ways-to-speed-up-your-site-190h</link>
      <guid>https://dev.to/jaredwolff/7-effective-ways-to-speed-up-your-site-190h</guid>
      <description>&lt;p&gt;&lt;strong&gt;This post is originally from my blog on &lt;a href="https://www.jaredwolff.com/seven-ways-to-optimize-your-site-for-speed/" rel="noopener noreferrer"&gt;www.jaredwolff.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Web rankings are dictated by how fast you serve your content.&lt;/p&gt;

&lt;p&gt;You need to optimize to get that top spot. As an added benefit your visitors will thank you. (or not even notice — a good thing!)&lt;/p&gt;

&lt;p&gt;In this article, I show you how to optimize your site for speed.&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the Baseline
&lt;/h2&gt;

&lt;p&gt;First, let's sample your site.&lt;/p&gt;

&lt;p&gt;Google's &lt;a href="https://developers.google.com/speed/pagespeed/insights/" rel="noopener noreferrer"&gt;PageSpeed insights tool&lt;/a&gt; should be your first stop.  After using it you'll know if your site is optimized for both mobile and desktop.&lt;/p&gt;

&lt;p&gt;Google bases their speed tests on how fast your page renders content. i.e. They measure how long it takes until your website hits your visitors eyeballs. The longer the wait, the lower the score.&lt;/p&gt;

&lt;p&gt;Here's a screenshot from &lt;a href="https://www.jaredwolff.com/homemade-indoor-air-quality-sensor/" rel="noopener noreferrer"&gt;this page&lt;/a&gt; on my blog.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FScreen_Shot_2019-07-07_at_11-d667f3ba-39e7-4de8-85dc-5cb0ff9cdb2c.33.30_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FScreen_Shot_2019-07-07_at_11-d667f3ba-39e7-4de8-85dc-5cb0ff9cdb2c.33.30_AM.png" alt="Screenshot of Pagespeed Insights"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This screenshot was taken after optimizing a few big issues. &lt;a href="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/" rel="noopener noreferrer"&gt;I ditched Disqus&lt;/a&gt;, inlined my CSS and Javascript. Performance went from being in the 50/100 range to being in the 90/100 on mobile. Not bad!&lt;/p&gt;

&lt;p&gt;One of my main complaints about Pagespeed Insights is how &lt;strong&gt;slow it is&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Like.. &lt;strong&gt;unbearably slow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The solution?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lighthouse.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Lighthouse
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/GoogleChrome/lighthouse" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; is an &lt;code&gt;npm&lt;/code&gt; package that you can install on your computer. It's basically Pagespeed Insights but on steroids. (Pagespeed Insights is based on Lighthouse)&lt;/p&gt;

&lt;p&gt;When you run it locally, you'll keep any information that get's generated from the tests. (Though, i'm sure Chrome phones home about the things you're doing.. 😬)&lt;/p&gt;

&lt;p&gt;Here's how to get started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;To install you can run:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g lighthouse
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also make sure you have some version of Google Chrome installed. I'm using &lt;a href="https://www.google.com/chrome/canary/" rel="noopener noreferrer"&gt;Chrome Canary.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then you can run it and get a report like this&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lighthouse https://www.jaredwolff.com --view
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--view&lt;/code&gt; will cause the report to be opened in your default web browser. Here is a report from the same page as earlier:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FScreen_Shot_2019-07-07_at_2-9830dfd1-91ff-47bf-9d50-419e13fb4c5c.36.47_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FScreen_Shot_2019-07-07_at_2-9830dfd1-91ff-47bf-9d50-419e13fb4c5c.36.47_PM.png" alt="Screenshot from Lighthouse"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It not only includes Performance but Accessibility, Best practices and SEO suggestions.&lt;/p&gt;

&lt;p&gt;The downside is you'll have to run this test on a page-by-page basis. I suggest you sample pages that have lots of content. &lt;em&gt;That way you're testing the worst of your site.&lt;/em&gt; Once you fix the worst, your whole site will see a performance boost!&lt;/p&gt;

&lt;h2&gt;
  
  
  Go Static
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FCopy_of_Particle_Bluetooth-385a56b6-9f74-4a2d-9ab3-fd0ef8daa8a4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FCopy_of_Particle_Bluetooth-385a56b6-9f74-4a2d-9ab3-fd0ef8daa8a4.png" alt="Static site generator logos"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember when websites were 100% HTML &amp;amp; CSS?&lt;/p&gt;

&lt;p&gt;Over the past years we went from pure HTML, to Ruby On Rails and slowly we're making our way back.&lt;/p&gt;

&lt;p&gt;The reason?&lt;/p&gt;

&lt;p&gt;Speed.&lt;/p&gt;

&lt;p&gt;Every time you visit a website running Flask, Ruby on Rails or similar it goes something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make a request for a page&lt;/li&gt;
&lt;li&gt;Server pieces your site together&lt;/li&gt;
&lt;li&gt;Server minifies and gzip&lt;/li&gt;
&lt;li&gt;Sever sends content back to the browser&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This itself, doesn't take long. When you multiply that by 1000's or 10's of 1000's you start running into problems.&lt;/p&gt;

&lt;p&gt;What if you do all the &lt;em&gt;piecing together&lt;/em&gt; once?&lt;/p&gt;

&lt;p&gt;That's the advantage of a statically generated website.&lt;/p&gt;

&lt;p&gt;Let's take a look at how it works:&lt;/p&gt;

&lt;h3&gt;
  
  
  How does a static site generator work?
&lt;/h3&gt;

&lt;p&gt;A static site generator is a collection of templates and styles. They can be assembled together to generate different content.&lt;/p&gt;

&lt;p&gt;As a backend, static site generators use markdown and (sometimes) JSON.&lt;/p&gt;

&lt;p&gt;When it's time to compile, templates are assembled. The Markdown is converted to HTML and then injected into the templates. The result? An output directory of rich "dynamic looking" pages. (Much like the one you're reading right now if you're on &lt;a href="https://www.jaredwolff.com" rel="noopener noreferrer"&gt;my site&lt;/a&gt;!)&lt;/p&gt;

&lt;p&gt;Personally, my blog is &lt;a href="https://gohugo.io" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt; powered. I've also dabbled with &lt;a href="https://middlemanapp.com" rel="noopener noreferrer"&gt;Middleman&lt;/a&gt; and &lt;a href="https://jekyllrb.com" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt;. No matter what you're looking for, you'll likely find a static site generator that will fit your needs! Netlify has a nice list of Static Site Generators sorted by popularity. &lt;a href="https://www.staticgen.com" rel="noopener noreferrer"&gt;Check it out here.&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Plugins Galore
&lt;/h3&gt;

&lt;p&gt;Static site generators no longer only compile sites. They optimize, minify and resize images. This built in functionality is another reason why to consider a statically generated site. Tap into these resources, and your site will be notably faster.&lt;/p&gt;

&lt;p&gt;For example, originally I was using &lt;code&gt;highlight.min.js&lt;/code&gt; for highlighting code. Since discovering the built in syntax highlighting in Hugo, I ditched &lt;code&gt;highlight.min.js&lt;/code&gt;. Hugo injects the CSS into the HTML for the code blocks. Leaving nicely formatted (and static) page!&lt;/p&gt;

&lt;h2&gt;
  
  
  Embedding Javascript and CSS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2Farrows-2029157_1280-76cb0525-f4bf-4f1b-aa80-344c448f0fe5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2Farrows-2029157_1280-76cb0525-f4bf-4f1b-aa80-344c448f0fe5.png" alt="Arrows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I alluded to in the earlier section, you'll take a performance hit when you have to load anything extra.&lt;/p&gt;

&lt;p&gt;Recently, Hugo got the ability to copy content from file into the final HTML code. This is fantastic for things like CSS and Javascript. This way, everything is built into your HTML file. There's no extra fetches required!&lt;/p&gt;

&lt;p&gt;For example, I place my full &lt;code&gt;stye.css&lt;/code&gt; file in the header of the site. That way all the styles get applied immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;!-- Css --&amp;gt;
    {{- $style := resources.Get "/css/style.css" -}}
    &amp;lt;style&amp;gt;
      {{ ( $style | minify ).Content | safeCSS }}
    &amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the footer, i'm exporting the minified &lt;code&gt;lazysizes.min.js&lt;/code&gt; into a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. It's important that this is loaded asap because it dictates how the rest of the site will load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;!-- Lazy Load Script --&amp;gt;
    {{- $lazysizes := resources.Get "/js/lazysizes.min.js" -}}
    &amp;lt;script&amp;gt;
      {{ ( $lazysizes | minify ).Content | safeJS }}
    &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Side Note:&lt;/strong&gt; both &lt;code&gt;style.css&lt;/code&gt; and &lt;code&gt;lazysizes.min.js&lt;/code&gt; are located in my main theme folder under &lt;code&gt;assets&lt;/code&gt;. Hugo uses the &lt;code&gt;assets&lt;/code&gt; folder to look for these files. If you have a Hugo site and you want to embed your css and javascript I recommend you &lt;a href="https://gohugo.io/hugo-pipes/bundling/#readout" rel="noopener noreferrer"&gt;check out this resource.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side Note Numero Dos:&lt;/strong&gt; As you can see above, i'm using Hugo's built in &lt;code&gt;minify&lt;/code&gt; function for the embedded items. The javascript is already minified but &lt;code&gt;style.css&lt;/code&gt; isn't. There's also another programatic way to minify all your assets. I'll go into more on that in the &lt;strong&gt;Optimizing using Gulp&lt;/strong&gt; section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use what's built in
&lt;/h2&gt;

&lt;p&gt;You may be noticing by now that importing javascript can take a toll. Using it for every feature on a website could lead to crap performance!&lt;/p&gt;

&lt;p&gt;For example, we could use a javascript library for form validation. But is there a better way?&lt;/p&gt;

&lt;p&gt;(yup)&lt;/p&gt;

&lt;p&gt;You can use the HTML5 tags like &lt;code&gt;required&lt;/code&gt; and use &lt;code&gt;pattern&lt;/code&gt; to validate in browser. If you want an example, check out the &lt;a href="https://www.jaredwolff.com/about/" rel="noopener noreferrer"&gt;contact form&lt;/a&gt; on my website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FScreen_Shot_2019-07-09_at_5-a66144a9-0394-447b-b8ab-4233e61c1152.44.10_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FScreen_Shot_2019-07-09_at_5-a66144a9-0394-447b-b8ab-4233e61c1152.44.10_AM.png" alt="Contact form Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the validation you see, is done in browser. No extra javascript. No delay in loading. 👌&lt;/p&gt;

&lt;p&gt;A great resource for all of this is here: &lt;a href="https://css-tricks.com/form-validation-part-1-constraint-validation-html/" rel="noopener noreferrer"&gt;https://css-tricks.com/form-validation-part-1-constraint-validation-html/&lt;/a&gt; Chris also has a detailed step-by-step portion on vanilla javascript validation. You can take it further if you so choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Organizing Code
&lt;/h2&gt;

&lt;p&gt;Where you put your javascript and css will affect performance. For instance, I place my javascript and CSS in strategic locations. The main stylesheet in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; otherwise the rest are in the footer or inline with the HTML. Similarly, the javascript mostly lives at the bottom of the page. That way, all the important stuff gets loaded first.&lt;/p&gt;

&lt;p&gt;Here is an example of how I organize my javascript:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
&amp;lt;/footer&amp;gt;
...

&amp;lt;script src="https://code.jquery.com/jquery-3.4.1.min.js"   integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="   crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src="https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I encourage you to experiment to see what improves your site performance. If you can go without certain javascript libraries, I highly encourage you nix em!&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazy Loading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2Fpanda-1892023_1280-cdcbd99d-7f9a-4ac3-9d32-0d01c5f67f8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2Fpanda-1892023_1280-cdcbd99d-7f9a-4ac3-9d32-0d01c5f67f8b.png" alt="Lazy Panda"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lazy loading is an effective way to defer loading of assets not in view. This improves, what Google calls, the "time to interactive" experience. An added bonus is if the visitor doesn't scroll down, the images aren't loaded. Thus saving bandwidth and money for high traffic sites.&lt;/p&gt;

&lt;p&gt;Lazy loading comes in the form of javascript. There are a few libraries out there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/aFarkas/lazysizes" rel="noopener noreferrer"&gt;Lazysizes&lt;/a&gt; (⭐️ 11k) - seems to be the most popular in this department. It is larger than some of the alternatives. I've been testing it's usefulness around lazy loading iframes and javascript content that isn't immediately necessary. For instance, I embed an iframe for the &lt;a href="https://www.jaredwolff.com/homemade-indoor-air-quality-sensor/#live-example" rel="noopener noreferrer"&gt;Google Docs chart on this post&lt;/a&gt;. On the same post I also lazy load the &lt;a href="https://www.jaredwolff.com/homemade-indoor-air-quality-sensor/#you-did-it" rel="noopener noreferrer"&gt;Youtube video towards the end of the article.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/callmecavs/layzr.js" rel="noopener noreferrer"&gt;Layzr.js&lt;/a&gt; (⭐️ 5.5k) - for images only&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ApoorvSaxena/lozad.js" rel="noopener noreferrer"&gt;lozad&lt;/a&gt; (⭐️ 5.4k) - does everything that lazysizes does. This library is focused on using the Intersection Observer API. Whereas Lazysizes uses both the Intersection Observer API and&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/malchata/yall.js" rel="noopener noreferrer"&gt;yall&lt;/a&gt; (⭐️ 800) - This library is also focused on using the Intersection Observer API.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting up Lazy Loading
&lt;/h3&gt;

&lt;p&gt;It's extremely simple to set up. I'll show you how to use &lt;code&gt;lazysizes&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Include the file in your HTML&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="lazysizes.min.js" async=""&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Or if you're using Hugo, inject it directly into your HTML like I did in &lt;strong&gt;Embedding Javascript and CSS&lt;/strong&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the &lt;code&gt;lazyload&lt;/code&gt; class to whatever you're lazy loading&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change the &lt;code&gt;src&lt;/code&gt; tag to &lt;code&gt;data-src&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Watch the Magic
&lt;/h3&gt;

&lt;p&gt;When someone visits your site, the lazy loader starts monitoring where the user is. When the visitor scrolls, it loads soon to be viewed content marked with the &lt;code&gt;lazyload&lt;/code&gt;class.&lt;/p&gt;

&lt;p&gt;Here is an example for a Youtube video embedded on &lt;a href="https://www.jaredwolff.com/homemade-indoor-air-quality-sensor/#you-did-it" rel="noopener noreferrer"&gt;this page&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;iframe class="lazyload" width="700px" height="400px" data-src="https://www.youtube.com/embed/IR2W0GmRKk8" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prevents the iframe from loading until the user has scrolled close by. Nice!&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing using Gulp
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FCopy_of_Particle_Bluetooth-f6420aa8-5c0b-40b3-b478-86b14e5782b0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FCopy_of_Particle_Bluetooth-f6420aa8-5c0b-40b3-b478-86b14e5782b0.jpg" alt="Gulp logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not everything can be optimized by Jekyll or Hugo. So what can you do?&lt;/p&gt;

&lt;p&gt;Enter &lt;code&gt;gulp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gulp&lt;/code&gt; is my tool of choice for optimizing my Hugo site. There are a wide array of well supported plugins for &lt;code&gt;gulp&lt;/code&gt;. They can do everything from optimize images to minify HTML.&lt;/p&gt;

&lt;p&gt;Here are some of my favorites:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gulp-uglify&lt;/code&gt; - minifies and compresses javascript. I only have one javascript library i'm using right now which this applies to. If you're project is Javascript heavy though, definitely look into &lt;code&gt;gulp-uglify&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gulp-htmlmin&lt;/code&gt; - minifies HTML. You can also use it to minify inline Javascript and CSS.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gulp-imagemin&lt;/code&gt; - probably the most useful gulp plugin for my purposes. Right now i'm having it resize, convert to jpg and then convert to a progressive jpeg. It's relatively fast and when combined with &lt;code&gt;gulp-cache&lt;/code&gt; it only has to be done once! It may seem complicated but the output produces image sizes that Lighthouse loves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;If you're curious, here's a snippet from my &lt;code&gt;gupfile.js&lt;/code&gt; for &lt;code&gt;imagemin&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gulp.task("resize", function() {
  return gulp
    .src(["content/**/**/*.+(png|jpg|jpeg|gif)"])
    .pipe(cache(imagemin([
      imagemingm.resize({width: 720}),
      imagemingm.convert('jpg'),
      imageminmozjpeg({quality: 80})
    ],{
    verbose: true
    }), {
      fileCache: new Cache({ tmpDir: path.join(process.env.PWD, 'node_modules'), cacheDirName: '.cache' })
    }))
    .pipe(gulp.dest("public/"));
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Some important things to notice&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I've used &lt;code&gt;**&lt;/code&gt; to denote a wildcard value for a directory name. Depending on the depth of your images you may need to add more &lt;code&gt;**/&lt;/code&gt; to your path. This works well for my path which is typically &lt;code&gt;/contents/post-name/images/image-file.jpg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Setting up other plugins that don't come with &lt;code&gt;gulp-imagemin&lt;/code&gt; can be confusing. &lt;code&gt;imageminmozjpeg&lt;/code&gt; for example, is imported separately. I set it  up at the top of my file like so: &lt;code&gt;var imageminmozjpeg = require('imagemin-mozjpeg');&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Finally, you can see that i'm using &lt;code&gt;gulp-cache&lt;/code&gt; here. Depending on where you're building your site, you may not have to define any options. I use Netlify for my site. The only way for &lt;code&gt;gulp-cache&lt;/code&gt; to work is to define &lt;code&gt;tmpDir&lt;/code&gt; and &lt;code&gt;cacheDirName&lt;/code&gt; to the &lt;code&gt;node_modules&lt;/code&gt; folder. That way, when your site is built, your cache is re-loaded. No resizing images if we don't have to!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using &lt;code&gt;gulp-cache&lt;/code&gt;, my build + deploy times went from 10 minutes to ~60 seconds. 🎉 Trading the minor increase in cache size for compute time is definitely worth it. Plus, I'm sure Netlify is happy about that!&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove the Cruft
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FCopy_of_Particle_Bluetooth-3-13292779-1b90-4ee9-8fe3-f547938260b9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FCopy_of_Particle_Bluetooth-3-13292779-1b90-4ee9-8fe3-f547938260b9.jpg" alt="Dirty hand print"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When there's convenience, there's always a tradeoff.&lt;/p&gt;

&lt;p&gt;That's what I was finding as I continued to use Google Analytics and services like Disqus. Disqus in particular, had a tendency to drive me up the wall. (Ever look at your Javascript Debug console on a site using Disqus? You'll see why..)&lt;/p&gt;

&lt;p&gt;I recently wrote a tutorial on how to switch from &lt;a href="https://www.jaredwolff.com/how-to-setup-worry-free-blog-comments-in-less-than-20-simple-steps/" rel="noopener noreferrer"&gt;Disqus to self-hosted Commento&lt;/a&gt;. There are two benefits to make the switch:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You get control of your site content. There's no 3rd party service that could go poof tomorrow. (It is up to you to make sure your content is backed up! One advantage of &lt;a href="https://www.commento.io" rel="noopener noreferrer"&gt;paying for a service&lt;/a&gt; instead of hosting).&lt;/li&gt;
&lt;li&gt;You'll get a performance boost!
I've tested the before and after and the results are in. Disqus was a major drag on network and CPU usage.
Commento, is light and resource usage is minimal. Perfect companion for a statically generated site!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As a site owner, the quality and speed is completely dependent on your site features. Remember, there may be better alternatives out there. No need to sell your soul to Google, Disqus, or anyone if you don't need to!&lt;/p&gt;

&lt;h2&gt;
  
  
  Site Audit
&lt;/h2&gt;

&lt;p&gt;As I wrap up this post, I do want to leave you with one more handy tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serpstat's&lt;/strong&gt; &lt;strong&gt;Site Audit&lt;/strong&gt; tool.&lt;/p&gt;

&lt;p&gt;I used it to crawl my site on a regular interval. It's extremely useful for catching all types of errors. For instance, just recently it caught a few images that were broken. I made a few  edits and everything was back up and running!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FScreen_Shot_2019-07-07_at_1-66610613-306c-44d6-b55a-34b75655623a.59.54_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fseven-ways-to-optimize-your-site-for-speed%2Fimages%2FScreen_Shot_2019-07-07_at_1-66610613-306c-44d6-b55a-34b75655623a.59.54_PM.png" alt="Screenshot of Serpstat Site Audit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to keep your site error free, nothing beats a site audit tool like Serpstat's!&lt;/p&gt;

&lt;h2&gt;
  
  
  You're never done
&lt;/h2&gt;

&lt;p&gt;You read right. You'll never be done.&lt;/p&gt;

&lt;p&gt;Improving performance for an actively developed site is never ending. Despite your best efforts you could spend days on the minute of optimizing images and lazy loading. When it comes down to it, how much of it is worth it? That's a question i'm still trying to answer myself!&lt;/p&gt;

&lt;p&gt;I hope you've found this article useful. What was your biggest takeaway? Have a optimization tip that I didn't mention? Sound off in the comments below!&lt;/p&gt;

</description>
      <category>web</category>
      <category>optimize</category>
    </item>
    <item>
      <title>How to Use Particle's Powerful Bluetooth Api</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Mon, 15 Jul 2019 03:33:23 +0000</pubDate>
      <link>https://dev.to/jaredwolff/how-to-use-particle-s-powerful-bluetooth-api-1gfp</link>
      <guid>https://dev.to/jaredwolff/how-to-use-particle-s-powerful-bluetooth-api-1gfp</guid>
      <description>&lt;p&gt;I was defeated.&lt;/p&gt;

&lt;p&gt;I had spent the whole night trying to get a Bluetooth Low Energy project working. It was painful. It was frustrating. I was ready to give up.&lt;/p&gt;

&lt;p&gt;That was during the early days of Bluetooth Low Energy. Since then it's gotten easier and easier to develop. The Particle Mesh Bluetooth Library is no exception.&lt;/p&gt;

&lt;p&gt;In this walkthrough, i'll show you how to use Particle's Bluetooth API. We'll configure some LEDs and watch them change over all devices in the Mesh network. We'll be using an Argon and Xenon board.&lt;/p&gt;

&lt;p&gt;Ready? Let's get started!&lt;/p&gt;

&lt;p&gt;P.S. this post is lengthy. If you want something to download, &lt;a href="https://www.jaredwolff.com/files/how-to-use-particles-powerful-bluetooth-api-pdf/" rel="noopener noreferrer"&gt;click here for a a beautifully formatted PDF.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stage 1: Setting Up Bluetooth
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.particle.io/workbench/" rel="noopener noreferrer"&gt;Download/Install Particle Workbench&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new Project. I picked a suitable location and then named it &lt;code&gt;ble_mesh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_5-4348ea15-e220-4138-8d28-a8c5de99e9fe.32.11_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_5-4348ea15-e220-4138-8d28-a8c5de99e9fe.32.11_PM.png" alt="Create a new Project in Particle Workbench"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to your &lt;code&gt;/src/&lt;/code&gt; direcory and open your &lt;code&gt;&amp;lt;your project name&amp;gt;.ino&lt;/code&gt; file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then make sure you change the version of your deviceOS to  &amp;gt; &lt;strong&gt;1.3.0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_5-9d20c90b-e27c-4609-9917-2ec1bb849727.40.15_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_5-9d20c90b-e27c-4609-9917-2ec1bb849727.40.15_PM.png" alt="Select DeviceOS Version"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Write the Code
&lt;/h3&gt;

&lt;p&gt;We want to set up a service with 3 characteristics. The characteristics relate to the intensity of the RGB LEDs respectively. Here's how to get your Bluetooth Set Up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In your &lt;code&gt;Setup()&lt;/code&gt; function enable app control of your LED&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RGB.control(true);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up your UUIDs at the top of your &lt;code&gt;.ino&lt;/code&gt; file&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const char* serviceUuid = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
const char* red         = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
const char* green       = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
const char* blue        = "6E400004-B5A3-F393-E0A9-E50E24DCCA9E";
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;UUIDs are unique identifiers or addresses. They're used to differentiate different services and characteristics on a device.&lt;/p&gt;

&lt;p&gt;The above UUIDs are used in previous Particle examples. If you want to create your own you can use  &lt;code&gt;uuidgen&lt;/code&gt; on the OSX command line. You can also go to a website like &lt;strong&gt;&lt;a href="https://www.guidgenerator.com/online-guid-generator.aspx" rel="noopener noreferrer"&gt;Online GUID Generator&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-14_at_11-343e1df6-69e8-4560-98b9-f2f54caeb7d3.28.46_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-14_at_11-343e1df6-69e8-4560-98b9-f2f54caeb7d3.28.46_AM.png" alt="Online GUID Generator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use the above settings to get your own UUID. You can then create your service and characteristic UUIDS from this generated one:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const char* serviceUuid = "b425040**0**-fb4b-4746-b2b0-93f0e61122c6"; //service
const char* red         = "b4250401-fb4b-4746-b2b0-93f0e61122c6"; //red char
const char* green       = "b4250402-fb4b-4746-b2b0-93f0e61122c6"; //green char
const char* blue        = "b4250403-fb4b-4746-b2b0-93f0e61122c6"; //blue char
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There's no right or wrong way to do this. But you have to be careful you're not using the UUIDs reserved by the Bluetooth SIG. This is highly unlikely. If you do want to double check you can go &lt;a href="https://www.bluetooth.com/specifications/gatt/characteristics/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://www.bluetooth.com/specifications/gatt/services/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For now, we'll stick with the first set of UUIDs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In &lt;code&gt;Setup()&lt;/code&gt;, initialize your service.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Set the RGB BLE service
BleUuid rgbService(serviceUuid);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the first step of "registering' your service. More on this below.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Initialize each characteristic in &lt;code&gt;Setup()&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BleCharacteristic redCharacteristic("red", BleCharacteristicProperty::WRITE_WO_RSP, red, serviceUuid, onDataReceived, (void*)red);
BleCharacteristic greenCharacteristic("green", BleCharacteristicProperty::WRITE_WO_RSP, green, serviceUuid, onDataReceived, (void*)green);
BleCharacteristic blueCharacteristic("blue", BleCharacteristicProperty::WRITE_WO_RSP, blue, serviceUuid, onDataReceived, (void*)blue);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this setup, we're going to use the &lt;code&gt;WRITE_WO_RSP&lt;/code&gt; property. This allows us to write the data and expect no response.&lt;br&gt;
I've referenced the UUIDs as the next two parameters. The first being the characteristic UUID. The second being the service UUID.&lt;/p&gt;

&lt;p&gt;The next parameter is the callback function. When data is written to this callback, this function will fire.&lt;/p&gt;

&lt;p&gt;Finally the last parameter is the context. What does this mean exactly? We're using the same callback for all three characteristics. The only way we can know which characteristic was written to (in deviceOS at least) is by setting a context. In this case we're going to use the already available UUIDs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Right after defining the characteristics, let's add them so they show up:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Add the characteristics
BLE.addCharacteristic(redCharacteristic);
BLE.addCharacteristic(greenCharacteristic);
BLE.addCharacteristic(blueCharacteristic);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set up the callback function.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Static function for handling Bluetooth Low Energy callbacks
static void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice&amp;amp; peer, void* context) {

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can do this at the top of the file (above &lt;code&gt;Setup()&lt;/code&gt;) We will define this more later.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, in order for your device to be connectable, we have to set up advertising. Place this code at the end of your &lt;code&gt;Setup()&lt;/code&gt; function&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Advertising data
BleAdvertisingData advData;

// Add the RGB LED service
advData.appendServiceUUID(rgbService);

// Start advertising!
BLE.advertise(&amp;amp;advData);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First we create a &lt;code&gt;BleAdvertisingData&lt;/code&gt; object. We add the &lt;code&gt;rgbService&lt;/code&gt; from Step 3. Finally, we can start advertising so our service and characteristics are discoverable!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Time to test
&lt;/h3&gt;

&lt;p&gt;At this point we have a minimally viable program. Let's compile it and program it to our Particle hardware. This should work with any Mesh enabled device. (Xenon, Argon, Boron)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Before we start testing, temporarily add &lt;code&gt;SYSTEM_MODE(MANUAL);&lt;/code&gt; to the top of your file. This will prevent the device connecting to the mesh network. If the device is blinking blue on startup, you'll have to set it up with the &lt;a href="https://apps.apple.com/ru/app/particle-iot/id991459054?l=en" rel="noopener noreferrer"&gt;Particle App&lt;/a&gt; before continuing.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://github.com/particle-iot/device-os/releases/tag/v1.3.0-rc.1" rel="noopener noreferrer"&gt;Download the 1.3.0-rc.1 image here.&lt;/a&gt; For Xenon, you'll need &lt;strong&gt;&lt;a href="mailto:xenon-system-part1@1.3.0-rc.1.bin"&gt;xenon-system-part1@1.3.0-rc.1.bin&lt;/a&gt;.&lt;/strong&gt; For others look for &lt;strong&gt;&lt;a href="mailto:boron-system-part1@1.3.0-rc.1.bin"&gt;boron-system-part1@1.3.0-rc.1.bin&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="mailto:argon-system-part1@1.3.0-rc.1.bin"&gt;argon-system-part1@1.3.0-rc.1.bin&lt;/a&gt;.&lt;/strong&gt; The files are at the bottom of the page under &lt;strong&gt;Assets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-14_at_11-df9b46f4-e230-484e-b69e-828526f5566e.39.39_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-14_at_11-df9b46f4-e230-484e-b69e-828526f5566e.39.39_AM.png" alt="Assets location on DeviceOS Release Page"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Put your device into DFU mode. Hold the &lt;strong&gt;Mode Button&lt;/strong&gt; and momentarily click the &lt;strong&gt;Reset&lt;/strong&gt; &lt;strong&gt;Button.&lt;/strong&gt; Continue holding the &lt;strong&gt;Mode Button&lt;/strong&gt; until the LED blinks yellow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In a command line window, change directories to where you stored the file you downloaded. In my case the command is &lt;code&gt;cd ~/Downloads/&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then run:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;particle flash --usb xenon-system-part1@1.3.0-rc.1.bin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will install the latest OS to your Xenon. Once it's done it will continue to rapidly blink yellow. Again if you have a different Particle Mesh device, change the filename to match.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In Visual Code, use the &lt;strong&gt;Command + Shift + P&lt;/strong&gt; key combination to pop up the command menu. Select &lt;strong&gt;Particle: Compile application (local)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_10-8cb8dda2-73ae-4b0d-af5b-33892d66752e.52.19_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_10-8cb8dda2-73ae-4b0d-af5b-33892d66752e.52.19_PM.png" alt="Compile application (local) choice"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fix any errors that may pop up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, open the same menu and select &lt;strong&gt;Flash application (local)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_10-4ff7bc95-58f1-497f-9edf-9eadb69e3abb.51.59_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_10-4ff7bc95-58f1-497f-9edf-9eadb69e3abb.51.59_PM.png" alt="Flash application (local) choice"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When programming is complete, pull out your phone.  Then, open your favorite Bluetooth Low Energy app. The best ones are &lt;strong&gt;&lt;a href="https://apps.apple.com/cn/app/nrf-connect/id1054362403?l=en" rel="noopener noreferrer"&gt;NRF Connect&lt;/a&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://apps.apple.com/ru/app/lightblue-explorer/id557428110?l=en" rel="noopener noreferrer"&gt;Light Blue Explorer.&lt;/a&gt;&lt;/strong&gt; I'm going to use Light Blue Explorer for this example.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Check if a device named &lt;strong&gt;"Xenon-"&lt;/strong&gt; is advertising. Insert &lt;strong&gt;&lt;/strong&gt; with the unique ID for your device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2187-aa8b88cc-f423-4b5a-8bb0-e1f75c3b7dd9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2187-aa8b88cc-f423-4b5a-8bb0-e1f75c3b7dd9.png" alt="Light Blue Explorer Scan Results"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Find your device and click the name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Look at the list of services &amp;amp; characteristics. Does it include the service and characteristic UUID's that we have set so far? For instance, does the service UUID show up as &lt;strong&gt;6E400001-B5A3-F393-E0A9-E50E24DCCA9E&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2188-d7759af0-964e-4067-9ca2-d78edd190410.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2188-d7759af0-964e-4067-9ca2-d78edd190410.png" alt="Confirm Light Blue Explorer has new characteristic UUIDs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything shows up as you expect, you're in a good place. If not go through the earlier instructions to make sure everything matches.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Stage 2: Handling Data
&lt;/h2&gt;

&lt;p&gt;The next stage of our project is to process write events. We'll be updating our &lt;code&gt;onDataReceived&lt;/code&gt; function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write the Code
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, let's create a struct that will keep the state of the LEDs. This can be done at the top of the file.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Variables for keeping state
typedef struct {
  uint8_t red;
  uint8_t green;
  uint8_t blue;
} led_level_t;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The second half of that is to create a static variable using this data type&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Static level tracking
static led_level_t m_led_level;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first two steps allows us to use one single variable to represent the three values of the RGB LED.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, let's check for basic errors inside the &lt;code&gt;onDataReceive&lt;/code&gt; function For instance we want to make sure that we're receiving only one byte.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// We're only looking for one byte
  if( len != 1 ) {
    return;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, we want to see which characteristic this event came from. We can use the &lt;code&gt;context&lt;/code&gt; variable to determine this.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Sets the global level
  if( context == red ) {
    m_led_level.red = data[0];
  } else if ( context == green ) {
    m_led_level.green = data[0];
  } else if ( context == blue ) {
    m_led_level.blue = data[0];
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember, in this case context will be equal to the pointer of either the red, green, or blue UUID string. You can also notice we're setting &lt;code&gt;m_led_level&lt;/code&gt;. That way we can update the RGB led even if only one value has changed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, once set, you can write to the &lt;code&gt;RGB&lt;/code&gt; object&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Set RGB color
    RGB.color(m_led_level.red, m_led_level.green, m_led_level.blue);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Test the Code
&lt;/h3&gt;

&lt;p&gt;Let's go through the same procedure as before to flash the device.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put your device into DFU mode. Hold the &lt;strong&gt;Mode Button&lt;/strong&gt; and click the &lt;strong&gt;Reset&lt;/strong&gt; &lt;strong&gt;Button.&lt;/strong&gt; Continue holding the &lt;strong&gt;Mode Button&lt;/strong&gt; until the LED blinks yellow.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, open the same menu and select &lt;strong&gt;Flash application (local)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_10-4ff7bc95-58f1-497f-9edf-9eadb69e3abb.51.59_PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-13_at_10-4ff7bc95-58f1-497f-9edf-9eadb69e3abb.51.59_PM.png" alt="Flash application (local) choice"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once it's done programming, connect to the device using &lt;strong&gt;Light Blue Explorer&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tap on the characteristic that applies to the red LED.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write FF&lt;/strong&gt;. The red LED should turn on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2191-b8ad0693-f4f8-4828-ae99-b0a0e204cc50.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2191-b8ad0693-f4f8-4828-ae99-b0a0e204cc50.jpg" alt="Write new value choice"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2190-2c799875-22e5-44bc-9edc-c647cf8669f6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2190-2c799875-22e5-44bc-9edc-c647cf8669f6.png" alt="Write the hex value of 0xff"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write 00&lt;/strong&gt;. The red LED should turn off.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do the same for the other two characteristics. We now have full control of the RGB LED over Bluetooth Low Energy!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Stage 3: Sharing Via Mesh
&lt;/h2&gt;

&lt;p&gt;Finally, now that we're successfully receiving BLE message, it's time to forward them on to our mesh network.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write the Code
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;First let's remove MANUAL mode. Comment out &lt;code&gt;SYSTEM_MODE(MANUAL);&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;At the top of the file let's add a variable we'll used to track if we need to publish&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Tracks when to publish to Mesh
static bool m_publish;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then initialize it in &lt;code&gt;Setup()&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Set to false at first
m_publish = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then, after setting the RGB led in the &lt;code&gt;onDataReceived&lt;/code&gt; callback, let's set it true:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Set RGB color
RGB.color(m_led_level.red, m_led_level.green, m_led_level.blue);

// Set to publish
m_publish = true;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let's add a conditional in the &lt;code&gt;loop()&lt;/code&gt; function. This will cause us to publish the LED status to the Mesh network:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if( m_publish ) {
    // Reset flag
    m_publish = false;

    // Publish to Mesh
  Mesh.publish("red", String::format("%d", m_led_level.red));
  Mesh.publish("green", String::format("%d", m_led_level.green));
  Mesh.publish("blue", String::format("%d", m_led_level.blue));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Mesh.publish&lt;/code&gt; requires a string for both inputs. Thus, we're using &lt;code&gt;String::format&lt;/code&gt; to create a string with our red, green and blue values.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then let's subscribe to the same variables in &lt;code&gt;Setup()&lt;/code&gt;. That way another device can cause the LED on this device to update as well.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mesh.subscribe("red", meshHandler);
Mesh.subscribe("green", meshHandler);
Mesh.subscribe("blue", meshHandler);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Toward the top of the file we want to create &lt;code&gt;meshHandler&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Mesh event handler
static void meshHandler(const char *event, const char *data)
{
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In this application, we need the &lt;code&gt;event&lt;/code&gt; parameter and &lt;code&gt;data&lt;/code&gt; parameter. In order use them, we have to change them to a &lt;code&gt;String&lt;/code&gt; type. That way we can use the built in conversion and comparison functions. So, inside the &lt;code&gt;meshHandler&lt;/code&gt; function add:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Convert to String for useful conversion and comparison functions
  String eventString = String(event);
  String dataString = String(data);
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally we do some comparisons. We first check if the event name matches. Then we set the value of the &lt;code&gt;dataString&lt;/code&gt; to the corresponding led level.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  // Determine which event we recieved
  if( eventString.equals("red") ) {
    m_led_level.red = dataString.toInt();
  } else if ( eventString.equals("green") ) {
    m_led_level.green = dataString.toInt();
  } else if ( eventString.equals("blue") ) {
    m_led_level.blue = dataString.toInt();
  } else {
        return;
    }

  // Set RGB color
  RGB.color(m_led_level.red, m_led_level.green, m_led_level.blue);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then at the end we set the new RGB color. Notice how I handle an unknown state by adding a &lt;code&gt;return&lt;/code&gt; statement in the &lt;code&gt;else&lt;/code&gt; section. It's always good to filter out error conditions before they wreak havoc!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Test the Code
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the Particle App on your phone&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let's set up the Argon first. &lt;strong&gt;If it's not blinking blue, hold the mode button until it's blinking blue.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_1216-f78725ab-a9f5-4270-b389-73d6dddb1f62.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_1216-f78725ab-a9f5-4270-b389-73d6dddb1f62.jpg" alt="Particle Argon with Blue LED"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: if you've already programmed the app, the LED will be off by default. &lt;strong&gt;Hold the mode button for 5 seconds and then continue.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go through the pairing process. The app walks you though all the steps. &lt;strong&gt;Make sure you remember the Admin password for your mesh network.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2194-2482aa12-082c-4786-8883-860b50b4cd53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2194-2482aa12-082c-4786-8883-860b50b4cd53.png" alt="Particle Setup App Board Choice"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2195-2d030603-e59f-4e76-9d1a-9d4f2c078a4e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2195-2d030603-e59f-4e76-9d1a-9d4f2c078a4e.png" alt="Scan the sticker"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2198-80bb83d8-818b-4cd8-a583-9262da9b5121.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2198-80bb83d8-818b-4cd8-a583-9262da9b5121.png" alt="Pairing with your Argon"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2199-e3c107dd-2704-4aa3-9781-550ad14ea7fc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2199-e3c107dd-2704-4aa3-9781-550ad14ea7fc.png" alt="Enter wifi password"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Program an Argon with the latest firmware (1.3.0) (see &lt;strong&gt;Stage 1 - Time to Test - Step 2&lt;/strong&gt; for a reminder on how to do this)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once rapidly blinking yellow, program the Argon with the Tinker app. You can download it at the &lt;a href="https://github.com/particle-iot/device-os/releases/tag/v1.3.0-rc.1" rel="noopener noreferrer"&gt;release page&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once we have a nice solid Cyan LED (connected to the Particle Cloud) we'll program the app. Use the &lt;strong&gt;Cloud Flash&lt;/strong&gt; option in the drop down menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-15_at_9-a50458b1-ade1-4d4d-818b-ef214f3fec5d.55.19_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-15_at_9-a50458b1-ade1-4d4d-818b-ef214f3fec5d.55.19_AM.png" alt="Cloud Flash option"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As far as I can tell, Particle forces any device flashed locally into safe mode when connecting to the cloud. It may be my setup. Your mileage may vary here. Best to use  &lt;strong&gt;Cloud Flash&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Make sure you select the correct deviceOS version (&lt;strong&gt;1.3.0-rc1&lt;/strong&gt;), device type (&lt;strong&gt;Argon&lt;/strong&gt;) and device name (&lt;strong&gt;What you named it during setup&lt;/strong&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect to the Xenon using the &lt;strong&gt;phone app&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect the Xenon to your Mesh network using the phone app&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Flash your Xenon using &lt;strong&gt;Cloud Flash&lt;/strong&gt;. Use the name that you gave it during the phone app setup. As long as the device is connected to Particle Cloud or in safe mode (Purple LED), it should program.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-15_at_10-2f76843b-3cee-4818-adfb-7ece4173df2d.06.32_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-15_at_10-2f76843b-3cee-4818-adfb-7ece4173df2d.06.32_AM.png" alt="Confirm board type, deviceOS version and device name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-15_at_10-8e140009-440c-480d-838e-b83c782a8c96.08.47_AM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FScreen_Shot_2019-07-15_at_10-8e140009-440c-480d-838e-b83c782a8c96.08.47_AM.png" alt="Cloud flash option"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once connected, let's get to the fun part. Open up &lt;strong&gt;Light Blue Explorer.&lt;/strong&gt; Connect to either the &lt;strong&gt;Argon&lt;/strong&gt; or the &lt;strong&gt;Xenon&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2200-e1c513ba-6689-4652-b442-e5c6f916d619.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_2200-e1c513ba-6689-4652-b442-e5c6f916d619.png" alt="Select either the Argon or Xenon"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Select one of the LED characteristics and change the value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_0627-2db8ab26-99e1-46a2-9583-97126fbe2594.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-use-particles-powerful-bluetooth-api%2Fimages%2FIMG_0627-2db8ab26-99e1-46a2-9583-97126fbe2594.jpg" alt="Argon and Xenon with red LEDs on"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The LED should change on all devices!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Final Code
&lt;/h2&gt;

&lt;p&gt;Here's the final code with all the pieces put together. You can use this to make sure you put them in the right place!!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
  * Project ble_mesh
  * Description: Bluetooth Low Energy + Mesh Example
  * Author: Jared Wolff
  * Date: 7/13/2019
  */&lt;/span&gt;

&lt;span class="c1"&gt;//SYSTEM_MODE(MANUAL);&lt;/span&gt;

&lt;span class="c1"&gt;// UUIDs for service + characteristics&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;serviceUuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"6E400001-B5A3-F393-E0A9-E50E24DCCA9E"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"6E400002-B5A3-F393-E0A9-E50E24DCCA9E"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"6E400003-B5A3-F393-E0A9-E50E24DCCA9E"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;        &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"6E400004-B5A3-F393-E0A9-E50E24DCCA9E"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Set the RGB BLE service&lt;/span&gt;
&lt;span class="n"&gt;BleUuid&lt;/span&gt; &lt;span class="nf"&gt;rgbService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceUuid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Variables for keeping state&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;led_level_t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Static level tracking&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;led_level_t&lt;/span&gt; &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Tracks when to publish to Mesh&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;m_publish&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Mesh event handler&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;meshHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Convert to String for useful conversion and comparison functions&lt;/span&gt;
  &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;eventString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;dataString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Determine which event we recieved&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;eventString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;eventString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"green"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;eventString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"blue"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dataString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Set RGB color&lt;/span&gt;
  &lt;span class="n"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Static function for handling Bluetooth Low Energy callbacks&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onDataReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;BlePeerDevice&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// We're only looking for one byte&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Sets the global level&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Set RGB color&lt;/span&gt;
  &lt;span class="n"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Set to publish&lt;/span&gt;
  &lt;span class="n"&gt;m_publish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// setup() runs once, when the device is first turned on.&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Enable app control of LED&lt;/span&gt;
  &lt;span class="n"&gt;RGB&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Init default level&lt;/span&gt;
  &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Set to false at first&lt;/span&gt;
  &lt;span class="n"&gt;m_publish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Set the subscription for Mesh updates&lt;/span&gt;
  &lt;span class="n"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;meshHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"green"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;meshHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;meshHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Set up characteristics&lt;/span&gt;
  &lt;span class="n"&gt;BleCharacteristic&lt;/span&gt; &lt;span class="n"&gt;redCharacteristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BleCharacteristicProperty&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WRITE_WO_RSP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serviceUuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onDataReceived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;BleCharacteristic&lt;/span&gt; &lt;span class="n"&gt;greenCharacteristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"green"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BleCharacteristicProperty&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WRITE_WO_RSP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serviceUuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onDataReceived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;BleCharacteristic&lt;/span&gt; &lt;span class="n"&gt;blueCharacteristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BleCharacteristicProperty&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WRITE_WO_RSP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serviceUuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onDataReceived&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Add the characteristics&lt;/span&gt;
  &lt;span class="n"&gt;BLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCharacteristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redCharacteristic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;BLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCharacteristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greenCharacteristic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;BLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addCharacteristic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blueCharacteristic&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Advertising data&lt;/span&gt;
  &lt;span class="n"&gt;BleAdvertisingData&lt;/span&gt; &lt;span class="n"&gt;advData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Add the RGB LED service&lt;/span&gt;
  &lt;span class="n"&gt;advData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;appendServiceUUID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rgbService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Start advertising!&lt;/span&gt;
  &lt;span class="n"&gt;BLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;advertise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;advData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// loop() runs over and over again, as quickly as it can execute.&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// Checks the publish flag,&lt;/span&gt;
  &lt;span class="c1"&gt;// Publishes to a variable called "red" "green" and "blue"&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;m_publish&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Reset flag&lt;/span&gt;
    &lt;span class="n"&gt;m_publish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Publish to Mesh&lt;/span&gt;
    &lt;span class="n"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;red&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"green"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;m_led_level&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial you learned how to add Bluetooth to a Particle Mesh project. As you can imagine, the possibilities are endless. For instance you can add user/administrative apps into the experience. &lt;em&gt;Now that's awesome.&lt;/em&gt; 😎&lt;/p&gt;

&lt;p&gt;You can expect more content like this in my upcoming book: &lt;strong&gt;&lt;em&gt;The Ultimate Guide to Particle Mesh&lt;/em&gt;&lt;/strong&gt;. Subscribe to my list for updates and insider content. Plus all early subscribers get a discount when it's released! &lt;a href="https://www.jaredwolff.com/the-ultimate-guide-to-particle-mesh" rel="noopener noreferrer"&gt;Click here to sign up.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bluetooth</category>
      <category>particle</category>
      <category>tutorial</category>
      <category>iot</category>
    </item>
    <item>
      <title>How to Protocol Buffer Bluetooth Low Energy Service Part 3</title>
      <dc:creator>Jared Wolff</dc:creator>
      <pubDate>Thu, 11 Jul 2019 15:19:05 +0000</pubDate>
      <link>https://dev.to/jaredwolff/how-to-protocol-buffer-bluetooth-low-energy-service-part-3-2d5p</link>
      <guid>https://dev.to/jaredwolff/how-to-protocol-buffer-bluetooth-low-energy-service-part-3-2d5p</guid>
      <description>&lt;p&gt;In &lt;a href="https://www.jaredwolff.com/how-to-define-your-own-bluetooth-low-energy-configuration-service-using-protobuf" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; we’ve learned the anatomy of a Protocol Buffer. In &lt;a href="https://www.jaredwolff.com/how-to-protocol-buffer-bluetooth-low-energy-service-part-2" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt; we’ve learned how a Bluetooth Low Energy Service gets pieced together on the Nordic NRF52 SDK. This final post brings together all the elements in an end-to-end working example.&lt;/p&gt;

&lt;p&gt;Let’s get going!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Everything Up
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-6880beab-c5c2-4184-9012-4a6e68bcebc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-6880beab-c5c2-4184-9012-4a6e68bcebc8.png" alt="Goose attack"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go look at the &lt;code&gt;Readme.md&lt;/code&gt; in each of the example repositories. (You will have to clone the repositories, &lt;a href="https://www.jaredwolff.com/files/protobuf" rel="noopener noreferrer"&gt;sign up here to get the code&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I’ve made the setup for the firmware dead simple. For the javascript side, it’s a bit more hairy but’ doable! I’ll also include the instructions here as well:&lt;/p&gt;

&lt;h3&gt;
  
  
  Firmware Setup (For Mac)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Initialize the full repository (there are submodules!): &lt;code&gt;git submodule update --init&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;protoc&lt;/code&gt; using Homebrew: &lt;code&gt;brew install protobuf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make sdk&lt;/code&gt;. This will download your SDK files.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make tools_osx&lt;/code&gt;. This will download your ARMGCC toolchain (for Mac). For other environments see below.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make gen_key&lt;/code&gt; once (and only once)! This will set up your key for DFU.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make&lt;/code&gt; and this will build your bootloader and main app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You only have to do steps 1-5 once.&lt;/p&gt;

&lt;h3&gt;
  
  
  Javascript App Setup (For Mac)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Prerequisite:&lt;/strong&gt; you will need Xcode command line tools. You can get those &lt;a href="https://developer.apple.com/download/more/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone this repo to a place on your computer&lt;/li&gt;
&lt;li&gt;Make sure you have &lt;a href="https://github.com/nvm-sh/nvm/blob/master/README.md" rel="noopener noreferrer"&gt;nvm installed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;nvm install v8.0.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;nvm install v10.15.3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;nvm use v8.0.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;yarn&lt;/code&gt; (if you don’t have yarn &lt;code&gt;npm install yarn -g&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Once installed, run &lt;code&gt;nvm use v10.15.3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then run &lt;code&gt;node index.js&lt;/code&gt; to start the example&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using NVM helps mitigate a compile issue with the Bluetooth library. You mileage may vary on this one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the heck does this work?
&lt;/h2&gt;

&lt;p&gt;In this project, the Protocol Buffer has two functions. The first as a “command” to the bluetooth device. Secondly, a “response” to that command from the device.&lt;/p&gt;

&lt;p&gt;Our example javascript app command uses “This is” as the payload. With the power of some string operations, let's make a full sentence out of it!&lt;/p&gt;

&lt;p&gt;The series of events looks something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The test app connects and sends that data using our Bluetooth Low Energy service.&lt;/li&gt;
&lt;li&gt;On the firmware decodes the message.&lt;/li&gt;
&lt;li&gt;The firmware modifies the original data by adding “ cool.” Resulting in "This is cool"&lt;/li&gt;
&lt;li&gt;The firmware encodes the payload and makes the data available for reading.&lt;/li&gt;
&lt;li&gt;The app finally reads the characteristic, decodes and displays the result!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Seems complicated but there's some benefits to this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In Protocol Buffers, data structures are well defined. This means that it has some great error checking abilities. If you're using any type of data structure you may have to code your own data validation.&lt;/li&gt;
&lt;li&gt;Encoding and decoding is simple and straight forward. You can encode same data on different platforms and always get the same decoded result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Want to get the example up and running? Seeing is believing after all. Let's do this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is This Thing On?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-c72fcf71-f0db-49e5-92d7-44fe601e75fa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-c72fcf71-f0db-49e5-92d7-44fe601e75fa.png" alt="Phone in hand running NRF connect"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you're done with the setup (above), let's get this firmware programmed! Lucky for you it's only two quick steps!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Plug in your NRF52 DK board&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;make flash_all&lt;/code&gt;. (This compiles and flashes &lt;em&gt;all&lt;/em&gt; the code for the project.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once flashed, the easiest way to know if things are working is when Led 1 is blinking. That means it’s advertising and ready for a connection.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-6c58e8c5-d005-4b61-a32b-29eea61b1ce7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-6c58e8c5-d005-4b61-a32b-29eea61b1ce7.png" alt="NRF52 DK"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s make sure it’s advertising correctly though. You can grab a tool like NRF Connect for &lt;a href="https://itunes.apple.com/cn/app/nrf-connect/id1054362403?l=en" rel="noopener noreferrer"&gt;iOS&lt;/a&gt; or Android. Or, you can grab LightBlue. (Also available for &lt;a href="https://apps.apple.com/ru/app/lightblue-explorer/id557428110?l=en" rel="noopener noreferrer"&gt;iOS&lt;/a&gt; and Android)&lt;/p&gt;

&lt;p&gt;Open up one of the app and scan for advertising devices. You’ll be looking for &lt;strong&gt;Nordic_Buttonless&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-062f5e84-ec22-4525-a591-4330a55fe30f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-062f5e84-ec22-4525-a591-4330a55fe30f.png" alt="BLE Apps scanning"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now let’s connect and double check our connectable services &amp;amp; characteristics:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-0466566a-1d8a-4e8b-a1c9-f2f76faa116b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-0466566a-1d8a-4e8b-a1c9-f2f76faa116b.png" alt="BLE Apps showing services"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see there are two services. One is Nordic’s DFU service. The other is our Protocol Buffer service!&lt;/p&gt;

&lt;p&gt;You can compare &lt;code&gt;PROTOBUF_UUID_BASE&lt;/code&gt; in &lt;code&gt;ble_protobuf.h&lt;/code&gt; with the UUID of the “Unknown Service”. Nordic’s chips are Little Endian while the data here is Big Endian format. (i.e. you’ll have to reverse the bytes to see that they’re the same!)&lt;/p&gt;

&lt;p&gt;You can even click in further to see the characteristics in NRF Connect. In the case of LightBlue, the characteristic UUIDs are already shown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Javascript App
&lt;/h2&gt;

&lt;p&gt;So, once we’re up and advertising, it’s time to run the app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-7e92193a-4dbe-4e08-9ebe-ee41be501580.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jaredwolff.com%2Fhow-to-protocol-buffer-bluetooth-low-energy-service-part-3%2Fimages%2FUntitled-7e92193a-4dbe-4e08-9ebe-ee41be501580.png" alt="Bluetooth javascript app results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simply change to the &lt;code&gt;ble-protobuf-js&lt;/code&gt; directory and run &lt;code&gt;node index.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you've installed everything correctly, you should start seeing output like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example started
scanning
peripheral with ID 06f9b62ec5334454875b9f53d2f3fa74 found with name: Nordic_Buttonles
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It should then immediately connect and send data to the device. It should receive the response immediately and print it to the screen.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;connected to Nordic_Buttonles
Sent: This is
Received: This is cool.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Bingo!&lt;/p&gt;

&lt;h2&gt;
  
  
  You’ve made it!
&lt;/h2&gt;

&lt;p&gt;🎉 Congrats if you made it this far through the tutorial. You should now be feeling confident enough to start playing with the software code. Maybe you’ll cook up something cool?&lt;/p&gt;

&lt;p&gt;Here’s links to some resources that you may find handy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/protocol-buffers" rel="noopener noreferrer"&gt;Protocol Buffer documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.github.com/nanopb/nanopb" rel="noopener noreferrer"&gt;NanoPB&lt;/a&gt; - (the implementation of Protocol Buffers we used for this project)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nordicsemi.com/DocLib/Content/SDK_Doc/nRF5_SDK/v15-2-0/index" rel="noopener noreferrer"&gt;Nordic SDK Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Again, here are the links to &lt;a href="https://www.jaredwolff.com/how-to-define-your-own-bluetooth-low-energy-configuration-service-using-protobuf" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt; and &lt;a href="https://www.jaredwolff.com/how-to-protocol-buffer-bluetooth-low-energy-service-part-2" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you haven’t already, &lt;a href="https://www.jaredwolff.com/files/protobuf" rel="noopener noreferrer"&gt;get all the code for this project&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Protocol Buffers are quite versatile and convenient. Using them with Bluetooth Low Energy service is one way that you can take advantage. As the connected world of IoT continues to expand, I have no doubt that we'll be seeing more use cases in the future!&lt;/p&gt;

&lt;p&gt;I hope this has inspired you to incorporate Protocol Buffers into your own project(s). Make sure you &lt;a href="https://www.jaredwolff.com/files/protobuf" rel="noopener noreferrer"&gt;download the sample code&lt;/a&gt; and get started right now.&lt;/p&gt;

&lt;p&gt;Has this tutorial been useful for you? Let me know what you think.&lt;/p&gt;

</description>
      <category>bluetoothlowenergy</category>
      <category>protocolbuffers</category>
    </item>
  </channel>
</rss>
