mirror of
https://github.com/lupyuen/lupyuen.github.io.git
synced 2025-01-13 09:08:30 +08:00
1797 lines
No EOL
137 KiB
HTML
1797 lines
No EOL
137 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<link rel="stylesheet" type="text/css" href="/_static/css/banner-styles.css?v=bsmaklHF" />
|
||
<link rel="stylesheet" type="text/css" href="/_static/css/iconochive.css?v=qtvMKcIJ" />
|
||
<link rel="canonical" href="https://lupyuen.org/articles/coding-nrf52-with-rust-and-apache-mynewt-on-visual-studio-code.html" />
|
||
<!-- End Wayback Rewrite JS Include -->
|
||
<title data-rh="true">Coding nRF52 with Rust and Apache Mynewt on Visual Studio Code</title>
|
||
<meta data-rh="true" charset="utf-8" />
|
||
<meta data-rh="true" name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
|
||
<meta data-rh="true" name="theme-color" content="#000000" />
|
||
<meta data-rh="true" property="og:type" content="article" />
|
||
<meta data-rh="true" property="article:published_time" content="2019-11-26T04:43:21.543Z" />
|
||
<meta data-rh="true" name="title" content="Coding nRF52 with Rust and Apache Mynewt on Visual Studio Code" />
|
||
<meta data-rh="true" property="og:title" content="Coding nRF52 with Rust and Apache Mynewt on Visual Studio Code" />
|
||
<meta data-rh="true" property="twitter:title"
|
||
content="Coding nRF52 with Rust and Apache Mynewt on Visual Studio Code" />
|
||
<meta data-rh="true" name="description"
|
||
content="Build an iBeacon with Apache NimBLE… Connect nRF52 to ST-Link / Raspberry Pi… And more!" />
|
||
<meta data-rh="true" property="og:description"
|
||
content="Build an iBeacon with Apache NimBLE… Connect nRF52 to ST-Link / Raspberry Pi… And more!" />
|
||
<meta data-rh="true" property="twitter:description"
|
||
content="Build an iBeacon with Apache NimBLE… Connect nRF52 to ST-Link / Raspberry Pi… And more!" />
|
||
<meta data-rh="true" name="twitter:card" content="summary_large_image" />
|
||
<meta data-rh="true" name="twitter:creator" content="@MisterTechBlog" />
|
||
<meta data-rh="true" name="author" content="Lup Yuen Lee 李立源" />
|
||
<meta data-rh="true" name="robots" content="index,follow" />
|
||
<meta data-rh="true" name="referrer" content="unsafe-url" />
|
||
<meta data-rh="true" name="twitter:label1" value="Reading time" />
|
||
<meta data-rh="true" name="twitter:data1" value="26 min read" />
|
||
<meta property="og:image"
|
||
content="https://lupyuen.github.io/images/legacy/o1.jpeg">
|
||
|
||
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
|
||
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
|
||
<link rel="stylesheet" type="text/css" href="../normalize.css">
|
||
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
|
||
|
||
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
|
||
<link rel="stylesheet" type="text/css" href="../prism.css">
|
||
<script src="../storage.js"></script><noscript>
|
||
<link rel="stylesheet" href="../noscript.css"></noscript>
|
||
<link rel="shortcut icon" href="../favicon.ico">
|
||
<style type="text/css">
|
||
#crate-search {
|
||
background-image: url("../down-arrow.svg");
|
||
}
|
||
a {
|
||
color: #77d;
|
||
}
|
||
</style>
|
||
<!-- End scripts/rustdoc-header.html -->
|
||
|
||
</head>
|
||
|
||
<body>
|
||
<div id="root">
|
||
<div class="a b c">
|
||
<article>
|
||
<section class="cj ck cl cm ak cn ce n co"></section><span class="r"></span>
|
||
<div>
|
||
<div class="cp u cq cr cs ct"></div>
|
||
<section class="cu cv cw cx cy">
|
||
<div class="cz ak">
|
||
<div class="figure da cz ak paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o1.jpeg" /></p>
|
||
|
||
|
||
<figcaption><p><em>EBYTE E73-TBB Development Board with onboard
|
||
Nordic nRF52832 Microcontroller, connected to ST-Link USB Programmer. Shot with Sony NEX-7.
|
||
</em></p></figcaption>
|
||
</div>
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<div>
|
||
<div id="a6d1" class="ea eb ec bk ed b ee ef eg eh ei ej ek el em en eo">
|
||
<h1 class="ed b ee ep eg eq ei er ek es em et ec">Coding nRF52 with Rust and Apache Mynewt on Visual
|
||
Studio Code</h1>
|
||
</div>
|
||
<div class="eu">
|
||
<div class="n ev ew ex ey">
|
||
<div class="o n">
|
||
<div><a rel="noopener"
|
||
href="https://lupyuen.github.io">
|
||
<div class="dd ez fa">
|
||
<div class="bs n fb o p cp fc fd fe ff fg ct"></div>
|
||
</div>
|
||
</a></div>
|
||
<div class="fi ak r">
|
||
<div class="n">
|
||
<div style="flex:1"><span class="bj b bk bl bm bn r ec q">
|
||
<div class="fj n o fk"><span class="bj dy ds bl di fl fm fn fo fp ec"><a
|
||
class="at au av aw ax ay az ba bb bc fq bf bg bh bi" rel="noopener"
|
||
href="https://lupyuen.github.io">Lup
|
||
Yuen Lee 李立源</a></span>
|
||
|
||
</div>
|
||
</span></div>
|
||
</div><span class="bj b bk bl bm bn r bo bp"><span class="bj dy ds bl di fl fm fn fo fp bo">
|
||
<div><a class="at au av aw ax ay az ba bb bc fq bf bg bh bi" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/coding-nrf52-with-rust-and-apache-mynewt-on-visual-studio-code">
|
||
3 Oct 2019</a> <!-- -->·
|
||
<!-- -->
|
||
<!-- -->26
|
||
<!-- --> min read
|
||
</div>
|
||
</span></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p id="f66d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The <a
|
||
href="https://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.0.pdf"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr hh">nRF52
|
||
Microcontroller</strong></a> by Nordic Semiconductor is an awesome gadget with powerful Bluetooth
|
||
Low Energy networking capability. It’s affordable too… For under $8, I can buy an <a
|
||
href="http://www.ebyte.com/product-view-news.aspx?id=644"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">EBYTE E73-TBB Development
|
||
Board</a> with onboard nRF52 (photo above). And it works like a supercharged <a
|
||
href="https://en.wikipedia.org/wiki/Micro_Bit"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">BBC micro:bit</a> (based on the
|
||
older nRF51) in a smaller, cheaper form factor!</p>
|
||
<p id="ca61" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Powered by an <strong
|
||
class="gr hh">Arm Cortex-M4 CPU </strong>(hardware floating-point) with <strong class="gr hh">64 KB
|
||
of RAM</strong> and <strong class="gr hh">512 KB of Flash ROM</strong>, the nRF52 has plenty of
|
||
capacity to run modern embedded platforms… Like <a
|
||
href="https://mynewt.apache.org/"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr hh">Apache
|
||
Mynewt</strong></a> realtime OS and <strong class="gr hh">Embedded Rust</strong>!</p>
|
||
<p id="8503" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">But what tools
|
||
would we use to code the nRF52? Will we get locked in with proprietary IDEs and programming
|
||
dongles?</em></p>
|
||
<p id="de66" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Great News: The nRF52 works with
|
||
popular open-source tools on Windows and macOS like <a
|
||
href="https://code.visualstudio.com/"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr hh">Visual
|
||
Studio Code</strong></a><strong class="gr hh">, </strong><a
|
||
href="http://openocd.org/" class="at cg hd he hf hg"
|
||
target="_blank" rel="noopener nofollow"><strong class="gr hh">OpenOCD</strong></a><strong
|
||
class="gr hh">, </strong><a
|
||
href="https://www.rust-lang.org/"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong
|
||
class="gr hh">Rust</strong></a><strong class="gr hh"> and </strong><a
|
||
href="https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180924134644&SearchText=st-link+v2&switch_new_app=y"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong
|
||
class="gr hh">ST-Link</strong></a>… I’ll show you how, right now! (ST-Link is not really
|
||
open-source but <a
|
||
href="https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180924134644&SearchText=st-link+v2&switch_new_app=y"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">it’s only $2</a>!)</p>
|
||
<p id="d918" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Even if you’re new to nRF52,
|
||
Bluetooth, Mynewt, Visual Studio Code, … You’re welcome to skim! We’ll cover a broad range of topics.
|
||
including…</p>
|
||
<p id="edeb" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">1️⃣ <strong
|
||
class="gr hh">Bluetooth Low Energy</strong> and the <strong class="gr hh">iBeacon</strong> protocol
|
||
</p>
|
||
<p id="c689" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">2️⃣ Build your own iBeacon with
|
||
nRF52 and <strong class="gr hh">Apache NimBLE</strong></p>
|
||
<p id="4b2b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">3️⃣ <strong
|
||
class="gr hh">Embedded Rust</strong> programming on nRF52</p>
|
||
<p id="0096" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">4️⃣ Programming <strong
|
||
class="gr hh">IoT Sensors</strong> with Mynewt and Rust</p>
|
||
<p id="79d7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">5️⃣ Why Rust not C?</p>
|
||
<p id="7d68" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">6️⃣ Code, flash and debug the
|
||
nRF52 with <strong class="gr hh">Visual Studio Code</strong> and <strong
|
||
class="gr hh">OpenOCD</strong></p>
|
||
<p id="0080" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">7️⃣ <strong class="gr hh">Remove
|
||
Flash Protection</strong> from your nRF52 with a <strong class="gr hh">Raspberry Pi</strong></p>
|
||
<p id="97cf" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">8️⃣ Plus upcoming topics: <strong
|
||
class="gr hh">Bluetooth Mesh</strong> and <strong class="gr hh">PineTime Smart Watch</strong>!</p>
|
||
<p id="b990" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">There’s something
|
||
for everyone in this article</em>! The source code for this article may be found in the <code
|
||
class="dm hj hk hl hm b">nrf52</code> branch of this repository…</p>
|
||
<div class="hn ho hp hq hr hs"><a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52"
|
||
rel="noopener nofollow">
|
||
<section class="hv cl cm ak ce n ar hw hx hy hz ia ib ic id ie if ig ih ii ij ik">
|
||
<div class="il n co p im in">
|
||
<h2 class="bj io ip bl ec">
|
||
<div class="di ht fm fn hu fp">lupyuen/stm32bluepill-mynewt-sensor</div>
|
||
</h2>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="3325" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">What’s an iBeacon?</h1>
|
||
<p id="d546" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">There was a time… (When people
|
||
still shopped at physical retail stores…) You could walk into your favourite store and your phone
|
||
would <em class="hi">“Ding!”</em> with a notification specially customised for you… <em
|
||
class="hi">“Save up to 40% on Baby and Kids Products!”</em></p>
|
||
<p id="f4b6" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">What is this magic
|
||
that senses your mere presence… And summons a unique offer… Just for you?</em></p>
|
||
<p id="2866" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">That magic is called <a
|
||
href="https://en.wikipedia.org/wiki/IBeacon"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong
|
||
class="gr hh">iBeacon</strong></a>. It’s a wireless protocol released by Apple in 2013 for
|
||
detecting Bluetooth Low Energy devices nearby (within a few metres). iBeacon Transmitters are dumb
|
||
devices planted in the store that broadcast an iBeacon ID that’s specific to the store.</p>
|
||
<p id="1e9d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Your phone needs to have an app
|
||
installed that indicates which iBeacon ID it’s seeking. When your phone detects a nearby iBeacon
|
||
Transmitter with a matching iBeacon ID, it wakes up the app so that the app can send you a custom
|
||
notification.</p>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o2.png" /></p>
|
||
|
||
</div>
|
||
<p id="4d90" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In reality it’s hard to detect
|
||
Bluetooth 4 iBeacons reliably because of conflicts with WiFi, which also operates in the crowded 2.4
|
||
GHz airwaves (see <a
|
||
href="https://www.hindawi.com/journals/misy/2016/8367638/"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://www.hindawi.com/journals/misy/2016/8367638/</a>).</p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o3.png" /></p>
|
||
|
||
<figcaption><p><em>Our nRF52 detected as an iBeacon (“My
|
||
iBeacon”) in the “Locate Beacon” app: <a
|
||
href="https://apps.apple.com/us/app/locate-beacon/id738709014"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://apps.apple.com/us/app/locate-beacon/id738709014</a></em></p></figcaption>
|
||
</div>
|
||
<p id="f401" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In this tutorial we’ll program
|
||
our nRF52 to be an iBeacon Transmitter, because…</p>
|
||
<ol class="">
|
||
<li id="b4bc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc km kn ko">iBeacon is the
|
||
simplest Bluetooth LE protocol to implement (and troubleshoot)</li>
|
||
<li id="b6d5" class="gp gq ec bk gr b gs kp gu kq gw kr gy ks ha kt hc km kn ko">It’s easy to use our
|
||
phones to verify that our nRF52 is indeed working correctly as a Bluetooth LE transmitter</li>
|
||
<li id="2833" class="gp gq ec bk gr b gs kp gu kq gw kr gy ks ha kt hc km kn ko">Nostalgia…<em
|
||
class="hi"> iBeacon actually served a purpose in the real world!</em> (But if you decide to
|
||
implement iBeacons today, beware of the iBeacon security implications, especially <a
|
||
href="https://en.wikipedia.org/wiki/IBeacon#Spoofing"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">iBeacon spoofing</a>)</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="d379" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">nRF52 is Radio-Capable</h1>
|
||
<p id="11de" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">nRF52 is similar to other
|
||
microcontrollers (like the STM32 F103 found in Blue Pill)… Except that the nRF52 has 2.4 GHz Radio
|
||
capabilities not found in most other microcontrollers.</p>
|
||
<p id="2044" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">2.4 GHz is used
|
||
for WiFi… Does this mean that the nRF52 can talk WiFi?</em></p>
|
||
<p id="e9be" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Not quite… WiFi protocols are
|
||
highly complex, beyond what the nRF52 can handle. Specialised microcontrollers like ESP8266 are better
|
||
at handling WiFi.</p>
|
||
<p id="4f46" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">But nRF52 is perfect for
|
||
Bluetooth Low Energy (LE) protocols, including iBeacon. Note that Bluetooth LE is not compatible with
|
||
the older standard Bluetooth. So we can’t operate our nRF52 like a classic Bluetooth tethered network
|
||
device.</p>
|
||
<p id="b18d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">nRF52 doesn’t come with hardcoded
|
||
firmware that enables the Bluetooth LE functions… We need to load our own Bluetooth LE firmware. Let’s
|
||
discuss two options: Nordic SoftDevice and Apache NimBLE.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="dc0d" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Nordic SoftDevice</h1>
|
||
<p id="77d7" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Most nRF52 developers would
|
||
probably use <a
|
||
href="https://infocenter.nordicsemi.com/pdf/S112_SDS_v3.1.pdf"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Nordic SoftDevice</a>. This is the
|
||
standard firmware provided by Nordic Semiconductor that implements the Bluetooth LE functions.</p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o4.png" /></p>
|
||
|
||
<figcaption><p><em>Nordic SoftDevice Architecture. From <a
|
||
href="https://infocenter.nordicsemi.com/topic/struct_nrf52/struct/nrf52_softdevices.html"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://infocenter.nordicsemi.com/topic/struct_nrf52/struct/nrf52_softdevices.html</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="3ffc" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The firmware runs as a base
|
||
system layer underneath our application code and RTOS.</p>
|
||
<p id="bd36" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">SoftDevice reserves some hardware
|
||
resources for itself, like the radio transceiver, some timers and some ROM+RAM.</p>
|
||
<p id="9935" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The remaining resources would be
|
||
available for our application, which would call the <a
|
||
href="https://infocenter.nordicsemi.com/pdf/S112_SDS_v3.1.pdf"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">SoftDevice API</a> to perform
|
||
Bluetooth LE functions and receive notifications.</p>
|
||
<p id="b48a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">What if we wish to
|
||
experiment with the Bluetooth LE implementation… Trace it to see how it works, tweak it to improve
|
||
it, or even roll out a new Bluetooth LE protocol?</em></p>
|
||
<p id="a39d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">SoftDevice is clearly not meant
|
||
for experimentation… Apache NimBLE is perfect for that!</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="9ea4" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Apache NimBLE</h1>
|
||
<p id="4c92" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc"><a
|
||
href="https://mynewt.apache.org/latest/network/docs/index.html"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Apache NimBLE</a> is an
|
||
open-source Bluetooth LE stack that <strong class="gr hh">completely replaces SoftDevice</strong> on
|
||
nRF51 and nRF52 chipsets. It’s designed to run with the Apache Mynewt embedded OS, so NimBLE feels
|
||
like a typical Mynewt task.</p>
|
||
<p id="3ed9" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Apache NimBLE is
|
||
the Bluetooth LE implementation that we’re adopting for this tutorial.</em></p>
|
||
<p id="2f2f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In this tutorial we’ll often
|
||
refer to NimBLE as Mynewt… Because NimBLE is so seamlessly integrated with Mynewt. Just note that
|
||
Mynewt and NimBLE actually belong to two different code repositories…</p>
|
||
<p id="f73c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Mynewt: <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/apache/mynewt-core" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">https://github.com/apache/mynewt-core</a></code>
|
||
</p>
|
||
<p id="8322" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">NimBLE: <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/apache/mynewt-nimble" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">https://github.com/apache/mynewt-nimble</a></code>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="ffd4" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Why Visual Studio Code with
|
||
ST-Link (instead of nRFgo Studio with J-LINK)</h1>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o5.jpeg" /></p>
|
||
|
||
|
||
<figcaption><p><em>nRF52 Development Board connected to ST-Link
|
||
USB Programmer</em></p></figcaption>
|
||
</div>
|
||
<p id="d46f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">If you’re already familiar with
|
||
nRF52 development tools like <a
|
||
href="https://www.nordicsemi.com/Software-and-Tools/Development-Tools/nRFgo-Studio"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">nRFgo Studio</a> and <a
|
||
href="https://en.wikipedia.org/wiki/Segger_Microcontroller_Systems#J-Link"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">J-LINK</a>… This tutorial will
|
||
open your eyes!</p>
|
||
<p id="75b2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">My previous tutorials have been
|
||
based on open-source tools and affordable, accessible hardware. For this tutorial we’ll be reusing <a
|
||
href="https://code.visualstudio.com/"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Visual Studio Code</a> and ST-Link
|
||
(with <a href="http://openocd.org/"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">OpenOCD</a>).</p>
|
||
<p id="1ad7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Yes, the
|
||
open-source tools we use for coding STM32 may also be used for nRF52!</em></p>
|
||
</div>
|
||
</div>
|
||
<div class="cz">
|
||
<div class="n p">
|
||
<div class="ky kz la lb lc ld ag le ah lf aj ak">
|
||
<div class="figure js jt ju jv jw cz lh li paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o6.png" /></p>
|
||
|
||
|
||
<figcaption><p><em>Debugging Embedded Rust on nRF52 with Visual
|
||
Studio Code and ST-Link</em></p></figcaption>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<p id="9090" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The generic <a
|
||
href="https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180924134644&SearchText=st-link+v2"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">ST-Link V2 USB Adapter</a><strong
|
||
class="gr hh"> </strong>costs under $2 (search <a
|
||
href="https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180924134644&SearchText=st-link+v2&switch_new_app=y"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">AliExpress</a> for <code
|
||
class="dm hj hk hl hm b">st-link v2</code>) and works perfectly fine for flashing and debugging the
|
||
nRF52… <strong class="gr hh">Except for removing nRF52 flash protection.</strong></p>
|
||
<p id="fb3e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">How is ST-Link
|
||
different from J-LINK, since both are used for flashing and debugging Arm microcontrollers?</em></p>
|
||
<p id="2442" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">ST-Link and J-LINK are both Arm
|
||
SWD (Serial Wire Debug) Programmers. ST-Link is known as a High-Level Adapter… ST-Link doesn’t
|
||
implement all SWD functions, just the <em class="hi">minimal set of high-level functions needed for
|
||
flashing and debugging. </em>Thus ST-Link can’t be used for removing the nRF52 flash protection.</p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o7.jpeg" /></p>
|
||
|
||
|
||
<figcaption><p><em>Removing nRF52 flash ROM protection with
|
||
Raspberry Pi</em></p></figcaption>
|
||
</div>
|
||
<p id="0328" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">If your nRF52 flash ROM is
|
||
protected (and ST-Link refuses to flash your device), you may use a Raspberry Pi to remove the
|
||
protection.</p>
|
||
<p id="15e5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This only needs to be done once
|
||
(and ST-Link will work fine after that).</p>
|
||
<p id="dc60" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Check the instructions in the
|
||
section <em class="hi">“Advanced Topic: Remove nRF52 Flash Protection” </em>at the end of this
|
||
article.</p>
|
||
<p id="1129" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Welcome nRF52 (and
|
||
nRF51)! Come join STM32 in the Open Source Party… Rust included!</em></p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="cz ak">
|
||
<div class="figure js jt ju jv jw cz ak paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o8.png" /></p>
|
||
|
||
|
||
<figcaption><p><em>Mynewt Project Structure based on <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="a2c8" class="jb jc ec bk bj io ee ln eg lo jf lp jh lq jj lr jl">Mynewt Project Structure</h1>
|
||
<p id="e168" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Mynewt is a lightweight embedded
|
||
operating system that pulls in only the modules that it needs to create the firmware image. Here are
|
||
the files in our Mynewt project… (<a class="at cg hd he hf hg" target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/install-apache-mynewt-and-embedded-rust-for-nrf52-and-visual-studio-code-on-windows-and-macos">Check
|
||
this article</a> if you wish to download the source code and browse with Visual Studio Code)</p>
|
||
<p id="e060" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">1️⃣ <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/apps" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">apps</a></code>:
|
||
<strong class="gr hh">C Source Code for Bootloader and Application</strong></p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o9.png" /></p>
|
||
|
||
</div>
|
||
<p id="9ed4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This is where we put our
|
||
Bootloader and Application source code in C. The Mynewt build script will compile the code here into
|
||
the Bootloader and Application Firmware Images.</p>
|
||
<p id="6089" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/apps/boot_stub/src" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">apps/boot_stub/src</a></code>:
|
||
C source code for our Bootloader. We’re using a simple Stub Bootloader: Upon startup it doesn’t do
|
||
anything, it just jumps to the Application.</p>
|
||
<p id="e247" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/apps/my_sensor_app/src" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">apps/my_sensor_app/src</a></code>:
|
||
C source code for our Application. The iBeacon code is located in <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/apps/my_sensor_app/src/ble.c" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">ble.c</a></code>.
|
||
We’ll cover this in a while.</p>
|
||
<p id="1f86" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">2️⃣ <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/rust" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">rust</a></code>:
|
||
<strong class="gr hh">Rust Source Code</strong></p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o10.png" /></p>
|
||
|
||
|
||
</div>
|
||
<p id="8303" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">All Rust code is placed in this
|
||
folder. Mynewt doesn’t support Rust officially (yet), so I created a custom Application Build Script
|
||
for Mynewt that injects Rust Application code into the Mynewt Application Build. (We’ll soon discover
|
||
that the Rust code was injected in a sneaky way…)</p>
|
||
<p id="4a6a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This means that we can write our
|
||
<code class="dm hj hk hl hm b">main()</code> function in Rust and call other Rust modules and crates.
|
||
Since Rust supports the calling of C functions, we may call the Mynewt API from Rust as well. (Though
|
||
calling the Mynewt API through a proper Rust Wrapper is preferred… More about this later)</p>
|
||
<p id="0f81" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s the Rust code in our
|
||
Mynewt Project…</p>
|
||
<p id="bff9" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/rust/app/src" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">rust/app/src</a></code>:
|
||
Rust source code for our Application. The <code class="dm hj hk hl hm b">main()</code> function is
|
||
defined in <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">lib.rs</a></code>,
|
||
it’s called when our device starts up. We’ll cover this in a while.</p>
|
||
<p id="f52f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/rust/mynewt/src" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">rust/mynewt/src</a></code>:
|
||
Rust Wrappers for Mynewt API. It’s possible to call the Mynewt API via <code
|
||
class="dm hj hk hl hm b">extern</code> declarations in Rust, but that wouldn’t be efficient.
|
||
(Imagine converting Rust strings to null-terminated C strings for every <code
|
||
class="dm hj hk hl hm b">extern</code> call.) Also we wouldn’t be able to exploit the power of Rust
|
||
Macros, Iterators, Error Handling, …</p>
|
||
<p id="84b4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Thus I have created Rust Wrappers
|
||
that allow Rust applications to call the Mynewt API in a safe and simple way. We’ll see examples of
|
||
this in a while.</p>
|
||
<p id="c632" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/rust/macros/src" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">rust/macros/src</a></code>:
|
||
Rust Macros for generating Rust Wrappers. Most of the Rust Wrappers were automatically generated with
|
||
the <code class="dm hj hk hl hm b">bindgen</code> tool and Rust Procedural Macros. These macros are
|
||
invoked only during Rust compilation, not at runtime.</p>
|
||
<p id="5e56" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">3️⃣ <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/libs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">libs</a></code>:
|
||
<strong class="gr hh">Custom Mynewt Libraries used by our Application</strong></p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o11.png" /></p>
|
||
|
||
|
||
</div>
|
||
<p id="80ff" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">These are C libraries that I have
|
||
created to make Mynewt more friendly for embedded developers. <code
|
||
class="dm hj hk hl hm b">semihosting_console</code> allows debugging messages to be displayed in the
|
||
Visual Studio Code Debugger (without using a serial port). <code
|
||
class="dm hj hk hl hm b">temp_stub</code> is a Mynewt Driver that simulates a Temperature Sensor,
|
||
used in our Rust application.</p>
|
||
<p id="fdde" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b">rust_app</code> is a Stub Library for injecting the compiled Rust
|
||
Application code. Our build script will bundle the compiled Rust Application code (including external
|
||
crates) into <code class="dm hj hk hl hm b">rust_app</code>, which gets linked into the Application
|
||
Firmware. (Remember our <code class="dm hj hk hl hm b">main()</code> function in Rust? It gets bundled
|
||
into <code class="dm hj hk hl hm b">rust_app</code>)</p>
|
||
<p id="ce1b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Similarly, <code
|
||
class="dm hj hk hl hm b">rust_libcore</code> is a Stub Library for injecting the Rust Core Library
|
||
into the Application Firmware. The Rust Core Library is part of the Rust Compiler and it’s needed for
|
||
core functions (like manipulating strings). Note that we’re not using the full Rust Standard Library,
|
||
which contains lots of code that’s irrelevant for embedded platforms.</p>
|
||
<p id="aaf3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">4️⃣ <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/hw/bsp" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">hw/bsp</a></code>:
|
||
<strong class="gr hh">Board Support Packages for Mynewt</strong></p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o12.png" /></p>
|
||
|
||
|
||
</div>
|
||
<p id="04a8" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">A Board Support Package contains
|
||
information, scripts and drivers necessary to build Mynewt for our microcontroller (nRF52832) and the
|
||
associated peripherals on our microcontroller board: flash memory, LEDs, UART ports, …</p>
|
||
<p id="d76f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/hw/bsp/nrf52" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">hw/bsp/nrf52</a></code>:
|
||
Board Support Package for our nRF52 microcontroller board. This is a clone of the official <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/apache/mynewt-core/tree/master/hw/bsp/ada_feather_nrf52" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">ada_feather_nrf52</a></code>
|
||
Board Support Package, with the <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/hw/bsp/nrf52/include/bsp/bsp.h#L42-L49"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">LED and Button settings</a>
|
||
customised for the EBYTE E73-TBB Development Board. (You should update these settings to suit your
|
||
nRF52 development board.)</p>
|
||
<p id="80d7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">5️⃣ <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/targets" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">targets</a></code>:
|
||
<strong class="gr hh">Bootloader and Application Targets for Mynewt</strong></p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o13.png" /></p>
|
||
|
||
|
||
</div>
|
||
<p id="dac8" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Mynewt Applications are designed
|
||
to be portable across microcontrollers… An application like <code
|
||
class="dm hj hk hl hm b">my_sensor_app</code> may be recompiled to run on STM32 Blue Pill F103,
|
||
STM32 F476, or even BBC micro:bit (based on Nordic nRF51).</p>
|
||
<p id="8926" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">How do we compile an application
|
||
like <code class="dm hj hk hl hm b">my_sensor_app</code> for our nRF52832 development board? We tell
|
||
Mynewt to create a “Target” for the application, i.e. an instance of <code
|
||
class="dm hj hk hl hm b">my_sensor_app</code> that’s targeted for our Board Support Package <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/hw/bsp/nrf52" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">hw/bsp/nrf52</a></code>
|
||
</p>
|
||
<p id="3f45" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/targets" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">targets</a></code>
|
||
folder contains Bootloaders and Applications that have been targeted for specific Board Support
|
||
Packages…</p>
|
||
<p id="cfb2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/targets/nrf52_boot" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">targets/nrf52_boot</a></code>:
|
||
This is the <code class="dm hj hk hl hm b">boot_stub</code> Bootloader targeted for nRF52</p>
|
||
<p id="53dd" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/targets/nrf52_my_sensor" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">targets/nrf52_my_sensor</a></code>:
|
||
This is the <code class="dm hj hk hl hm b">my_sensor_app</code> Application targeted for nRF52. The
|
||
Application Settings are configured at <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/targets/nrf52_my_sensor/syscfg.yml" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">targets/nrf52_my_sensor/syscfg.yml</a></code>
|
||
</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/0d232faf505ea8987a864b39c1f7517c.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Application Settings. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/targets/nrf52_my_sensor/syscfg.yml"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/targets/nrf52_my_sensor/syscfg.yml</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="ab9a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">6️⃣ <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/scripts" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">scripts</a></code>:
|
||
<strong class="gr hh">Build, Flash and Debug Scripts</strong></p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o14.png" /></p>
|
||
|
||
|
||
|
||
</div>
|
||
<p id="1a26" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/build-app.cmd" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">scripts/build-app.cmd</a>, <a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/build-app.sh" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">.sh</a></code>:
|
||
This shell script <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/build-app.sh#L80-L82"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">builds the Rust Application</a> by
|
||
calling <code class="dm hj hk hl hm b">cargo build</code> and <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/build-app.sh#L87-L120"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">bundles the compiled Rust code</a>
|
||
(with external crates) into the <code class="dm hj hk hl hm b">rust_app</code> library.</p>
|
||
<p id="fda9" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Then it <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/build-app.sh#L157-L160"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">builds the Application
|
||
Firmware</a> by running <code class="dm hj hk hl hm b">newt build nrf52_my_sensor</code>, injecting
|
||
<code class="dm hj hk hl hm b">rust_app</code> (Rust Application) and <code
|
||
class="dm hj hk hl hm b">rust_libcore</code> (<a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/build-app.sh#L129-L148"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Rust Core Library</a>) into the
|
||
Application Firmware.</p>
|
||
<p id="0f38" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The Rust build is targeted for
|
||
<code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/build-app.cmd#L3-L6" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">thumbv7em-none-eabihf</a></code>
|
||
(Arm Cortex M4 with Hardware Floating-Point), which is the designation for the Arm processor in the
|
||
nRF52832 microcontroller.</p>
|
||
<p id="9f6d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/scripts/nrf52" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">scripts/nrf52</a></code>:
|
||
Contains the build, flash and debug scripts specific to nRF52. The <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/flash-boot.cmd"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Flash Bootloader</a>, <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/flash-app.cmd"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Flash Application</a> and <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/debug.ocd"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Debug Scripts</a> include OpenOCD
|
||
scripts (<code class="dm hj hk hl hm b">*.ocd</code>) that connect to the nRF52 via ST-Link.</p>
|
||
<p id="830c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s the OpenOCD command used
|
||
in the <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/flash-app.cmd"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Flash Application</a> Script that
|
||
connects to nRF52 via ST-Link to flash the Application Firmware…</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="102b" class="mi jc ec bk hm b ds mj mk r ml">openocd/bin/openocd \<br/> -f scripts/nrf52/flash-init.ocd \<br/> -f interface/stlink.cfg \<br/> -c "transport select hla_swd" \<br/> -f target/nrf52.cfg \<br/> -f scripts/nrf52/flash-app.ocd</span></pre>
|
||
<p id="3f56" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The OpenOCD script <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/flash-app.ocd" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">scripts/nrf52/flash-app.ocd</a></code>
|
||
specifies the firmware image file to be flashed (<code
|
||
class="dm hj hk hl hm b">my_sensor_app.img</code>) and the ROM address (<code
|
||
class="dm hj hk hl hm b">0x0000 4000</code>)…</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="2673" class="mi jc ec bk hm b ds mj mk r ml"># From <a href="https://devzone.nordicsemi.com/f/nordic-q-a/42824/flashing-nrf5832-using-only-st-link-v2-and-openocd" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">https://devzone.nordicsemi.com/f/nordic-q-a/42824/flashing-nrf5832-using-only-st-link-v2-and-openocd</a><br/>gdb_flash_program enable<br/>gdb_breakpoint_override hard</span><span id="6a4b" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml"># Connect to the device.<br/>init</span><span id="44a6" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml"># Enable ARM semihosting to show debug console output.<br/>arm semihosting enable</span><span id="c173" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">echo "Stopping..."<br/>reset halt</span><span id="73c3" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">echo "Flashing Application..."<br/>program bin/targets/nrf52_my_sensor/app/apps/my_sensor_app/my_sensor_app.img verify 0x00004000</span><span id="7139" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml"># Restart the device.<br/>reset halt<br/>exit</span></pre>
|
||
<p id="628a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Note that…<br />Bootloader Code
|
||
is located at ROM Address <code class="dm hj hk hl hm b">0x0000 0000</code> <br />Application Code is
|
||
located at ROM Address <code class="dm hj hk hl hm b">0x0000 4000</code></p>
|
||
<p id="e459" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">7️⃣ <code
|
||
class="dm hj hk hl hm b">repos</code>: <strong class="gr hh">Apache Mynewt and NimBLE Source
|
||
Code</strong></p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o15.png" /></p>
|
||
|
||
|
||
|
||
</div>
|
||
<p id="b0ac" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This folder contains the official
|
||
Mynewt and NimBLE source code in C. We shouldn’t change anything here.</p>
|
||
<p id="f9b5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">8️⃣ <code
|
||
class="dm hj hk hl hm b">bin</code>: <strong class="gr hh">Compiled Bootloader and Application
|
||
Code</strong></p>
|
||
<p id="fae7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The build scripts produce
|
||
Bootloader and Application Firmware Images in this folder. These firmware images are used by the <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/flash-boot.cmd"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Flash Bootloader</a> and <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/flash-app.cmd"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Flash Application</a> Scripts to
|
||
flash the Bootloader and Application to the nRF52.</p>
|
||
<p id="fd03" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The Application Firmware is also
|
||
flashed to the nRF52 when we click <code class="dm hj hk hl hm b">Start Debugging</code>.</p>
|
||
<p id="8bc8" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">9️⃣ <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/.vscode" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">.vscode</a></code>:
|
||
<strong class="gr hh">Visual Studio Code Settings</strong></p>
|
||
<p id="3f39" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/.vscode/tasks.json" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">tasks.json</a></code>
|
||
defines the Build and Flash Bootloader / Application Tasks in Visual Studio Code. These tasks invoke
|
||
the scripts in the <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/tree/nrf52/scripts" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">scripts</a></code>
|
||
folder</p>
|
||
<p id="c94d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/.vscode/launch-nrf52.json" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">launch-nrf52.json</a></code>
|
||
contains the nRF52 debugger settings for the <a
|
||
href="https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Cortex-Debug Extension</a> that
|
||
we’re using to debug our application. When we run the <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/build-app.cmd#L4-L6"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Build Application</a> script, the
|
||
script copies <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/.vscode/launch-nrf52.json" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">launch-nrf52.json</a></code>
|
||
to <code class="dm hj hk hl hm b">launch.json</code>, which is loaded by the Cortex-Debug debugger.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="6166" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Create an iBeacon with NimBLE
|
||
</h1>
|
||
<p id="918b" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Let’s look at the application
|
||
code that calls the NimBLE API to create an iBeacon Transmitter: <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/apps/my_sensor_app/src/ble.c" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">apps/my_sensor_app/src/ble.c</a></code>.
|
||
Why did we code this in C and not Rust? We’ll discuss this in a while.</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/36aad6f0453ef7dd7f61d2c1c20bce82.js"></script></p>
|
||
|
||
|
||
</div>
|
||
<p id="be55" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b">start_ble()</code> is called by the Rust <code
|
||
class="dm hj hk hl hm b">main()</code> function to start the iBeacon broadcasts in our application.
|
||
</p>
|
||
<p id="8732" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In Bluetooth LE applications,
|
||
it’s mandatory to wait for the Host (i.e. Arm Processor) and Controller (i.e. Radio Transceiver) to
|
||
sync up before performing any Bluetooth LE functions.</p>
|
||
<p id="fe53" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here we set up the <code
|
||
class="dm hj hk hl hm b">ble_hs_cfg.sync_cb</code> callback defined in NimBLE, so that NimBLE will
|
||
call our function <code class="dm hj hk hl hm b">ble_app_on_sync()</code> as soon as the Host and
|
||
Controller are in sync. (Which happens very quickly upon startup… Just set a breakpoint in <code
|
||
class="dm hj hk hl hm b">ble_app_on_sync()</code> and watch!)</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/ee65281b1e160c054fa896d40ee52901.js"></script></p>
|
||
|
||
|
||
</div>
|
||
<p id="acb7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">When the Host and Controller are
|
||
in sync, <code class="dm hj hk hl hm b">ble_app_on_sync()</code> calls two functions to set up the
|
||
iBeacon Transmitter…</p>
|
||
<ol class="">
|
||
<li id="9cb0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc km kn ko"><code
|
||
class="dm hj hk hl hm b">ble_app_set_addr()</code>: Generate a Non-Resolvable Private Address</li>
|
||
<li id="d9c3" class="gp gq ec bk gr b gs kp gu kq gw kr gy ks ha kt hc km kn ko"><code
|
||
class="dm hj hk hl hm b">ble_app_advertise()</code>: Advertise indefinitely as an iBeacon</li>
|
||
</ol>
|
||
<p id="dc35" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">What’s a <strong
|
||
class="gr hh">Non-Resolvable Private Address</strong>? Just like any networking protocol, in
|
||
Bluetooth LE we need to identify ourselves with a network address. Since we’re creating an iBeacon
|
||
Transmitter with no receive capability, it’s OK to use a temporary random address, i.e. Non-Resolvable
|
||
Private Address.</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/1ab29d101c4bd579821e8942cb0d282f.js"></script></p>
|
||
|
||
|
||
</div>
|
||
<p id="e5dd" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s how we call the NimBLE API
|
||
<code class="dm hj hk hl hm b">ble_hs_id_gen_rnd()</code> to generate that random 6-byte
|
||
Non-Resolvable Private Address.</p>
|
||
<p id="f857" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Once we have obtained the random
|
||
address, we tell NimBLE to use it by calling <code
|
||
class="dm hj hk hl hm b">ble_hs_id_set_rnd()</code>. If you’re curious to see the random address,
|
||
just set a Debugger Breakpoint by clicking the gutter to add a red dot like this…</p>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o16.png" /></p>
|
||
|
||
|
||
|
||
<figcaption><p><em>Setting a breakpoint to observe the
|
||
randomly-generated 6-byte Non-Resolvable Private Address</em></p></figcaption>
|
||
</div>
|
||
<p id="5ec4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Now let’s find out what our nRF52
|
||
shall be broadcasting…</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="6779" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Set iBeacon Parameters</h1>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o17.png" /></p>
|
||
|
||
<figcaption><p><em>iBeacon ID links the iBeacon Transmitter to
|
||
the Mobile App</em></p></figcaption>
|
||
</div>
|
||
<p id="e87a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Remember our iBeacon sketch?
|
||
Every iBeacon Transmitter needs to broadcast the following…</p>
|
||
<ol class="">
|
||
<li id="39d3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc km kn ko"><strong
|
||
class="gr hh">iBeacon ID</strong>: Our iBeacon Transmitter shall broadcast this 16-byte iBeacon
|
||
ID, which looks like <code class="dm hj hk hl hm b">11111111–1111–1111–1111–111111111111</code> (in
|
||
hexadecimal). Upon sensing the iBeacon ID in the airwaves, the phone OS (iOS or Android) will <a
|
||
href="https://www.raywenderlich.com/632-ibeacon-tutorial-with-ios-and-swift"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">wake up our Mobile App</a>
|
||
that’s linked to this iBeacon ID.</li>
|
||
<li id="cc3d" class="gp gq ec bk gr b gs kp gu kq gw kr gy ks ha kt hc km kn ko"><strong
|
||
class="gr hh">Major ID</strong>: A 16-bit number to differentiate iBeacon Transmitters</li>
|
||
<li id="523c" class="gp gq ec bk gr b gs kp gu kq gw kr gy ks ha kt hc km kn ko"><strong
|
||
class="gr hh">Minor ID</strong>: Another 16-bit number to differentiate iBeacon Transmitters</li>
|
||
</ol>
|
||
<p id="618c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">How are Major and
|
||
Minor IDs used?</em> Let’s say we operate a chain of stores. All stores in the chain would use the
|
||
same Mobile App, so all iBeacon Transmitters in the stores should broadcast the same iBeacon ID.</p>
|
||
<p id="aa9d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">How would we
|
||
identify which store the customer has stepped into?</em> Easy… Just assign a unique Major ID for
|
||
each store! The app would be able to sense the Major ID from the iBeacon broadcasts and figure out
|
||
which store you’re at.</p>
|
||
<p id="83d1" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Could we identify
|
||
which part of the store the customer is at?</em> Sure! Just assign a unique Minor ID for each
|
||
iBeacon Transmitter in the store. Here’s how we broadcast the iBeacon ID, Major ID and Minor ID in
|
||
NimBLE…</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/8da46373649da93dd313b5ae1a8c777d.js"></script></p>
|
||
|
||
|
||
</div>
|
||
<p id="9a20" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In our code we used an arbitrary
|
||
iBeacon ID <code class="dm hj hk hl hm b">uuid128</code> that’s defined as <code
|
||
class="dm hj hk hl hm b">11111111–1111–1111–1111–111111111111</code> (in hexadecimal). We pass the
|
||
iBeacon ID to <code class="dm hj hk hl hm b">ble_ibeacon_set_adv_data()</code>, together with Major ID
|
||
<code class="dm hj hk hl hm b">2</code> and Minor ID <code class="dm hj hk hl hm b">10</code>. When we
|
||
call <code class="dm hj hk hl hm b">ble_gap_adv_start()</code>, our nRF52 starts advertising itself as
|
||
an iBeacon.</p>
|
||
<p id="f1a1" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">There’s one more parameter that
|
||
we passed to <code class="dm hj hk hl hm b">ble_ibeacon_set_adv_data()</code>… Measured Power, which
|
||
is the RSSI value at 1 meter: <code class="dm hj hk hl hm b">-60</code>. This value is broadcast by
|
||
the iBeacon, together with the other IDs.</p>
|
||
<p id="c2d0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Since we are diving deep into
|
||
wireless transmission, let’s study the meaning of RSSI, known to most of us as Signal Strength…</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="f2d6" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">How Near Is Our iBeacon?</h1>
|
||
<p id="1524" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc"><a
|
||
href="https://www.bluetooth.com/blog/proximity-and-rssi/"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Received Signal Strength
|
||
Indication</a> (RSSI) is a common metric for measuring the Signal Strength of wireless networks like
|
||
WiFi, NB-IoT and Bluetooth LE. RSSI values are usually negative, and higher values denote stronger
|
||
signals (RSSI <code class="dm hj hk hl hm b">-50</code> is stronger than RSSI <code
|
||
class="dm hj hk hl hm b">-60</code>).</p>
|
||
<p id="14ec" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Why do iBeacons
|
||
broadcast their RSSI values?</em> So that we may estimate how near they are!</p>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o18.png" /></p>
|
||
|
||
|
||
<figcaption><p><em>Estimated Proximity for our nRF52 iBeacon in
|
||
the Locate Beacon app: 0.7 metres</em></p></figcaption>
|
||
</div>
|
||
<p id="4946" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Recall that we set our iBeacon
|
||
Transmitter’s Measured Power as <code class="dm hj hk hl hm b">-60</code>. According to the definition
|
||
of Measured Power, if we placed our mobile phone 1 metre away from our iBeacon Transmitter, the phone
|
||
would record the RSSI as <code class="dm hj hk hl hm b">-60</code>.</p>
|
||
<p id="c39e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Thus if the RSSI recorded by the
|
||
phone was <code class="dm hj hk hl hm b">-50</code> or higher, the iBeacon Transmitter would probably
|
||
be close to the phone (within 1 metre). This gives us a simple way to estimate the distance between
|
||
the iBeacon Transmitter and our mobile phone.</p>
|
||
<p id="079c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">In the real world, the estimated
|
||
proximity of iBeacons is not really accurate. Bluetooth LE signals often clash with WiFi in the
|
||
crowded 2.4 GHz airwaves, so the RSSI values may fluctuate wildly. And when Bluetooth LE signals pass
|
||
through objects (like human bodies) the RSSI values will drop.</p>
|
||
<p id="cac4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">It’s hard to do accurate distance
|
||
ranging for any kind of wireless signal (WiFi and NB-IoT included). But it’s good to understand what
|
||
RSSI means, and how wireless signals degrade when transmitting over long distances, passing through
|
||
obstacles.</p>
|
||
<p id="6bad" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">For more details on implementing
|
||
iBeacon Transmitters with NimBLE, check out the complete tutorial at <code
|
||
class="dm hj hk hl hm b"><a href="https://mynewt.apache.org/latest/tutorials/ble/ibeacon.html" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">https://mynewt.apache.org/latest/tutorials/ble/ibeacon.html</a></code>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="cz">
|
||
<div class="n p">
|
||
<div class="ky kz la lb lc ld ag le ah lf aj ak">
|
||
<div class="figure js jt ju jv jw cz lh li paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o19.jpeg" /></p>
|
||
|
||
|
||
|
||
<figcaption><p><em>Testing our nRF52 iBeacon with the “Locate
|
||
Beacon” mobile app</em></p></figcaption>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="3a00" class="jb jc ec bk bj io ee ln eg lo jf lp jh lq jj lr jl">Test Our iBeacon</h1>
|
||
<p id="9ee9" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Let’s use a real Mobile App to
|
||
verify that our nRF52 is indeed broadcasting as an iBeacon.</p>
|
||
<p id="a308" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><a class="at cg hd he hf hg"
|
||
target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/install-apache-mynewt-and-embedded-rust-for-nrf52-and-visual-studio-code-on-windows-and-macos">Follow
|
||
the instructions in this article</a> to flash and debug your nRF52 with an <a
|
||
href="https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180924134644&SearchText=st-link+v2&switch_new_app=y"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">ST-Link V2 adapter</a>.</p>
|
||
<p id="76dd" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Start a debug session for nRF52
|
||
in Visual Studio Code. Whenever the program pauses at a breakpoint (this will happen twice), click
|
||
<code class="dm hj hk hl hm b">Continue</code> or press <code class="dm hj hk hl hm b">F5</code>. Keep
|
||
the nRF52 powered on.</p>
|
||
<p id="178f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Install the <strong
|
||
class="gr hh">“</strong><a
|
||
href="https://apps.apple.com/us/app/locate-beacon/id738709014"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow"><strong class="gr hh">Locate
|
||
Beacon</strong></a><strong class="gr hh">”</strong> app on your iPhone…</p>
|
||
<div class="hn ho hp hq hr hs"><a
|
||
href="https://apps.apple.com/us/app/locate-beacon/id738709014"
|
||
rel="noopener nofollow">
|
||
<section class="hv cl cm ak ce n ar hw hx hy hz ia ib ic id ie if ig ih ii ij ik">
|
||
<div class="il n co p im in">
|
||
<h2 class="bj io ip bl ec">
|
||
<div class="di ht fm fn hu fp">Locate Beacon</div>
|
||
</h2>
|
||
</div>
|
||
<div class="my r">
|
||
<div class="mz r na nb nc my nd ne nf"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o20.png" /></p>
|
||
|
||
|
||
|
||
</div>
|
||
<p id="6840" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">1️⃣ Launch the “Locate Beacon”
|
||
app on your iPhone.</p>
|
||
<p id="1df7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Tap the Gear icon at top right
|
||
</p>
|
||
<p id="0d42" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Tap <code
|
||
class="dm hj hk hl hm b">Add New UUID</code></p>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o0.png" /></p>
|
||
|
||
|
||
|
||
</div>
|
||
<div class="figure du cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o21.png" /></p>
|
||
|
||
|
||
|
||
</div>
|
||
<p id="da8b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">2️⃣ Tap the <code
|
||
class="dm hj hk hl hm b">+</code> button at top right</p>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o0.png" /></p>
|
||
|
||
|
||
|
||
</div>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o0.png" /></p>
|
||
|
||
|
||
|
||
</div>
|
||
<div class="figure du cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o22.png" /></p>
|
||
|
||
</div>
|
||
<p id="c6cf" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">3️⃣ Enter <code
|
||
class="dm hj hk hl hm b">My iBeacon</code> as the name</p>
|
||
<p id="e19a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">For the UUID, enter<br /><code
|
||
class="dm hj hk hl hm b">11111111–1111–1111–1111–111111111111</code></p>
|
||
<p id="e9df" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Leave the Major, Minor and Power
|
||
fields empty</p>
|
||
<p id="3d7f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Tap <code
|
||
class="dm hj hk hl hm b">Save</code></p>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o0.png" /></p>
|
||
|
||
|
||
</div>
|
||
<div class="figure du cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o23.png" /></p>
|
||
|
||
|
||
</div>
|
||
<p id="b2d7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">4️⃣ Our nRF52 should appear in
|
||
the list of detected iBeacons as <code class="dm hj hk hl hm b">My iBeacon</code></p>
|
||
<p id="386d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Tap <code
|
||
class="dm hj hk hl hm b">My iBeacon</code></p>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o0.png" /></p>
|
||
|
||
|
||
</div>
|
||
<div class="figure du cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o24.png" /></p>
|
||
|
||
|
||
</div>
|
||
<p id="928d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">5️⃣ Details of our iBeacon
|
||
appear, including the RSSI and estimated proximity.</p>
|
||
<p id="dfe2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">How did I figure
|
||
out that my nRF52’s Measured Power was </em><code class="dm hj hk hl hm b">-60</code>? By trial and
|
||
error!</p>
|
||
<p id="2ff6" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I placed my phone 1 metre away
|
||
from the nRF52, then adjusted the Measured Power value until the estimated distance in the Locate
|
||
Beacon app showed 1 metre.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="e10d" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Poll A Sensor With Rust</h1>
|
||
<p id="b82e" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Now let’s talk about Embedded
|
||
Rust! As we have discovered, our Mynewt Project allows us to embed Rust modules and crates into our
|
||
Application Firmware (via sneaky substitution of the <code class="dm hj hk hl hm b">rust_app</code>
|
||
and <code class="dm hj hk hl hm b">rust_libcore</code> libraries). The <code
|
||
class="dm hj hk hl hm b">main()</code> function is defined in Rust, in fact.</p>
|
||
<p id="7318" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">But we haven’t seen Rust in
|
||
action while creating our iBeacon. <em class="hi">How can we be sure that Rust is indeed running
|
||
properly on our nRF52?</em></p>
|
||
<p id="ecb3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">We’ll prove that by <strong
|
||
class="gr hh">polling a Simulated Mynewt Sensor with Rust</strong>! Here how we do that…</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/abc8db30b03f4aef9ea0bbc7f7554f31.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Very first thing in any Mynewt Application:
|
||
Call sysinit(). From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="8e2f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Our <code
|
||
class="dm hj hk hl hm b">main()</code> function is defined in the Rust module <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">rust/app/src/lib.rs</a></code>.
|
||
The first thing that happens in <code class="dm hj hk hl hm b">main()</code>: Call <code
|
||
class="dm hj hk hl hm b">sysinit()</code> to initialise the Mynewt libraries and drivers. (Mynewt
|
||
programs in C also call <code class="dm hj hk hl hm b">sysinit()</code> at startup)</p>
|
||
<p id="fd37" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">On the nRF52, <code
|
||
class="dm hj hk hl hm b">sysinit()</code> initialises the 2.4 GHz Radio Transceiver and the Stub
|
||
Temperature Sensor. We’ll talk about this simulated temperature sensor in a while.</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/48c7069c26672cfcc7310e85b6b1ea76.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Start polling the simulated temperature
|
||
sensor. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="b467" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Remember we said that Mynewt
|
||
programs are designed to be portable across microcontrollers? The Rust-Mynewt application we’re
|
||
studying now actually runs fine on STM32 F103 (Blue Pill) and L476 microcontrollers… But they run a
|
||
little differently.</p>
|
||
<p id="c094" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">On STM32 microcontrollers, our
|
||
Rust application polls the onboard temperature sensor every 10 seconds and transmits the temperature
|
||
data to a server (via an NB-IoT module connected to the microcontroller).</p>
|
||
<p id="7c9a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">On the nRF52 we won’t be
|
||
transmitting the sensor data to a server, so the code to start the Server Transport has been commented
|
||
out (for now).</p>
|
||
<p id="a445" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Next, we call <code
|
||
class="dm hj hk hl hm b">start_sensor_listener()</code> (defined in our application module <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">app_sensor.rs</a></code>)
|
||
to begin polling the simulated temperature sensor every 10 seconds.</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/40d6de45a1e55ef1b447f1030d5b1d71.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Start broadcasting as iBeacon. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="7105" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Remember our C function <code
|
||
class="dm hj hk hl hm b">start_ble()</code> that initiates the iBeacon broadcasting? This is how we
|
||
call <code class="dm hj hk hl hm b">start_ble()</code> from <code
|
||
class="dm hj hk hl hm b">main()</code>. It needs to be tagged as <code
|
||
class="dm hj hk hl hm b">unsafe</code> because to the Rust Compiler, all C functions are risky and
|
||
could potentially cause problems (memory corruption, crashing, …) The <code
|
||
class="dm hj hk hl hm b">unsafe</code> tag will be removed once we create a safe and proper Rust
|
||
Wrapper for NimBLE.</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/060053e8c27a4e063cc0cb4f739d51a5.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Mynewt Event Loop. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/lib.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="69f0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">At the end of the <code
|
||
class="dm hj hk hl hm b">main()</code> function we have a standard Mynewt Event Loop to handle
|
||
Mynewt system events. (Mynewt programs in C also have this Mynewt Event Loop). Without this Event
|
||
Loop, the NimBLE functions will never get any processing done.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="4258" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Mynewt Sensor Framework,
|
||
Enhanced With Rust</h1>
|
||
<p id="e0b1" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Let’s look at <code
|
||
class="dm hj hk hl hm b">start_sensor_listener()</code>, defined in our application module <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">app_sensor.rs</a></code>...
|
||
</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/3fcd274796c5a5c3b4fec674ab091f88.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Define the name of the Stub Temperature Sensor
|
||
and the polling interval. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="275e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">At the top of <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">app_sensor.rs</a></code>
|
||
we define some constants for polling the sensor.</p>
|
||
<p id="ef6d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b">SENSOR_DEVICE</code> is defined as <code
|
||
class="dm hj hk hl hm b">temp_stub_0</code>, which refers to the <strong class="gr hh">Stub
|
||
Temperature Sensor</strong>. The Stub Temperature Sensor works like a regular Mynewt Temperature
|
||
Sensor… Except that it always returns a hardcoded raw temperature value <code
|
||
class="dm hj hk hl hm b">1757</code>. Useful for testing sensor applications without connecting a
|
||
real temperature sensor. (On STM32 this program polls the actual onboard temperature sensor)</p>
|
||
<p id="7d2c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b">SENSOR_POLL_TIME</code> is set to 10,000 milliseconds, or 10 seconds. We’ll
|
||
be asking Mynewt to poll our simulated temperature sensor every 10 seconds.</p>
|
||
<p id="5e64" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">What’s <code
|
||
class="dm hj hk hl hm b">Strn</code>? This is a custom string type that I have defined to make
|
||
passing of strings safer and more efficient. Mynewt APIs require all strings to be null-terminated;
|
||
Rust strings don’t need the terminating null.</p>
|
||
<p id="36ba" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">If we pass strings back and forth
|
||
between Rust and the Mynewt APIs, we could end up creating many clones of the same string, with and
|
||
without terminating nulls. Or worse… Mynewt could crash because some Rust code has incorrectly passed
|
||
in a string that’s not null-terminated. So I have wrapped the Mynewt APIs to accept the safer,
|
||
efficient <code class="dm hj hk hl hm b">Strn</code> type that’s always null-terminated.</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/fbe6082436e81f82222e483581751657.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Fetch the Stub Temperature Sensor by name.
|
||
From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="4a3a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s the first clue that Mynewt
|
||
has an awesome <strong class="gr hh">Sensor Framework</strong>… Mynewt keeps track of all installed
|
||
sensors by name. <code class="dm hj hk hl hm b">sensor_mgr::find_bydevname()</code> is the Mynewt API
|
||
that returns a list of sensors (i.e. a Sensor Iterator) that match a name (<code
|
||
class="dm hj hk hl hm b">temp_stub_0</code>).</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/b385670a3ba141e0ce9d29acad18c749.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Set the polling interval for the Stub
|
||
Temperature Sensor. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="3168" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Mynewt recognises every sensor
|
||
and the type of data that the sensor produces. So let’s ask Mynewt to poll the sensor on our behalf!
|
||
</p>
|
||
<p id="d548" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">By calling <code
|
||
class="dm hj hk hl hm b">sensor::set_poll_rate_ms()</code> we’re kindly asking Mynewt to poll our
|
||
simulated temperature sensor every 10 seconds. <em class="hi">Truly awesome!</em></p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/7ce991cc8791de3a88c84fb5945d74b7.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Create a Sensor Listener. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="1fbd" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Now we fill in the nitty-gritty
|
||
polling details…<em class="hi"> What shall we do with the temperature sensor data after Mynewt has
|
||
obtained it?</em> Here we ask Mynewt to call our Rust function <code
|
||
class="dm hj hk hl hm b">aggregate_sensor_data()</code> with the sensor data.</p>
|
||
<p id="2fb5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><code
|
||
class="dm hj hk hl hm b">aggregate_sensor_data()</code> is known as a Sensor Listener Function… It’s
|
||
a function that listens for updated sensor data and acts on the data.</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/80245e18b0bcb4c24eaac6a47ae4dc6f.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Register the Sensor Listener with Mynewt
|
||
Sensor Framework. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_sensor.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="f399" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">To activate the Sensor Listener
|
||
Function, we call the Mynewt API <code class="dm hj hk hl hm b">sensor::register_listener()</code>.
|
||
Mynewt will begin polling the simulated temperature sensor every 10 seconds and call <code
|
||
class="dm hj hk hl hm b">aggregate_sensor_data()</code> with the polled temperature data.</p>
|
||
<p id="4178" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Polling a sensor
|
||
in Mynewt is really so easy… in C and in Rust</em>! That’s possible only because Mynewt has a
|
||
well-designed Sensor Framework.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="c3e6" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Handle Sensor Data With Rust
|
||
</h1>
|
||
<p id="fc7b" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Remember that we don’t transmit
|
||
sensor data to a server in the nRF52 version of the Rust application (unlike the STM32 version). But
|
||
we’ll take a peek to understand how our sensor data could have been easily packaged and delivered to
|
||
an IoT server.</p>
|
||
<p id="b07f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Earlier we asked Mynewt to call
|
||
<code class="dm hj hk hl hm b">aggregate_sensor_data()</code> whenever it has polled our simulated
|
||
temperature sensor. Let’s see what happens inside <code
|
||
class="dm hj hk hl hm b">aggregate_sensor_data()</code>…</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/32744126701f2048ed9c4a38c77ac972.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Aggregate temperature data with GPS data. From
|
||
<a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_network.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_network.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="ae4a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">On the STM32 L476 microcontroller
|
||
our Rust application not only handles temperature sensor data… It handles <em class="hi">GPS latitude
|
||
/ longitude coordinates as well!</em> The application polls the GPS module for the current
|
||
geolocation (just like any Mynewt sensor) and attaches the geolocation to the temperature data before
|
||
transmitting to the server.</p>
|
||
<p id="4da2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">On nRF52 we’ll settle for less…
|
||
<code class="dm hj hk hl hm b">aggregate_sensor_data()</code> will simply transmit the temperature
|
||
data without attaching any GPS coordinates. It calls <code
|
||
class="dm hj hk hl hm b">send_sensor_data()</code> to transmit the temperature data…</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/9fa6bd52409db33dfaa09f8cb84b35a3.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Send sensor data to CoAP server. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_network.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_network.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="790b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This sounds incredulous… But
|
||
<code class="dm hj hk hl hm b">send_sensor_data()</code> was designed to transmit <em class="hi">any
|
||
sensor data</em> in <em class="hi">any format</em> that will be understood by our server!</p>
|
||
<p id="26ab" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">For example, the code here works
|
||
perfectly for transmitting temperature data to the server at thethings.io. This server accepts CoAP
|
||
Messages with a JSON Payload that contains the geolocated sensor data.</p>
|
||
<p id="5ce1" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Yet strangely, <code
|
||
class="dm hj hk hl hm b">send_sensor_data()</code> doesn’t contain any code that’s specific to
|
||
thethings.io…<em class="hi"> The sensor data appears to transform itself magically for
|
||
thethings.io</em>. How is this possible? We’ll learn in a while…</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/3b35e2b4b4af26ca615323396f05fdd7.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Compose an outgoing CoAP message. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_network.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_network.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="e72b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This is the end of the road for
|
||
nRF52… We haven’t started a Network Transport (like NB-IoT) that will deliver the sensor data to a
|
||
server, so nRF52 silently drops the sensor data here.</p>
|
||
<p id="ece0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">For STM32, the Rusty trail
|
||
continues…</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/469382be873bbc49acf5bab0c820cc36.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Compose and transmit the JSON Payload
|
||
containing the temperature and GPS data. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_network.rs"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/rust/app/src/app_network.rs</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="973e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Believe it (or not)… <em
|
||
class="hi">That’s all the code we need</em> to transform our geolocated sensor data into this
|
||
complicated nested CoAP + JSON format mandated by thethings.io…</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="7f4a" class="mi jc ec bk hm b ds mj mk r ml">{ "values": [<br/> { "key" : "t", <br/> "value": 1757, <br/> "geo" : { "lat": 1.2701, "long": 103.8078 }},</span><span id="295c" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml"> { "key" : "device",<br/> "value": "l476,bf39a9607e1187f6f3d80d6dd43" }<br/>]}</span></pre>
|
||
<p id="eae0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">The secret of the
|
||
sensor data transformation?</em> It’s in the <code class="dm hj hk hl hm b">coap!()</code> macro!
|
||
</p>
|
||
<p id="8866" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><strong class="gr hh">Rust
|
||
Declarative Macros</strong> are incredibly powerful… A Rust macro can transform a simple JSON object
|
||
into a complicated nested CoAP + JSON monster. The <code class="dm hj hk hl hm b">coap!()</code> macro
|
||
hides the details of the data transformation. That’s why we can transmit <em class="hi">any sensor
|
||
data</em> in <em class="hi">any format</em> that will be understood by the server… Just let the
|
||
<code class="dm hj hk hl hm b">coap!()</code> macro handle it!</p>
|
||
<p id="b4b7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Thanks to Mynewt, the
|
||
transmission of sensor data is highly efficient. The CoAP Message is transmitted (over NB-IoT) by a
|
||
background task in Mynewt. So our application may continue processing sensor data without waiting for
|
||
the transmission to complete.</p>
|
||
<p id="9dc3" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Mynewt and Rust
|
||
are perfectly paired for building safe and efficient embedded systems!</em></p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="1b07" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Watch Rust Run On nRF52</h1>
|
||
<p id="ff21" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Ready to watch Rust run on nRF52?
|
||
All you need is an nRF52 development board and an <a
|
||
href="https://www.aliexpress.com/wholesale?catId=0&initiative_id=SB_20180924134644&SearchText=st-link+v2&switch_new_app=y"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">ST-Link V2 adapter</a>.</p>
|
||
<p id="72aa" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><a class="at cg hd he hf hg"
|
||
target="_blank" rel="noopener"
|
||
href="https://lupyuen.github.io/articles/install-apache-mynewt-and-embedded-rust-for-nrf52-and-visual-studio-code-on-windows-and-macos">Follow
|
||
the instructions in this article</a> to flash and debug your nRF52.</p>
|
||
<p id="9c62" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The Output Log in Visual Studio
|
||
Code should look like this…</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/90ac1dca567c53b00b0877cc47c5ae5e.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Output Log from <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/logs/standalone-node.log"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/logs/standalone-node.log</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="a646" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Here’s a video demo of the
|
||
Application Build and Debug on nRF52…</p>
|
||
</div>
|
||
</div>
|
||
<div class="cz">
|
||
<div class="n p">
|
||
<div class="ky kz la lb lc ld ag le ah lf aj ak">
|
||
<div class="figure js jt ju jv jw cz">
|
||
<div class="dl r dd">
|
||
<div class="nm r"><iframe
|
||
src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FwqimbvJeGMo%3Ffeature%3Doembed&url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DwqimbvJeGMo&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FwqimbvJeGMo%2Fhqdefault.jpg&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=youtube"
|
||
allowfullscreen="" frameborder="0" height="300" width="400"
|
||
title="nRF52 with Embedded Rust and Apache Mynewt" class="cp t u dh ak"
|
||
scrolling="auto"></iframe></div>
|
||
</div>
|
||
<figcaption><p><em>Video demo of the Application Build and
|
||
Debug on nRF52</em></p></figcaption>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="cz">
|
||
<div class="n p">
|
||
<div class="ky kz la lb lc ld ag le ah lf aj ak">
|
||
<div class="figure js jt ju jv jw cz lh li paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o25.png" /></p>
|
||
|
||
|
||
|
||
<figcaption><p><em>Polling sensors in Mynewt: Rust vs C
|
||
</em></p></figcaption>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="4799" class="jb jc ec bk bj io ee ln eg lo jf lp jh lq jj lr jl">Why Embedded Rust Instead of
|
||
Embedded C?</h1>
|
||
<p id="60a5" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Take a look at the above Rust
|
||
code that’s running on our nRF52 for polling the simulated temperature sensor. Compare that with the
|
||
equivalent C code.</p>
|
||
<p id="a51e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Why is Rust better
|
||
than C?</em></p>
|
||
<p id="7df7" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Yes the Rust code looks more
|
||
verbose than C… But that’s a good thing! C programming is so terse that it makes C difficult to learn.
|
||
Experienced C programmers (like me) are indeed a dying breed.</p>
|
||
<p id="545f" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Rust uses sensible keywords (like
|
||
<code class="dm hj hk hl hm b">fn</code> to denote functions, <code
|
||
class="dm hj hk hl hm b">let</code> for declaring variables), making it easier to learn. Note also
|
||
that the Rust code doesn’t use any pointers. If you look at the C code, it’s really easy to misuse
|
||
pointers like <code class="dm hj hk hl hm b">listen_sensor</code> and <code
|
||
class="dm hj hk hl hm b">listener</code>… causing more problems and frustration to learners.</p>
|
||
<p id="dd13" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Error handling in Rust is done
|
||
elegantly… We use the <code class="dm hj hk hl hm b">?</code> operator to catch errors and exit early.
|
||
Compare that with the unsightly <code class="dm hj hk hl hm b">assert()</code> in C. What if we forget
|
||
to check the return code in C? Strange bugs ensue.</p>
|
||
<p id="9e4c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">Why not code
|
||
EVERYTHING in Rust… Including Mynewt OS and NimBLE?</em></p>
|
||
<p id="0db9" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">There’s an amazing community hard
|
||
at work creating <a
|
||
href="https://github.com/nrf-rs/nrf52-hal"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Rust on Bare Metal</a>. But it
|
||
will take a while to get it running and tested with real-world applications on nRF52, nRF51, STM32
|
||
F103, STM32 F476, RISC-V, …</p>
|
||
<p id="ca8d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I’m solving this problem with a
|
||
different approach by applying <strong class="gr hh">Lean Principles</strong>…</p>
|
||
<p id="5a8a" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">You, the reader,
|
||
the learner, are my Customer</em>. You wish to build a safe and efficient Embedded Application
|
||
(hopefully in Rust). <em class="hi">What Rust APIs shall I provide you?</em></p>
|
||
<p id="a5be" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I could build a Rust OS from
|
||
scratch based on Bare Metal Rust, and offer you a Native Rust API. But that’s not very Lean.</p>
|
||
<p id="28d1" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Or I could take an embedded OS
|
||
that’s already available, say Mynewt or Zephyr or FreeRTOS. Wrap it up with a clean Rust API, and give
|
||
that Wrapped Rust API to you instead.<em class="hi"> You wouldn’t know the difference between the
|
||
Native and Wrapped Rust APIs!</em> (Unless I told you)</p>
|
||
<p id="c5b0" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">The Wrapped Rust API won’t be
|
||
perfect, because as you create new gadgets with the API, you may find the API cumbersome or hard to
|
||
use. So I’ll take this opportunity to evolve the API iteratively, till we get the Perfect Embedded
|
||
Rust API. (That’s how I evolved the Mynewt Sensor Framework with Rust Iterators)</p>
|
||
<p id="af04" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Now we’re ready to revamp the OS
|
||
with Rust and restructure it to implement the Perfect Embedded Rust API in the safest and most
|
||
efficient way possible.</p>
|
||
<p id="79a4" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This, I think, is the right
|
||
approach for solving the Embedded Rust problem. And it needs to happen soon (based on Mynewt or Zephyr
|
||
or FreeRTOS or …) so that we may quickly move embedded coders away from unsafe C and onto Rust.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="c9e5" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">Rust Wrappers for NimBLE</h1>
|
||
<p id="2bd3" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc"><em class="hi">Where are the Rust
|
||
Wrappers for the NimBLE API?</em> Since the Mynewt Sensor Framework already has Rust Wrappers, it
|
||
shouldn’t be too difficult to create Rust Wrappers for NimBLE right?</p>
|
||
<p id="1b2e" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">That’s work in
|
||
progress</em>. With the Mynewt Sensor Framework I understand clearly how the API is used to read and
|
||
poll sensors under various situations. The Rust Wrappers for the Mynewt Sensor Framework were designed
|
||
for these use cases. With the NimBLE API… The use cases are still fuzzy to me.</p>
|
||
<p id="2fb8" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Some NimBLE applications appear
|
||
to have lots of repetitive code, <a
|
||
href="https://github.com/apache/mynewt-nimble/blob/master/apps/blemesh_models_example_2/src/device_composition.c"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">like this Bluetooth Mesh
|
||
application</a>. They could be greatly simplified with Rust Macros. Just like the <code
|
||
class="dm hj hk hl hm b">coap!()</code> Rust Macro we used for composing CoAP messages.</p>
|
||
<p id="d127" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><em class="hi">If you would like
|
||
to help out with the design of the Rust Wrappers for NimBLE, drop me a note!</em></p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="26e5" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">What’s Next?</h1>
|
||
<p id="2b30" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">Coding the nRF52 was an
|
||
incredible experience… I’m finishing this tutorial only two weeks after touching nRF52 for the very
|
||
first time!</p>
|
||
<p id="afb8" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">I got big plans for nRF52 in
|
||
upcoming tutorials…</p>
|
||
<p id="92db" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">1️⃣ <strong
|
||
class="gr hh">Bluetooth Mesh</strong>: What if we had a mesh network of nRF52 nodes that can relay
|
||
IoT sensor data to nearby nodes? One of these nodes could be a WiFi gateway (based on ESP8266) that
|
||
forwards the sensor data to an IoT server like thethings.io.</p>
|
||
<p id="8a54" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This will be an interesting
|
||
application of Bluetooth Mesh, which is <a
|
||
href="https://mynewt.apache.org/latest/network/docs/mesh/index.html"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">already supported in NimBLE</a>.
|
||
Here is the article…</p>
|
||
<div class="hn ho hp hq hr hs"><a rel="noopener"
|
||
href="https://lupyuen.github.io/articles/bluetooth-mesh-with-nrf52-and-apache-mynewt">
|
||
<section class="hv cl cm ak ce n ar hw hx hy hz ia ib ic id ie if ig ih ii ij ik">
|
||
<div class="il n co p im in">
|
||
<h2 class="bj io ip bl ec">
|
||
<div class="di ht fm fn hu fp">Bluetooth Mesh with nRF52 and Apache Mynewt</div>
|
||
</h2>
|
||
</div>
|
||
<div class="my r">
|
||
<div class="np r na nb nc my nd ne nf"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<p id="1a9d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">2️⃣ <strong
|
||
class="gr hh">PineTime Smart Watch</strong>: <a
|
||
href="https://www.cnx-software.com/2019/09/25/pinetime-smartwatch-specifications-released-availability-scheduled-for-h1-2020/"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">PineTime</a> is an upcoming smart
|
||
watch that’s powered by nRF52. What if we could run Rust and Mynewt OS on this watch… And allow watch
|
||
apps to be built easily with <a
|
||
href="https://lupyuen.github.io/articles/visual-embedded-rust-programming-with-visual-studio-code"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Visual Rust</a>?</p>
|
||
<p id="c32b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">What if the watches could form a
|
||
mesh and relay each wearer’s vital signs (from the heart rate sensor)? And alert you if any of your
|
||
loved ones are uncontactable through the mesh network? No more missing kids / grandparents / friends /
|
||
hikers / swimmers / pets / …</p>
|
||
<p id="4df5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">“Herding” sounds like a good name
|
||
for such Wearable Mesh apps. I have received the PineTime developer kit… Here’s my review!</p>
|
||
<div class="hn ho hp hq hr hs"><a rel="noopener"
|
||
href="https://lupyuen.github.io/articles/sneak-peek-of-pinetime-smart-watch-and-why-its-perfect-for-teaching-iot">
|
||
<section class="hv cl cm ak ce n ar hw hx hy hz ia ib ic id ie if ig ih ii ij ik">
|
||
<div class="il n co p im in">
|
||
<h2 class="bj io ip bl ec">
|
||
<div class="di ht fm fn hu fp">Sneak Peek of PineTime Smart Watch… And why it’s perfect for
|
||
teaching IoT</div>
|
||
</h2>
|
||
</div>
|
||
<div class="my r">
|
||
<div class="nq r na nb nc my nd ne nf"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
<div class="hn ho hp hq hr hs"><a rel="noopener"
|
||
href="https://lupyuen.github.io/articles/building-a-rust-driver-for-pinetimes-touch-controller">
|
||
<section class="hv cl cm ak ce n ar hw hx hy hz ia ib ic id ie if ig ih ii ij ik">
|
||
<div class="il n co p im in">
|
||
<h2 class="bj io ip bl ec">
|
||
<div class="di ht fm fn hu fp">Building a Rust Driver for PineTime’s Touch Controller</div>
|
||
</h2>
|
||
</div>
|
||
<div class="my r">
|
||
<div class="nr r na nb nc my nd ne nf"></div>
|
||
</div>
|
||
</section>
|
||
</a></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="cz">
|
||
<div class="n p">
|
||
<div class="ky kz la lb lc ld ag le ah lf aj ak">
|
||
<div class="figure js jt ju jv jw cz lh li paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o26.jpeg" /></p>
|
||
|
||
|
||
<figcaption><p><em>nRF52 connected to Raspberry Pi 4
|
||
</em></p></figcaption>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="1285" class="jb jc ec bk bj io ee ln eg lo jf lp jh lq jj lr jl">Advanced Topic: Remove nRF52
|
||
Flash Protection With Raspberry Pi</h1>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o27.png" /></p>
|
||
|
||
|
||
|
||
<figcaption><p><em>From “nRF52832 — Product Specification” <a
|
||
href="https://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.0.pdf"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.0.pdf</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="5fdf" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Do you have problems flashing or
|
||
debugging your nRF52? Here’s how you can fix it…</p>
|
||
<p id="9861" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">nRF52 has an <strong
|
||
class="gr hh">Access Port Protection</strong> feature that locks the nRF52 flash ROM from any
|
||
modification and prevents debugging. Access Port Protection is enabled in production devices, so that
|
||
people can’t snoop into an nRF52 gadget and tamper with the ROM.</p>
|
||
<p id="871b" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Some nRF52 development boards
|
||
(including my EBYTE E73-TBB) are shipped with Access Port Protection enabled. Most developers use a <a
|
||
href="https://en.wikipedia.org/wiki/Segger_Microcontroller_Systems#J-Link"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">J-LINK</a> USB Programmer to
|
||
remove the Access Port Protection, but I’ll show you how to use a Raspberry Pi (1, 2, 3 or 4) instead…
|
||
</p>
|
||
<p id="2744" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Connect your nRF52 board to a
|
||
Raspberry Pi 1 / 2 / 3 / 4 (powered off) as follows (refer to the photo above)…</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/8bda3c3fed0abfb96c5556b1b81ad598.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>Connecting nRF52 to Raspberry Pi. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/swd-pi.ocd"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/swd-pi.ocd</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<div class="figure js jt ju jv jw cz cl cm paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o28.png" /></p>
|
||
|
||
|
||
|
||
<figcaption><p><em>Connecting nRF52 to Raspberry Pi. Based on <a
|
||
href="https://pinout.xyz/" class="at cg hd he hf hg"
|
||
target="_blank" rel="noopener nofollow">https://pinout.xyz/</a></em></p></figcaption>
|
||
</div>
|
||
<p id="e388" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Power on the Raspberry Pi. Open a
|
||
common prompt on the Raspberry Pi. Enter into the command prompt…</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="1577" class="mi jc ec bk hm b ds mj mk r ml"># Build OpenOCD with CMSIS-DAP and GPIO support</span><span id="3992" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">sudo apt install wget git autoconf libtool make pkg-config libusb-1.0-0 libusb-1.0-0-dev libhidapi-dev libftdi-dev telnet</span><span id="9070" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">git clone https://github.com/ntfreak/openocd</span><span id="854c" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">cd openocd</span><span id="a69c" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">./bootstrap</span><span id="8174" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">./configure --enable-sysfsgpio --enable-bcm2835gpio --enable-cmsis-dap</span><span id="1f2e" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">make</span><span id="3aa7" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml">cd ..</span><span id="8b97" class="mi jc ec bk hm b ds mm mn mo mp mq mk r ml"># Download the OpenOCD script<br/>wget https://raw.githubusercontent.com/lupyuen/stm32bluepill-mynewt-sensor/nrf52/scripts/nrf52/swd-pi.ocd</span></pre>
|
||
<p id="d5aa" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Edit the downloaded <code
|
||
class="dm hj hk hl hm b"><a href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/swd-pi.ocd" class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">swd-pi.ocd</a></code>
|
||
</p>
|
||
<p id="93e9" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Comment out the lines (insert
|
||
<code class="dm hj hk hl hm b">#</code> prefix) for <code
|
||
class="dm hj hk hl hm b">bcm2835gpio_peripheral_base</code> and <code
|
||
class="dm hj hk hl hm b">bcm2835gpio_speed_coeffs</code> under <code
|
||
class="dm hj hk hl hm b">Pi 4</code></p>
|
||
<p id="8c35" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Uncomment the lines (remove <code
|
||
class="dm hj hk hl hm b">#</code> prefix) for <code
|
||
class="dm hj hk hl hm b">bcm2835gpio_peripheral_base</code> and <code
|
||
class="dm hj hk hl hm b">bcm2835gpio_speed_coeffs</code> for your model of Raspberry Pi: <code
|
||
class="dm hj hk hl hm b">Pi 1 / 2 / 3 / 4</code></p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/31b53018d50208003a6f4994b68ed22f.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>OpenOCD script. From <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/swd-pi.ocd"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/swd-pi.ocd</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="d9eb" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Enter into the command prompt…
|
||
</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="8ddb" class="mi jc ec bk hm b ds mj mk r ml"># Start OpenOCD<br/>sudo /home/pi/openocd/src/openocd \<br/> -s /home/pi/openocd/tcl \<br/> -d4 \<br/> -f swd-pi.ocd</span></pre>
|
||
<p id="3b5d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">We should see the following log…
|
||
</p>
|
||
<div class="figure js jt ju jv jw cz">
|
||
|
||
<p><script src="https://gist.github.com/lupyuen/4ced61f3a28a4295971e7ce5ba37f409.js"></script></p>
|
||
|
||
|
||
<figcaption><p><em>OpenOCD Log from <a
|
||
href="https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/swd-pi.log"
|
||
class="at cg hd he hf hg" target="_blank"
|
||
rel="noopener nofollow">https://github.com/lupyuen/stm32bluepill-mynewt-sensor/blob/nrf52/scripts/nrf52/swd-pi.log</a>
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="66e2" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">While OpenOCD is running, open a
|
||
second command prompt and enter…</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="2b37" class="mi jc ec bk hm b ds mj mk r ml">telnet localhost 4444<br/>nrf52.dap apreg 1 0x0c</span></pre>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o29.png" /></p>
|
||
|
||
|
||
<figcaption><p><em>Checking the <code
|
||
class="dm hj hk hl hm b">APPROTECTSTATUS</code> status register for Access Port Protection
|
||
</em></p></figcaption>
|
||
</div>
|
||
<p id="9d74" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This queries the <code
|
||
class="dm hj hk hl hm b">APPROTECTSTATUS</code> status register for Access Port Protection.</p>
|
||
<p id="b852" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">It should show <code
|
||
class="dm hj hk hl hm b">0</code>, which means protection is enabled.</p>
|
||
<p id="4922" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">We’ll now set the <code
|
||
class="dm hj hk hl hm b">ERASEALL</code> register to <code class="dm hj hk hl hm b">1</code> to
|
||
erase the flash ROM and remove Access Port Protection.</p>
|
||
<p id="beb6" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Enter the following commands into
|
||
the <code class="dm hj hk hl hm b">telnet</code> prompt.</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="eae9" class="mi jc ec bk hm b ds mj mk r ml">nrf52.dap apreg 1 0x04 0x01<br/>nrf52.dap apreg 1 0x04</span></pre>
|
||
<p id="8714" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This should show <code
|
||
class="dm hj hk hl hm b">1</code>, which means that the nRF52 is ready to be erased.</p>
|
||
<p id="9e4d" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Shut down and power off the
|
||
Raspberry Pi and nRF52 board.</p>
|
||
<p id="ce87" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Power on the Raspberry Pi and
|
||
nRF52 board. This performs the flash ROM erase.</p>
|
||
<p id="8f01" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">Start OpenOCD by entering into a
|
||
command prompt…</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="f4c9" class="mi jc ec bk hm b ds mj mk r ml">sudo /home/pi/openocd/src/openocd \<br/> -s /home/pi/openocd/tcl \<br/> -d4 \<br/> -f swd-pi.ocd</span></pre>
|
||
<p id="5027" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">While OpenOCD is running, open
|
||
another command prompt and enter…</p>
|
||
<pre
|
||
class="js jt ju jv jw mf mg mh"><span id="e4cc" class="mi jc ec bk hm b ds mj mk r ml">telnet localhost 4444<br/>targets<br/>halt<br/>nrf52.dap apreg 1 0x0c</span></pre>
|
||
<div class="figure js jt ju jv jw cz dr jz cd ka kb kc kd ke ba kf kg kh ki kj kk paragraph-image">
|
||
|
||
<p><img src="https://lupyuen.github.io/images/legacy/o30.png" /></p>
|
||
|
||
|
||
|
||
<figcaption><p><em>Checking APPROTECTSTATUS status register for
|
||
Access Port Protection.</em></p></figcaption>
|
||
</div>
|
||
<p id="ffae" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">This queries the <code
|
||
class="dm hj hk hl hm b">APPROTECTSTATUS</code> status register for access port protection.</p>
|
||
<p id="eb8c" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">It should now show <code
|
||
class="dm hj hk hl hm b">1</code>, which means protection has been disabled.</p>
|
||
<p id="f8e5" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc">We may now disconnect the nRF52
|
||
from the Raspberry Pi and use ST-Link to flash and debug our nRF52!</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<hr class="is dy it iu iv dv iw ix iy iz ja" />
|
||
<section class="cu cv cw cx cy">
|
||
<div class="n p">
|
||
<div class="ac ae af ag ah dz aj ak">
|
||
<h1 id="19be" class="jb jc ec bk bj io ee jd eg je jf jg jh ji jj jk jl">References</h1>
|
||
<p id="5215" class="gp gq ec bk gr b gs jm gu jn gw jo gy jp ha jq hc">My code was tested on the <a
|
||
href="http://www.ebyte.com/product-view-news.aspx?id=644"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">EBYTE E73-TBB Development
|
||
Board</a>. The board is based on the <a
|
||
href="http://www.ebyte.com/en/product-view-news.aspx?id=243"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">EBYTE E73–2G4M04S1B</a> module,
|
||
which embeds the nRF52832 microcontroller.</p>
|
||
<p id="68fa" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><a
|
||
href="https://drive.google.com/file/d/1PiHg8TyQQ73C9MlE0wuMjKbdqV7xFpAb/view?usp=sharing"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Manual for EBYTE E73–2G4M04S1B
|
||
Module</a></p>
|
||
<p id="11ba" class="gp gq ec bk gr b gs gt gu gv gw gx gy gz ha hb hc"><a
|
||
href="https://drive.google.com/file/d/12z7ArTpealX9TsCqjVzrHC8ETt40Koib/view?usp=sharing"
|
||
class="at cg hd he hf hg" target="_blank" rel="noopener nofollow">Manual for EBYTE E73-TBB
|
||
Development Board</a> (Chinese)</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</div>
|
||
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://github.com/sponsors/lupyuen">Sponsor me a coffee</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io">Check out my articles</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/rss.xml">RSS Feed</a></p>
|
||
</li>
|
||
</ul>
|
||
|
||
</body>
|
||
|
||
</html>
|
||
<!--
|
||
FILE ARCHIVED ON 20:18:03 Dec 17, 2019 AND RETRIEVED FROM THE
|
||
INTERNET ARCHIVE ON 23:07:51 Feb 22, 2021.
|
||
JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
|
||
|
||
ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
|
||
SECTION 108(a)(3)).
|
||
-->
|
||
<!--
|
||
playback timings (ms):
|
||
exclusion.robots.policy: 0.272
|
||
CDXLines.iter: 24.786 (3)
|
||
RedisCDXSource: 0.626
|
||
captures_list: 115.484
|
||
esindex: 0.011
|
||
LoadShardBlock: 86.401 (3)
|
||
PetaboxLoader3.resolve: 25.205
|
||
PetaboxLoader3.datanode: 115.682 (4)
|
||
exclusion.robots: 0.283
|
||
load_resource: 96.568
|
||
--> |