mirror of
https://github.com/lupyuen/lupyuen.github.io.git
synced 2025-01-13 02:08:32 +08:00
1436 lines
No EOL
87 KiB
HTML
1436 lines
No EOL
87 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="generator" content="rustdoc">
|
||
<title>LoRaWAN on Apache NuttX OS</title>
|
||
|
||
|
||
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
|
||
<meta property="og:title"
|
||
content="LoRaWAN on Apache NuttX OS"
|
||
data-rh="true">
|
||
<meta property="og:description"
|
||
content="Porting Semtech's LoRaWAN Stack to Apache NuttX OS... And testing it on PineDio Stack BL604 RISC-V Board"
|
||
data-rh="true">
|
||
<meta property="og:image"
|
||
content="https://lupyuen.github.io/images/lorawan3-title.jpg">
|
||
<meta property="og:type"
|
||
content="article" data-rh="true">
|
||
<link rel="canonical" href="https://lupyuen.org/articles/lorawan3.html" />
|
||
<!-- End scripts/articles/*-header.html -->
|
||
<!-- 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="../dark.css">
|
||
<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");
|
||
}
|
||
</style>
|
||
<!-- End scripts/rustdoc-header.html -->
|
||
|
||
|
||
</head>
|
||
<body class="rustdoc">
|
||
<!--[if lte IE 8]>
|
||
<div class="warning">
|
||
This old browser is unsupported and will most likely display funky
|
||
things.
|
||
</div>
|
||
<![endif]-->
|
||
|
||
|
||
<!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
|
||
|
||
<!-- Begin Theme Picker -->
|
||
<div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
|
||
width="18" alt="Pick another theme!"></button>
|
||
<div id="theme-choices"></div>
|
||
</div>
|
||
<!-- Theme Picker -->
|
||
|
||
<!-- End scripts/rustdoc-before.html -->
|
||
|
||
|
||
<h1 class="title">LoRaWAN on Apache NuttX OS</h1>
|
||
<nav id="rustdoc"><ul>
|
||
<li><a href="#small-steps" title="Small Steps">1 Small Steps</a><ul>
|
||
<li><a href="#lorawan-support" title="LoRaWAN Support">1.1 LoRaWAN Support</a><ul></ul></li>
|
||
<li><a href="#dependencies" title="Dependencies">1.2 Dependencies</a><ul></ul></li></ul></li>
|
||
<li><a href="#lorawan-objective" title="LoRaWAN Objective">2 LoRaWAN Objective</a><ul></ul></li>
|
||
<li><a href="#download-source-code" title="Download Source Code">3 Download Source Code</a><ul></ul></li>
|
||
<li><a href="#device-eui-join-eui-and-app-key" title="Device EUI, Join EUI and App Key">4 Device EUI, Join EUI and App Key</a><ul>
|
||
<li><a href="#secure-element" title="Secure Element">4.1 Secure Element</a><ul></ul></li></ul></li>
|
||
<li><a href="#lorawan-frequency" title="LoRaWAN Frequency">5 LoRaWAN Frequency</a><ul></ul></li>
|
||
<li><a href="#build-the-firmware" title="Build The Firmware">6 Build The Firmware</a><ul></ul></li>
|
||
<li><a href="#run-the-firmware" title="Run The Firmware">7 Run The Firmware</a><ul></ul></li>
|
||
<li><a href="#join-lorawan-network" title="Join LoRaWAN Network">8 Join LoRaWAN Network</a><ul>
|
||
<li><a href="#check-lorawan-gateway" title="Check LoRaWAN Gateway">8.1 Check LoRaWAN Gateway</a><ul></ul></li></ul></li>
|
||
<li><a href="#send-data-to-lorawan" title="Send Data To LoRaWAN">9 Send Data To LoRaWAN</a><ul>
|
||
<li><a href="#message-size" title="Message Size">9.1 Message Size</a><ul></ul></li>
|
||
<li><a href="#message-interval" title="Message Interval">9.2 Message Interval</a><ul></ul></li></ul></li>
|
||
<li><a href="#rerun-the-firmware" title="Rerun The Firmware">10 Rerun The Firmware</a><ul>
|
||
<li><a href="#check-lorawan-gateway-1" title="Check LoRaWAN Gateway">10.1 Check LoRaWAN Gateway</a><ul></ul></li></ul></li>
|
||
<li><a href="#lorawan-nonce" title="LoRaWAN Nonce">11 LoRaWAN Nonce</a><ul>
|
||
<li><a href="#strong-random-number-generator" title="Strong Random Number Generator">11.1 Strong Random Number Generator</a><ul></ul></li></ul></li>
|
||
<li><a href="#lorawan-event-loop" title="LoRaWAN Event Loop">12 LoRaWAN Event Loop</a><ul></ul></li>
|
||
<li><a href="#troubleshoot-lorawan" title="Troubleshoot LoRaWAN">13 Troubleshoot LoRaWAN</a><ul>
|
||
<li><a href="#lorawan-is-time-sensitive" title="LoRaWAN is Time Sensitive">13.1 LoRaWAN is Time Sensitive</a><ul></ul></li>
|
||
<li><a href="#empty-lorawan-message" title="Empty LoRaWAN Message">13.2 Empty LoRaWAN Message</a><ul></ul></li></ul></li>
|
||
<li><a href="#spi-with-dma" title="SPI With DMA">14 SPI With DMA</a><ul></ul></li>
|
||
<li><a href="#whats-next" title="What’s Next">15 What’s Next</a><ul></ul></li>
|
||
<li><a href="#notes" title="Notes">16 Notes</a><ul></ul></li>
|
||
<li><a href="#appendix-posix-timers-and-message-queues" title="Appendix: POSIX Timers and Message Queues">17 Appendix: POSIX Timers and Message Queues</a><ul></ul></li>
|
||
<li><a href="#appendix-random-number-generator-with-entropy-pool" title="Appendix: Random Number Generator with Entropy Pool">18 Appendix: Random Number Generator with Entropy Pool</a><ul></ul></li>
|
||
<li><a href="#appendix-build-flash-and-run-nuttx" title="Appendix: Build, Flash and Run NuttX">19 Appendix: Build, Flash and Run NuttX</a><ul>
|
||
<li><a href="#build-nuttx" title="Build NuttX">19.1 Build NuttX</a><ul></ul></li>
|
||
<li><a href="#flash-nuttx" title="Flash NuttX">19.2 Flash NuttX</a><ul></ul></li>
|
||
<li><a href="#run-nuttx" title="Run NuttX">19.3 Run NuttX</a><ul></ul></li></ul></li></ul></nav><p>📝 <em>3 Jan 2022</em></p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-title.jpg" alt="PineDio Stack BL604 RISC-V Board (left) talking LoRaWAN to RAKwireless WisGate LoRaWAN Gateway (right)" /></p>
|
||
<p><em>PineDio Stack BL604 RISC-V Board (left) talking LoRaWAN to RAKwireless WisGate LoRaWAN Gateway (right)</em></p>
|
||
<p>Last article we got <strong>LoRa</strong> (the long-range, low-bandwidth wireless network) running on <a href="https://lupyuen.github.io/articles/nuttx"><strong>Apache NuttX OS</strong></a>…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/sx1262"><strong>“LoRa SX1262 on Apache NuttX OS”</strong></a></li>
|
||
</ul>
|
||
<p>Today we shall run <strong>LoRaWAN</strong> on NuttX OS!</p>
|
||
<p><em>Why would we need LoRaWAN?</em></p>
|
||
<p>LoRa will work perfectly fine for unsecured <strong>Point-to-Point Wireless Communication</strong> between simple devices.</p>
|
||
<p>But if we’re building an <strong>IoT Sensor Device</strong> that will <strong>transmit data packets</strong> securely to a Local Area Network or to the internet, we need <strong>LoRaWAN</strong>.</p>
|
||
<p><a href="https://makezine.com/2021/05/24/go-long-with-lora-radio/">(More about LoRaWAN)</a></p>
|
||
<p>We shall test LoRaWAN on NuttX with <a href="https://lupyuen.github.io/articles/pinedio2"><strong>PineDio Stack BL604 RISC-V Board</strong></a> (pic above) and its onboard Semtech SX1262 Transceiver.</p>
|
||
<p><a href="https://twitter.com/4ever_freedom/status/1555048288272932864">(LoRaWAN on NuttX works OK on <strong>ESP32</strong>, thanks @4ever_freedom!)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/sx1262-library5.jpg" alt="Porting LoRaWAN to NuttX OS" /></p>
|
||
<h1 id="small-steps"><a class="doc-anchor" href="#small-steps">§</a>1 Small Steps</h1>
|
||
<p>In the last article we created a <strong>LoRa Library for NuttX</strong> (top right) that works with <strong>Semtech SX1262 Transceiver</strong>…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/lupyuen/lora-sx1262/tree/lorawan"><strong>lupyuen/lora-sx1262 (lorawan branch)</strong></a></li>
|
||
</ul>
|
||
<p>Today we’ll create a <strong>LoRaWAN Library for NuttX</strong> (centre right)…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/lupyuen/LoRaMac-node-nuttx"><strong>lupyuen/LoRaMac-node-nuttx</strong></a></li>
|
||
</ul>
|
||
<p>That’s a near-identical fork of <strong>Semtech’s LoRaWAN Stack</strong> (dated 14 Dec 2021)…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/Lora-net/LoRaMac-node"><strong>Lora-net/LoRaMac-node</strong></a></li>
|
||
</ul>
|
||
<p>We’ll test with this <strong>LoRaWAN App</strong> on NuttX…</p>
|
||
<ul>
|
||
<li><a href="https://github.com/lupyuen/lorawan_test"><strong>lupyuen/lorawan_test</strong></a></li>
|
||
</ul>
|
||
<h2 id="lorawan-support"><a class="doc-anchor" href="#lorawan-support">§</a>1.1 LoRaWAN Support</h2>
|
||
<p><em>Why did we fork Semtech’s LoRaWAN Stack? Why not build it specifically for NuttX?</em></p>
|
||
<p>LoRaWAN works <strong>slightly differently across the world regions</strong>, to comply with Local Wireless Regulations: Radio Frequency, Maximum Airtime (Duty Cycle), <a href="https://lupyuen.github.io/articles/lorawan#appendix-lora-carrier-sensing">Listen Before Talk</a>, …</p>
|
||
<p>Thus we should port <strong>Semtech’s LoRaWAN Stack</strong> to NuttX with <strong>minimal changes</strong>, in case of future updates. (Like for new regions)</p>
|
||
<p><em>How does our LoRaWAN Library talk to the LoRa SX1262 Library?</em></p>
|
||
<p>Our LoRaWAN Library talks through Semtech’s <strong>Radio Interface</strong> that’s exposed by the LoRa SX1262 Library…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/sx1262#appendix-radio-functions"><strong>“Radio Functions (LoRa SX1262)”</strong></a></li>
|
||
</ul>
|
||
<p><em>How did we create the LoRaWAN Library?</em></p>
|
||
<p>We followed the steps below to create <strong>“nuttx/libs/liblorawan”</strong> by cloning a NuttX Library…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/sx1262#appendix-create-a-nuttx-library"><strong>“Create a NuttX Library”</strong></a></li>
|
||
</ul>
|
||
<p>Then we replaced the “liblorawan” folder by a <strong>Git Submodule</strong> that contains our LoRaWAN code…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>cd nuttx/nuttx/libs
|
||
rm -r liblorawan
|
||
git rm -r liblorawan
|
||
git submodule add https://github.com/lupyuen/LoRaMac-node-nuttx liblorawan</code></pre></div>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#download-source-code">(To add the LoRaWAN Library to your NuttX Project, see this)</a></p>
|
||
<h2 id="dependencies"><a class="doc-anchor" href="#dependencies">§</a>1.2 Dependencies</h2>
|
||
<p>Our LoRaWAN Library should work on <strong>any NuttX platform</strong> (like ESP32), assuming that the following dependencies are installed…</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/lora-sx1262/tree/lorawan"><strong>lupyuen/lora-sx1262 (lorawan branch)</strong></a></p>
|
||
<p>LoRa Library for Semtech SX1262 Transceiver</p>
|
||
<p><a href="https://lupyuen.github.io/articles/sx1262">(See this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nimble-porting-nuttx"><strong>lupyuen/nimble-porting-nuttx</strong></a></p>
|
||
<p>NimBLE Porting Layer multithreading library</p>
|
||
<p><a href="https://lupyuen.github.io/articles/sx1262#multithreading-with-nimble-porting-layer">(See this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nuttx/tree/lorawan/drivers/rf"><strong>spi_test_driver (/dev/spitest0)</strong></a></p>
|
||
<p>SPI Test Driver</p>
|
||
<p><a href="https://lupyuen.github.io/articles/spi2">(See this)</a></p>
|
||
</li>
|
||
</ul>
|
||
<p>Our LoRa SX1262 Library assumes that the following <strong>NuttX Devices</strong> are configured…</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>/dev/gpio0</strong>: GPIO Input for SX1262 Busy Pin</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>/dev/gpio1</strong>: GPIO Output for SX1262 Chip Select</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>/dev/gpio2</strong>: GPIO Interrupt for SX1262 DIO1 Pin</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>/dev/spi0</strong>: SPI Bus for SX1262</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>/dev/spitest0</strong>: SPI Test Driver (see above)</p>
|
||
</li>
|
||
</ul>
|
||
<h1 id="lorawan-objective"><a class="doc-anchor" href="#lorawan-objective">§</a>2 LoRaWAN Objective</h1>
|
||
<p><em>What shall we accomplish with LoRaWAN today?</em></p>
|
||
<p>We’ll do the basic LoRaWAN use case on NuttX…</p>
|
||
<ul>
|
||
<li>
|
||
<p>Join NuttX to the <strong>LoRaWAN Network</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Send a <strong>Data Packet</strong> from NuttX to LoRaWAN</p>
|
||
</li>
|
||
</ul>
|
||
<p>Which works like this…</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-flow.jpg" alt="LoRaWAN Use Case" /></p>
|
||
<ol>
|
||
<li>
|
||
<p>NuttX sends a <strong>Join Network Request</strong> to the LoRaWAN Gateway.</p>
|
||
<p>Inside the Join Network Request are…</p>
|
||
<p><strong>Device EUI:</strong> Unique ID that’s assigned to our LoRaWAN Device</p>
|
||
<p><strong>Join EUI:</strong> Identifies the LoRaWAN Network that we’re joining</p>
|
||
<p><strong>Nonce:</strong> Non-repeating number, to prevent <a href="https://en.wikipedia.org/wiki/Replay_attack">Replay Attacks</a></p>
|
||
<p><em>(EUI sounds like Durian on Century Egg… But it actually means Extended Unique Identifier)</em></p>
|
||
</li>
|
||
<li>
|
||
<p>LoRaWAN Gateway returns a <strong>Join Network Response</strong></p>
|
||
<p><em>(Which contains the Device Address)</em></p>
|
||
</li>
|
||
<li>
|
||
<p>NuttX sends a <strong>Data Packet</strong> to the LoRaWAN Network</p>
|
||
<p><em>(Which has the Device Address and Payload “Hi NuttX”)</em></p>
|
||
</li>
|
||
<li>
|
||
<p>NuttX uses an <strong>App Key</strong> to sign the Join Network Request and the Data Packet</p>
|
||
<p><em>(App Key is stored inside NuttX, never exposed over the airwaves)</em></p>
|
||
</li>
|
||
</ol>
|
||
<p>In a while we’ll set the Device EUI, Join EUI and App Key in our code.</p>
|
||
<h1 id="download-source-code"><a class="doc-anchor" href="#download-source-code">§</a>3 Download Source Code</h1>
|
||
<p>To run LoRaWAN on NuttX, download the modified source code for <strong>NuttX OS and NuttX Apps</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>mkdir nuttx
|
||
cd nuttx
|
||
git clone --recursive --branch lorawan https://github.com/lupyuen/nuttx nuttx
|
||
git clone --recursive --branch lorawan https://github.com/lupyuen/nuttx-apps apps</code></pre></div>
|
||
<p>Or if we prefer to <strong>add the LoRaWAN Library</strong> to our NuttX Project, follow these instructions…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/pinedio2#appendix-bundled-features">(<strong>For PineDio Stack BL604:</strong> The features below are already preinstalled)</a></p>
|
||
<ol>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nuttx/tree/lorawan/drivers/rf"><strong>“Install SPI Test Driver”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/nimble-porting-nuttx"><strong>“Install NimBLE Porting Layer”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/lora-sx1262/tree/lorawan"><strong>“Install LoRa SX1262 Library”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/LoRaMac-node-nuttx"><strong>“Install LoRaWAN Library”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/lupyuen/lorawan_test"><strong>“Install LoRaWAN Test App”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Disable the Assertion Check for <strong>GPIO Pin Type</strong>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/sx1262#appendix-gpio-pin-type-issue"><strong>“GPIO Pin Type Issue”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<p>Let’s configure our LoRaWAN code.</p>
|
||
<p><img src="https://lupyuen.github.io/images/wisgate-app2.png" alt="Device EUI from ChirpStack" /></p>
|
||
<h1 id="device-eui-join-eui-and-app-key"><a class="doc-anchor" href="#device-eui-join-eui-and-app-key">§</a>4 Device EUI, Join EUI and App Key</h1>
|
||
<p><em>Where do we get the Device EUI, Join EUI and App Key?</em></p>
|
||
<p>We get the LoRaWAN Settings from our <strong>LoRaWAN Gateway</strong>, like ChirpStack (pic above)…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/wisgate#lorawan-application"><strong>“LoRaWAN Application (ChirpStack)”</strong></a></li>
|
||
</ul>
|
||
<p><em>How do we set the Device EUI, Join EUI and App Key in our code?</em></p>
|
||
<p>Edit the file…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>nuttx/libs/liblorawan/src/peripherals/soft-se/se-identity.h</code></pre></div>
|
||
<p>Look for these lines in <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/peripherals/soft-se/se-identity.h#L65-L79"><strong>se-identity.h</strong></a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>/*!
|
||
* When set to 1 DevEui is LORAWAN_DEVICE_EUI
|
||
* When set to 0 DevEui is automatically set with a value provided by MCU platform
|
||
*/
|
||
#define STATIC_DEVICE_EUI 1
|
||
|
||
/*!
|
||
* end-device IEEE EUI (big endian)
|
||
*/
|
||
#define LORAWAN_DEVICE_EUI { 0x4b, 0xc1, 0x5e, 0xe7, 0x37, 0x7b, 0xb1, 0x5b }
|
||
|
||
/*!
|
||
* App/Join server IEEE EUI (big endian)
|
||
*/
|
||
#define LORAWAN_JOIN_EUI { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }</code></pre></div>
|
||
<ul>
|
||
<li>
|
||
<p><strong>STATIC_DEVICE_EUI:</strong> Must be <code>1</code></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>LORAWAN_DEVICE_EUI:</strong> Change this to our <strong>LoRaWAN Device EUI</strong> (MSB First)</p>
|
||
<p>For ChirpStack: Copy from “Applications → app → Device EUI”</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>LORAWAN_JOIN_EUI:</strong> Change this to our <strong>LoRaWAN Join EUI</strong> (MSB First)</p>
|
||
<p>For ChirpStack: Join EUI is not needed, we leave it as zeroes</p>
|
||
</li>
|
||
</ul>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-secure1.png" alt="Device EUI and Join EUI" /></p>
|
||
<p>Next find this in the same file <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/peripherals/soft-se/se-identity.h#L98-L115"><strong>se-identity.h</strong></a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>#define SOFT_SE_KEY_LIST \
|
||
{ \
|
||
{ \
|
||
/*! \
|
||
* Application root key \
|
||
* WARNING: FOR 1.0.x DEVICES IT IS THE \ref LORAWAN_GEN_APP_KEY \
|
||
*/ \
|
||
.KeyID = APP_KEY, \
|
||
.KeyValue = { 0xaa, 0xff, 0xad, 0x5c, 0x7e, 0x87, 0xf6, 0x4d, 0xe3, 0xf0, 0x87, 0x32, 0xfc, 0x1d, 0xd2, 0x5d }, \
|
||
}, \
|
||
{ \
|
||
/*! \
|
||
* Network root key \
|
||
* WARNING: FOR 1.0.x DEVICES IT IS THE \ref LORAWAN_APP_KEY \
|
||
*/ \
|
||
.KeyID = NWK_KEY, \
|
||
.KeyValue = { 0xaa, 0xff, 0xad, 0x5c, 0x7e, 0x87, 0xf6, 0x4d, 0xe3, 0xf0, 0x87, 0x32, 0xfc, 0x1d, 0xd2, 0x5d }, \
|
||
}, \</code></pre></div>
|
||
<ul>
|
||
<li>
|
||
<p><strong>APP_KEY:</strong> Change this to our <strong>LoRaWAN App Key</strong> (MSB First)</p>
|
||
<p>For ChirpStack: Copy from “Applications → app → Devices → device_otaa_class_a → Keys (OTAA) → Application Key”</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>NWK_KEY:</strong> Change this to our <strong>LoRaWAN App Key</strong></p>
|
||
<p>(Same as <strong>APP_KEY</strong>)</p>
|
||
</li>
|
||
</ul>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-secure2a.png" alt="App Key" /></p>
|
||
<h2 id="secure-element"><a class="doc-anchor" href="#secure-element">§</a>4.1 Secure Element</h2>
|
||
<p><em>What’s “soft-se”? Why are our LoRaWAN Settings there?</em></p>
|
||
<p>For LoRaWAN Devices that are designed to be <strong>super secure</strong>, they <strong>don’t expose the LoRaWAN App Key</strong> in the firmware code…</p>
|
||
<p>Instead they store the App Key in the <a href="https://encyclopedia.kaspersky.com/glossary/secure-element/"><strong>Secure Element</strong></a> hardware.</p>
|
||
<p>Our LoRaWAN Library supports two kinds of Secure Elements: <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/tree/master/src/peripherals/atecc608a-tnglora-se"><strong>Microchip ATECC608A</strong></a> and <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/tree/master/src/peripherals/lr1110-se"><strong>Semtech LR1110</strong></a></p>
|
||
<p><em>But our NuttX Device doesn’t have a Secure Element right?</em></p>
|
||
<p>That’s why we define the App Key in the <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/tree/master/src/peripherals/soft-se"><strong>“Software Secure Element (soft-se)”</strong></a> that simulates a Hardware Secure Element… Minus the actual hardware security.</p>
|
||
<p>Our App Key will be exposed if somebody dumps the firmware for our NuttX Device. But it’s probably OK during development.</p>
|
||
<h1 id="lorawan-frequency"><a class="doc-anchor" href="#lorawan-frequency">§</a>5 LoRaWAN Frequency</h1>
|
||
<p>Let’s set the LoRaWAN Frequency…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Find the <strong>LoRaWAN Frequency</strong> for our region…</p>
|
||
<p><a href="https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html"><strong>“Frequency Plans by Country”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Edit our <strong>LoRaWAN Test App</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>apps/examples/lorawan_test/lorawan_test_main.c</code></pre></div></li>
|
||
<li>
|
||
<p>Find this in <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L34-L40"><strong>lorawan_test_main.c</strong></a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>#ifndef ACTIVE_REGION
|
||
#warning "No active region defined, LORAMAC_REGION_AS923 will be used as default."
|
||
#define ACTIVE_REGION LORAMAC_REGION_AS923
|
||
#endif</code></pre></div></li>
|
||
<li>
|
||
<p>Change <strong>AS923</strong> (both occurrences) to our LoRaWAN Frequency…</p>
|
||
<p><strong>US915</strong>, <strong>CN779</strong>, <strong>EU433</strong>, <strong>AU915</strong>, <strong>AS923</strong>, <strong>CN470</strong>, <strong>KR920</strong>, <strong>IN865</strong> or <strong>RU864</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Do the same for the LoRaMAC Handler: <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/apps/LoRaMac/common/LmHandler/LmHandler.c#L41-L47"><strong>LmHandler.c</strong></a></p>
|
||
<div class="example-wrap"><pre class="language-text"><code>nuttx/libs/liblorawan/src/apps/LoRaMac/common/LmHandler/LmHandler.c</code></pre></div>
|
||
<p>(We ought to define this parameter in Kconfig instead)</p>
|
||
</li>
|
||
</ol>
|
||
<h1 id="build-the-firmware"><a class="doc-anchor" href="#build-the-firmware">§</a>6 Build The Firmware</h1>
|
||
<p>Let’s build the NuttX Firmware that contains our <strong>LoRaWAN Library</strong>…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Install the build prerequisites…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/nuttx#install-prerequisites"><strong>“Install Prerequisites”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Assume that we have downloaded the <strong>NuttX Source Code</strong> and configured the <strong>LoRaWAN Settings</strong>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#download-source-code"><strong>“Download Source Code”</strong></a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#device-eui-join-eui-and-app-key"><strong>“Device EUI, Join EUI and App Key”</strong></a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#lorawan-frequency"><strong>“LoRaWAN Frequency”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Edit the <strong>Pin Definitions</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>## For BL602 and BL604:
|
||
nuttx/boards/risc-v/bl602/bl602evb/include/board.h
|
||
|
||
## For ESP32: Change "esp32-devkitc" to our ESP32 board
|
||
nuttx/boards/xtensa/esp32/esp32-devkitc/src/esp32_gpio.c</code></pre></div>
|
||
<p>Check that the <strong>Semtech SX1262 Pins</strong> are configured correctly in <a href="https://github.com/lupyuen/nuttx/blob/lorawan/boards/risc-v/bl602/bl602evb/include/board.h#L36-L95"><strong>board.h</strong></a> or <a href="https://github.com/lupyuen/nuttx/blob/lorawan/boards/xtensa/esp32/esp32-devkitc/src/esp32_gpio.c#L43-L67"><strong>esp32_gpio.c</strong></a>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/expander#pin-functions">(Which pins can be used? See this)</a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/sx1262#connect-sx1262-transceiver"><strong>“Connect SX1262 Transceiver”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Configure the build…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>cd nuttx
|
||
|
||
## For BL602: Configure the build for BL602
|
||
./tools/configure.sh bl602evb:nsh
|
||
|
||
## For PineDio Stack BL604: Configure the build for BL604
|
||
./tools/configure.sh bl602evb:pinedio
|
||
|
||
## For ESP32: Configure the build for ESP32.
|
||
## TODO: Change "esp32-devkitc" to our ESP32 board.
|
||
./tools/configure.sh esp32-devkitc:nsh
|
||
|
||
## Edit the Build Config
|
||
make menuconfig </code></pre></div></li>
|
||
<li>
|
||
<p>Enable the <strong>GPIO Driver</strong> in menuconfig…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/nuttx#enable-gpio-driver"><strong>“Enable GPIO Driver”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable the <strong>SPI Peripheral</strong>, <strong>SPI Character Driver</strong> and <strong>SPI Test Driver</strong>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/spi2#enable-spi"><strong>“Enable SPI”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable <strong>GPIO and SPI Logging</strong> for easier troubleshooting, but uncheck <strong>“Enable Info Debug Output”</strong>, <strong>“GPIO Info Output”</strong> and <strong>“SPI Info Output”</strong>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/spi2#enable-logging"><strong>“Enable Logging”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable <strong>Stack Backtrace</strong> for easier troubleshooting…</p>
|
||
<p>Check the box for <strong>“RTOS Features”</strong> → <strong>“Stack Backtrace”</strong></p>
|
||
<p><a href="https://lupyuen.github.io/images/lorawan3-config4.png">(See this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable <strong>POSIX Timers and Message Queues</strong> (for NimBLE Porting Layer)…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#appendix-posix-timers-and-message-queues"><strong>“POSIX Timers and Message Queues”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable <strong>Random Number Generator with Entropy Pool</strong> (for LoRaWAN Nonces)…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#appendix-random-number-generator-with-entropy-pool"><strong>“Random Number Generator with Entropy Pool”</strong></a></p>
|
||
<p>(We’ll talk about this in a while)</p>
|
||
</li>
|
||
<li>
|
||
<p>Click <strong>“Library Routines”</strong> and enable the following libraries…</p>
|
||
<p><strong>“LoRaWAN Library”</strong></p>
|
||
<p><strong>“NimBLE Porting Layer”</strong></p>
|
||
<p><strong>“Semtech SX1262 Library”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Enable our <strong>LoRaWAN Test App</strong>…</p>
|
||
<p>Check the box for <strong>“Application Configuration”</strong> → <strong>“Examples”</strong> → <strong>“LoRaWAN Test App”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Save the configuration and exit menuconfig</p>
|
||
<p><a href="https://gist.github.com/lupyuen/d0487cda965f72ed99631d168ea4f5c8">(See the .config for BL602 and BL604)</a></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>For ESP32:</strong> Edit the function <strong>esp32_bringup</strong> in this file…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>## Change "esp32-devkitc" to our ESP32 board
|
||
nuttx/boards/xtensa/esp32/esp32-devkitc/src/esp32_bringup.c</code></pre></div>
|
||
<p>And call <strong>spi_test_driver_register</strong> to register our SPI Test Driver.</p>
|
||
<p><a href="https://lupyuen.github.io/articles/spi2#register-device-driver">(See this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Build, flash and run the NuttX Firmware on BL602 or ESP32…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#appendix-build-flash-and-run-nuttx"><strong>“Build, Flash and Run NuttX”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-tx3.png" alt="Our NuttX Device successfully joins the LoRaWAN Network" /></p>
|
||
<p><a href="https://gist.github.com/lupyuen/83be5da091273bb39bad6e77cc91b68d">(Source)</a></p>
|
||
<h1 id="run-the-firmware"><a class="doc-anchor" href="#run-the-firmware">§</a>7 Run The Firmware</h1>
|
||
<p>We’re ready to run the NuttX Firmware and test our <strong>LoRaWAN Library</strong>!</p>
|
||
<ol>
|
||
<li>
|
||
<p>In the NuttX Shell, list the <strong>NuttX Devices</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>ls /dev</code></pre></div></li>
|
||
<li>
|
||
<p>We should see…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>/dev:
|
||
gpio0
|
||
gpio1
|
||
gpio2
|
||
spi0
|
||
spitest0
|
||
urandom
|
||
...</code></pre></div>
|
||
<p>Our SPI Test Driver appears as <strong>“/dev/spitest0”</strong></p>
|
||
<p>The SX1262 Pins for Busy, Chip Select and DIO1 should appear as <strong>“/dev/gpio0”</strong> (GPIO Input), <strong>“gpio1”</strong> (GPIO Output) and <strong>“gpio2”</strong> (GPIO Interrupt) respectively.</p>
|
||
<p>The Random Number Generator (with Entropy Pool) appears as <strong>“/dev/urandom”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>In the NuttX Shell, run our <strong>LoRaWAN Test App</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>lorawan_test</code></pre></div>
|
||
<p>Our app sends a <strong>Join Network Request</strong> to the LoRaWAN Gateway…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>RadioSetPublicNetwork: public syncword=3444
|
||
DevEui : 4B-C1-5E-E7-37-7B-B1-5B
|
||
JoinEui : 00-00-00-00-00-00-00-00
|
||
Pin : 00-00-00-00
|
||
### =========== MLME-Request ============ ##
|
||
### MLME_JOIN ##
|
||
### ===================================== ##
|
||
STATUS : OK</code></pre></div>
|
||
<p>(Which contains the Device EUI and Join EUI that we have configured earlier)</p>
|
||
</li>
|
||
<li>
|
||
<p>A few seconds later we should see the <strong>Join Network Response</strong> from the LoRaWAN Gateway…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>### =========== MLME-Confirm ============ ##
|
||
STATUS : OK
|
||
### =========== JOINED ============ ##
|
||
OTAA
|
||
DevAddr : 01DA9790
|
||
DATA RATE : DR_2</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/83be5da091273bb39bad6e77cc91b68d">(See the Output Log)</a></p>
|
||
<p>Congratulations our NuttX Device has successfully joined the LoRaWAN Network!</p>
|
||
</li>
|
||
<li>
|
||
<p>If we see this instead…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>### =========== MLME-Confirm ============ ##
|
||
STATUS : Rx 1 timeout</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/007788b9ea3974b127f6260bf57f5d8b">(See the Output Log)</a></p>
|
||
<p>Our Join Network Request has failed.</p>
|
||
<p>Check the next section for troubleshooting tips.</p>
|
||
</li>
|
||
<li>
|
||
<p>Our LoRaWAN Test App continues to <strong>transmit Data Packets</strong>. But we’ll cover this later…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>PrepareTxFrame: Transmit to LoRaWAN: Hi NuttX (9 bytes)
|
||
PrepareTxFrame: status=0, maxSize=11, currentSize=11
|
||
### =========== MCPS-Request ============ ##
|
||
### MCPS_UNCONFIRMED ##
|
||
### ===================================== ##
|
||
STATUS : OK
|
||
PrepareTxFrame: Transmit OK</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/83be5da091273bb39bad6e77cc91b68d">(See the Output Log)</a></p>
|
||
<p>Let’s find out how our LoRaWAN Test App joins the LoRaWAN Network.</p>
|
||
</li>
|
||
</ol>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-flow2.jpg" alt="Join LoRaWAN Network" /></p>
|
||
<h1 id="join-lorawan-network"><a class="doc-anchor" href="#join-lorawan-network">§</a>8 Join LoRaWAN Network</h1>
|
||
<p><em>How do we join the LoRaWAN Network in our NuttX App?</em></p>
|
||
<p>Let’s dive into the code for our <strong>LoRaWAN Test App</strong>: <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L260-L303">lorawan_test_main.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>int main(int argc, FAR char *argv[]) {
|
||
|
||
// Compute the interval between transmissions based on Duty Cycle
|
||
TxPeriodicity = APP_TX_DUTYCYCLE + randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );</code></pre></div>
|
||
<p>Our app begins by computing the <strong>Time Interval Between Transmissions</strong> of our Data Packets.</p>
|
||
<p>(More about this later)</p>
|
||
<p>Next it calls <strong>LmHandlerInit</strong> to initialise the LoRaWAN Library…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Init LoRaWAN
|
||
if ( LmHandlerInit(
|
||
&LmHandlerCallbacks, // Callback Functions
|
||
&LmHandlerParams // LoRaWAN Parameters
|
||
) != LORAMAC_HANDLER_SUCCESS ) {
|
||
printf( "LoRaMac wasn't properly initialized\n" );
|
||
while ( 1 ) {} // Fatal error, endless loop.
|
||
}</code></pre></div>
|
||
<p>(Functions named <strong>“Lm…”</strong> come from our LoRaWAN Library)</p>
|
||
<p>We set load the <strong>LoRa Alliance Compliance Protocol Packages</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Set system maximum tolerated rx error in milliseconds
|
||
LmHandlerSetSystemMaxRxError( 20 );
|
||
|
||
// LoRa-Alliance Compliance protocol package should always be initialized and activated.
|
||
LmHandlerPackageRegister( PACKAGE_ID_COMPLIANCE, &LmhpComplianceParams );
|
||
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL );
|
||
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL );
|
||
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &FragmentationParams );</code></pre></div>
|
||
<p>Below is the code that sends the <strong>Join Network Request</strong> to the LoRaWAN Gateway: <strong>LmHandlerJoin</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Join the LoRaWAN Network
|
||
LmHandlerJoin( );</code></pre></div>
|
||
<p>We start the <strong>Transmit Timer</strong> that will schedule the transmission of Data Packets (right after we have joined the LoRaWAN Network)…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Set the Transmit Timer
|
||
StartTxProcess( LORAMAC_HANDLER_TX_ON_TIMER );</code></pre></div>
|
||
<p>At this point we haven’t actually joined the LoRaWAN Network yet.</p>
|
||
<p>This happens in the <strong>LoRaWAN Event Loop</strong> that will handle the <strong>Join Network Response</strong> received from the LoRaWAN Gateway…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Handle LoRaWAN Events
|
||
handle_event_queue( NULL ); // Never returns
|
||
return 0;
|
||
}</code></pre></div>
|
||
<p>(We’ll talk about the LoRaWAN Event Loop later)</p>
|
||
<p>Let’s check the logs on our LoRaWAN Gateway. (RAKwireless WisGate, the black box below)</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-title.jpg" alt="PineDio Stack BL604 RISC-V Board (left) talking LoRaWAN to RAKwireless WisGate LoRaWAN Gateway (right)" /></p>
|
||
<h2 id="check-lorawan-gateway"><a class="doc-anchor" href="#check-lorawan-gateway">§</a>8.1 Check LoRaWAN Gateway</h2>
|
||
<p>To inspect the Join Network Request on our <strong>LoRaWAN Gateway</strong> (ChirpStack), click…</p>
|
||
<p><strong>Applications</strong> → <strong>app</strong> → <strong>device_otaa_class_a</strong> → <strong>LoRaWAN Frames</strong></p>
|
||
<p>Restart our NuttX Device and the LoRaWAN Test App…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/lorawan3#run-the-firmware"><strong>“Run The Firmware”</strong></a></li>
|
||
</ul>
|
||
<p>The <strong>Join Network Request</strong> appears in ChirpStack…</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack.png" alt="Join Network Request" /></p>
|
||
<p>(Yep that’s the Device EUI and Join EUI that we have configured earlier)</p>
|
||
<p>Followed by the <strong>Join Accept Response</strong>…</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack7.png" alt="Join Accept Response" /></p>
|
||
<p>The Join Network Request / Response also appears in ChirpStack at…</p>
|
||
<p><strong>Applications</strong> → <strong>app</strong> → <strong>device_otaa_class_a</strong> → <strong>Device Data</strong></p>
|
||
<p>Like so (“Join”)…</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack10.png" alt="Join Accept Response" /></p>
|
||
<p><em>What if we don’t see the Join Network Request or the Join Accept Response?</em></p>
|
||
<p>Check the <strong>“Troubleshoot LoRaWAN”</strong> section below for troubleshooting tips.</p>
|
||
<h1 id="send-data-to-lorawan"><a class="doc-anchor" href="#send-data-to-lorawan">§</a>9 Send Data To LoRaWAN</h1>
|
||
<p>Now that we’ve joined the LoRaWAN Network, we’re ready to <strong>send Data Packets</strong> to LoRaWAN!</p>
|
||
<p><strong>PrepareTxFrame</strong> is called by our LoRaWAN Event Loop to send a Data Packet when the <strong>Transmit Timer</strong> expires: <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L305-L336">lorawan_test_main.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>// Prepare the payload of a Data Packet transmit it
|
||
static void PrepareTxFrame( void ) {
|
||
|
||
// If we haven't joined the LoRaWAN Network, try again later
|
||
if (LmHandlerIsBusy()) { puts("PrepareTxFrame: Busy"); return; }</code></pre></div>
|
||
<p>If we haven’t joined a LoRaWAN Network yet, this function will return. (And we’ll try again later)</p>
|
||
<p>Assuming all is hunky dory, we proceed to transmit a <strong>9-byte message</strong> (including terminating null)…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Send a message to LoRaWAN
|
||
const char msg[] = "Hi NuttX";
|
||
printf("PrepareTxFrame: Transmit to LoRaWAN: %s (%d bytes)\n", msg, sizeof(msg));</code></pre></div>
|
||
<p>We copy the message to the <strong>Transmit Buffer</strong> (max 242 bytes) and create a <strong>Transmit Request</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Compose the transmit request
|
||
assert(sizeof(msg) <= sizeof(AppDataBuffer));
|
||
memcpy(AppDataBuffer, msg, sizeof(msg));
|
||
LmHandlerAppData_t appData = { // Transmit Request contains...
|
||
.Buffer = AppDataBuffer, // Transmit Buffer
|
||
.BufferSize = sizeof(msg), // Size of Transmit Buffer
|
||
.Port = 1, // Port Number: 1 to 223
|
||
};</code></pre></div>
|
||
<p>Next we <strong>validate the Message Size</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Validate the message size and check if it can be transmitted
|
||
LoRaMacTxInfo_t txInfo;
|
||
LoRaMacStatus_t status = LoRaMacQueryTxPossible(
|
||
appData.BufferSize, // Message size
|
||
&txInfo // Returns max message size
|
||
);
|
||
printf("PrepareTxFrame: status=%d, maxSize=%d, currentSize=%d\n", status, txInfo.MaxPossibleApplicationDataSize, txInfo.CurrentPossiblePayloadSize);
|
||
assert(status == LORAMAC_STATUS_OK);</code></pre></div>
|
||
<p>(What’s the Maximum Message Size? We’ll discuss in a while)</p>
|
||
<p>Finally we <strong>transmit the message</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Transmit the message
|
||
LmHandlerErrorStatus_t sendStatus = LmHandlerSend(
|
||
&appData, // Transmit Request
|
||
LmHandlerParams.IsTxConfirmed // 0 for Unconfirmed
|
||
);
|
||
assert(sendStatus == LORAMAC_HANDLER_SUCCESS);
|
||
puts("PrepareTxFrame: Transmit OK");
|
||
}</code></pre></div>
|
||
<p><em>Why is our Data Packet marked Unconfirmed?</em></p>
|
||
<p>Our Data Packet is marked Unconfirmed because we <strong>don’t expect an acknowledgement</strong> from the LoRaWAN Gateway.</p>
|
||
<p>This is the typical mode for <strong>IoT Sensor Devices</strong>, which don’t handle acknowledgements to conserve battery power.</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-tx6.png" alt="Sending a LoRaWAN Data Packet" /></p>
|
||
<h2 id="message-size"><a class="doc-anchor" href="#message-size">§</a>9.1 Message Size</h2>
|
||
<p><em>What’s the Maximum Message Size?</em></p>
|
||
<p>The <strong>Maximum Message (Payload) Size</strong> depends on…</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>LoRaWAN Data Rate</strong> (like Data Rate 2 or 3)</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>LoRaWAN Region</strong> (AS923 for Asia, AU915 for Australia / Brazil / New Zealand, EU868 for Europe, US915 for US, …)</p>
|
||
<p><a href="https://www.thethingsnetwork.org/docs/lorawan/frequencies-by-country.html">(See this)</a></p>
|
||
</li>
|
||
</ul>
|
||
<p>Our LoRaWAN Test App uses <strong>Data Rate 3</strong>: <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L58-L70">lorawan_test_main.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>// LoRaWAN Adaptive Data Rate
|
||
// Please note that when ADR is enabled the end-device should be static
|
||
#define LORAWAN_ADR_STATE LORAMAC_HANDLER_ADR_OFF
|
||
|
||
// Default Data Rate
|
||
// Please note that LORAWAN_DEFAULT_DATARATE is used only when ADR is disabled
|
||
#define LORAWAN_DEFAULT_DATARATE DR_3</code></pre></div>
|
||
<p>But there’s a catch: The <strong>First Message Transmitted</strong> (after joining LoRaWAN) will have <strong>Data Rate 2</strong> (instead of Data Rate 3)!</p>
|
||
<p>(We’ll see this in the upcoming demo)</p>
|
||
<p>For Data Rates 2 and 3, the <strong>Maximum Message (Payload) Sizes</strong> are…</p>
|
||
<div><table><thead><tr><th style="text-align: center">Region</th><th style="text-align: center">Data Rate</th><th style="text-align: center">Max Payload Size</th></tr></thead><tbody>
|
||
<tr><td style="text-align: center"><strong>AS923</strong></td><td style="text-align: center">DR 2 <br> DR 3</td><td style="text-align: center">11 bytes <br> 53 bytes</td></tr>
|
||
<tr><td style="text-align: center"><strong>AU915</strong></td><td style="text-align: center">DR 2 <br> DR 3</td><td style="text-align: center">11 bytes <br> 53 bytes</td></tr>
|
||
<tr><td style="text-align: center"><strong>EU868</strong></td><td style="text-align: center">DR 2 <br> DR 3</td><td style="text-align: center">51 bytes <br> 115 bytes</td></tr>
|
||
<tr><td style="text-align: center"><strong>US915</strong></td><td style="text-align: center">DR 2 <br> DR 3</td><td style="text-align: center">125 bytes <br> 222 bytes</td></tr>
|
||
</tbody></table>
|
||
</div>
|
||
<p><a href="https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/">(Based on LoRaWAN Regional Parameters)</a></p>
|
||
<p>Our LoRaWAN Test App sends a Message Payload of <strong>9 bytes</strong>, so it should work fine for Data Rates 2 and 3 across all LoRaWAN Regions.</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-tx5a.png" alt="Setting LoRaWAN Data Rate to 3" /></p>
|
||
<h2 id="message-interval"><a class="doc-anchor" href="#message-interval">§</a>9.2 Message Interval</h2>
|
||
<p><em>How often can we send data to the LoRaWAN Network?</em></p>
|
||
<p>We must comply with Local Wireless Regulations for <a href="https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/"><strong>Duty Cycle</strong></a>. Blasting messages non-stop is no-no!</p>
|
||
<p>To figure out how often we can send data, check out the…</p>
|
||
<ul>
|
||
<li><a href="https://avbentem.github.io/airtime-calculator/ttn/us915"><strong>“LoRaWAN Airtime Calculator”</strong></a></li>
|
||
</ul>
|
||
<p>For <strong>AS923 (Asia) at Data Rate 3</strong>, the LoRaWAN Airtime Calculator says that we can send a message every <strong>20.6 seconds</strong> (assuming Message Payload is <strong>9 bytes</strong>)…</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-airtime.jpg" alt="LoRaWAN Airtime Calculator" /></p>
|
||
<p><a href="https://avbentem.github.io/airtime-calculator/ttn/as923/9">(Source)</a></p>
|
||
<p>Let’s round up the Message Interval to <strong>40 seconds</strong> for demo.</p>
|
||
<p>We configure this Message Interval as <strong>APP_TX_DUTYCYCLE</strong> in <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L47-L56">lorawan_test_main.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>// Defines the application data transmission duty cycle.
|
||
// 40s, value in [ms].
|
||
#define APP_TX_DUTYCYCLE 40000
|
||
|
||
// Defines a random delay for application data transmission duty cycle.
|
||
// 5s, value in [ms].
|
||
#define APP_TX_DUTYCYCLE_RND 5000</code></pre></div>
|
||
<p><strong>APP_TX_DUTYCYCLE</strong> is used to compute the Timeout Interval of our <strong>Transmit Timer</strong>: <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L260-L303">lorawan_test_main.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>// Compute the interval between transmissions based on Duty Cycle
|
||
TxPeriodicity = APP_TX_DUTYCYCLE +
|
||
randr( -APP_TX_DUTYCYCLE_RND, APP_TX_DUTYCYCLE_RND );</code></pre></div>
|
||
<p><a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/boards/mcu/utilities.c#L48-L51">(<strong>randr</strong> is defined here)</a></p>
|
||
<p>Thus our LoRaWAN Test App transmits a message every <strong>40 seconds</strong>.</p>
|
||
<p>(±5 seconds of random delay)</p>
|
||
<h1 id="rerun-the-firmware"><a class="doc-anchor" href="#rerun-the-firmware">§</a>10 Rerun The Firmware</h1>
|
||
<p>Watch what happens when our LoRaWAN Test App <strong>transmits a Data Packet</strong>…</p>
|
||
<ol>
|
||
<li>
|
||
<p>In the NuttX Shell, run our <strong>LoRaWAN Test App</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>lorawan_test</code></pre></div></li>
|
||
<li>
|
||
<p>As seen earlier, our app transmits a <strong>Join Network Request</strong> and receives a <strong>Join Accept Response</strong> from the LoRaWAN Gateway…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>### =========== MLME-Confirm ============ ##
|
||
STATUS : OK
|
||
### =========== JOINED ============ ##
|
||
OTAA
|
||
DevAddr : 01DA9790
|
||
DATA RATE : DR_2</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/83be5da091273bb39bad6e77cc91b68d">(See the Output Log)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Upon joining the LoRaWAN Network, our app <strong>transmits a Data Packet</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>PrepareTxFrame: Transmit to LoRaWAN: Hi NuttX (9 bytes)
|
||
PrepareTxFrame: status=0, maxSize=11, currentSize=11
|
||
### =========== MCPS-Request ============ ##
|
||
### MCPS_UNCONFIRMED ##
|
||
### ===================================== ##
|
||
STATUS : OK
|
||
PrepareTxFrame: Transmit OK</code></pre></div>
|
||
<p>Note that the <strong>First Data Packet</strong> is assumed to have <strong>Data Rate 2</strong>, which allows Maximum Message Size <strong>11 bytes</strong> (for AS923).</p>
|
||
</li>
|
||
<li>
|
||
<p>After transmitting the First Data Packet, our LoRaWAN Library automagically upgrades the <strong>Data Rate to 3</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>### =========== MCPS-Confirm ============ ##
|
||
STATUS : OK
|
||
### ===== UPLINK FRAME 1 ===== ##
|
||
CLASS : A
|
||
TX PORT : 1
|
||
TX DATA : UNCONFIRMED
|
||
48 69 20 4E 75 74 74 58 00
|
||
DATA RATE : DR_3
|
||
U/L FREQ : 923400000
|
||
TX POWER : 0
|
||
CHANNEL MASK: 0003</code></pre></div></li>
|
||
<li>
|
||
<p>While transmitting the Second (and subsequent) Data Packet, the Maximum Message Size is extended to <strong>53 bytes</strong> (because of the increased Data Rate)…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>PrepareTxFrame: Transmit to LoRaWAN: Hi NuttX (9 bytes)
|
||
PrepareTxFrame: status=0, maxSize=53, currentSize=53
|
||
### =========== MCPS-Request ============ ##
|
||
### MCPS_UNCONFIRMED ##
|
||
### ===================================== ##
|
||
STATUS : OK
|
||
PrepareTxFrame: Transmit OK
|
||
...
|
||
|
||
### =========== MCPS-Confirm ============ ##
|
||
STATUS : OK
|
||
### ===== UPLINK FRAME 1 ===== ##
|
||
CLASS : A
|
||
TX PORT : 1
|
||
TX DATA : UNCONFIRMED
|
||
48 69 20 4E 75 74 74 58 00
|
||
DATA RATE : DR_3
|
||
U/L FREQ : 923400000
|
||
TX POWER : 0
|
||
CHANNEL MASK: 0003</code></pre></div></li>
|
||
<li>
|
||
<p>This repeats roughly every <strong>40 seconds</strong>.</p>
|
||
<p>Let’s check the logs in our LoRaWAN Gateway.</p>
|
||
</li>
|
||
</ol>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-tx7a.jpg" alt="Data Rate changes from 2 to 3" /></p>
|
||
<h2 id="check-lorawan-gateway-1"><a class="doc-anchor" href="#check-lorawan-gateway-1">§</a>10.1 Check LoRaWAN Gateway</h2>
|
||
<p>To inspect the Data Packet on our <strong>LoRaWAN Gateway</strong> (ChirpStack), click…</p>
|
||
<p><strong>Applications</strong> → <strong>app</strong> → <strong>device_otaa_class_a</strong> → <strong>LoRaWAN Frames</strong></p>
|
||
<p>And look for <strong>“Unconfirmed Data Up”</strong>…</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack8.png" alt="Send Data" /></p>
|
||
<p>To see the <strong>Decoded Payload</strong> of our Data Packet, click…</p>
|
||
<p><strong>Applications</strong> → <strong>app</strong> → <strong>device_otaa_class_a</strong> → <strong>Device Data</strong></p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack6.png" alt="Decoded Payload" /></p>
|
||
<p>If we see <strong>“Hi NuttX”</strong>… Congratulations our LoRaWAN Test App has successfully transmitted a Data Packet to LoRaWAN!</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-flow2.jpg" alt="Join LoRaWAN Network" /></p>
|
||
<h1 id="lorawan-nonce"><a class="doc-anchor" href="#lorawan-nonce">§</a>11 LoRaWAN Nonce</h1>
|
||
<p><em>Why did we configure NuttX to provide a Strong Random Number Generator with Entropy Pool?</em></p>
|
||
<p>The Strong Random Number Generator fixes a <strong>Nonce Quirk</strong> in our LoRaWAN Library that we observed during development…</p>
|
||
<ul>
|
||
<li>
|
||
<p>Remember that our LoRaWAN Library <strong>sends a Nonce</strong> to the LoRaWAN Gateway every time it starts. (Pic above)</p>
|
||
</li>
|
||
<li>
|
||
<p>What’s a Nonce? It’s a <strong>Non-Repeating Number</strong> that prevents <a href="https://en.wikipedia.org/wiki/Replay_attack"><strong>Replay Attacks</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>By default our LoRaWAN Library <strong>initialises the Nonce to 1</strong> and increments by 1 for every Join Network Request: 1, 2, 3, 4, …</p>
|
||
</li>
|
||
</ul>
|
||
<p>Now suppose the LoRaWAN Library <strong>crashes our device</strong> due to a bug. Watch what happens…</p>
|
||
<div><table><thead><tr><th style="text-align: left"><em>Our Device</em></th><th style="text-align: left"><em>LoRaWAN Gateway</em></th></tr></thead><tbody>
|
||
<tr><td style="text-align: left">Here is <strong>Nonce 1</strong></td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left"></td><td style="text-align: left">OK I accept <strong>Nonce 1</strong></td></tr>
|
||
<tr><td style="text-align: left">(Device crashes and restarts)</td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left">Here is <strong>Nonce 1</strong></td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left"></td><td style="text-align: left">(Silently rejects <strong>Nonce 1</strong> because it’s repeated)</td></tr>
|
||
<tr><td style="text-align: left">(Timeout waiting for response)</td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left">Here is <strong>Nonce 2</strong></td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left"></td><td style="text-align: left">OK I accept <strong>Nonce 2</strong></td></tr>
|
||
<tr><td style="text-align: left">(Device crashes and restarts)</td><td style="text-align: left"></td></tr>
|
||
</tbody></table>
|
||
</div>
|
||
<p>If our device keeps crashing, the LoRaWAN Gateway will eventually <strong>reject a whole bunch of Nonces</strong>: 1, 2, 3, 4, …</p>
|
||
<p>(Which makes development super slow and frustrating)</p>
|
||
<p>Thus we generate LoRaWAN Nonces with a <strong>Strong Random Number Generator</strong> instead.</p>
|
||
<p>(Random Numbers that won’t repeat upon restarting)</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack2a.jpg" alt="Repeated Nonces are rejected by LoRaWAN Gateway" /></p>
|
||
<h2 id="strong-random-number-generator"><a class="doc-anchor" href="#strong-random-number-generator">§</a>11.1 Strong Random Number Generator</h2>
|
||
<p>Our LoRaWAN Library supports <strong>Random Nonces</strong>… Assuming that we have a <strong>Secure Element</strong>.</p>
|
||
<p>Since we don’t have a Secure Element, let’s <strong>generate the Random Nonce in software</strong>: <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/nuttx.c#L140-L152">nuttx.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>/// Get random devnonce from the Random Number Generator
|
||
SecureElementStatus_t SecureElementRandomNumber( uint32_t* randomNum ) {
|
||
// Open the Random Number Generator /dev/urandom
|
||
int fd = open("/dev/urandom", O_RDONLY);
|
||
assert(fd > 0);
|
||
|
||
// Read the random number
|
||
read(fd, randomNum, sizeof(uint32_t));
|
||
close(fd);
|
||
|
||
printf("SecureElementRandomNumber: 0x%08lx\n", *randomNum);
|
||
return SECURE_ELEMENT_SUCCESS;
|
||
}</code></pre></div>
|
||
<p>The above code is called by our LoRaWAN Library when preparing a <strong>Join Network Request</strong>: <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/mac/LoRaMacCrypto.c#L980-L996">LoRaMacCrypto.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>// Prepare a Join Network Request
|
||
LoRaMacCryptoStatus_t LoRaMacCryptoPrepareJoinRequest( LoRaMacMessageJoinRequest_t* macMsg ) {
|
||
|
||
#if ( USE_RANDOM_DEV_NONCE == 1 )
|
||
// Get Nonce from Random Number Generator
|
||
uint32_t devNonce = 0;
|
||
SecureElementRandomNumber( &devNonce );
|
||
CryptoNvm->DevNonce = devNonce;
|
||
#else
|
||
// Init Nonce to 1
|
||
CryptoNvm->DevNonce++;
|
||
#endif</code></pre></div>
|
||
<p>To enable Random Nonces, we define <strong>USE_RANDOM_DEV_NONCE</strong> as 1 in <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/mac/LoRaMacCrypto.h#L58-L65">LoRaMacCrypto.h</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>// Indicates if a random devnonce must be used or not
|
||
#ifdef __NuttX__
|
||
// For NuttX: Get random devnonce from the Random Number Generator
|
||
#define USE_RANDOM_DEV_NONCE 1
|
||
#else
|
||
#define USE_RANDOM_DEV_NONCE 0
|
||
#endif // __NuttX__</code></pre></div>
|
||
<p>And that’s how we generate Random Nonces whenever we restart our device! (Pic below)</p>
|
||
<p><em>What happens if we don’t select Entropy Pool for our Random Number Generator?</em></p>
|
||
<p>Our Random Number Generator becomes “Weak”… It <strong>repeats the same Random Numbers</strong> upon restarting.</p>
|
||
<p>Thus we <strong>always select Entropy Pool</strong> for our Random Number Generator…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/lorawan3#appendix-random-number-generator-with-entropy-pool"><strong>“Random Number Generator with Entropy Pool”</strong></a></li>
|
||
</ul>
|
||
<p><strong>UPDATE:</strong> While running Auto Flash and Test with NuttX, we discovered that the Random Number Generator with Entropy Pool might <strong>generate the same Random Numbers</strong>. (Because the booting of NuttX becomes so predictable)</p>
|
||
<p>To fix this, we add <strong>Internal Temperature Sensor Data</strong> to the Entropy Pool, to generate truly random numbers…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/auto#appendix-fix-lorawan-nonce"><strong>“Fix LoRaWAN Nonce”</strong></a></li>
|
||
</ul>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-nonce7a.jpg" alt="Our LoRaWAN Library now generates random nonces" /></p>
|
||
<h1 id="lorawan-event-loop"><a class="doc-anchor" href="#lorawan-event-loop">§</a>12 LoRaWAN Event Loop</h1>
|
||
<p>Let’s look inside our LoRaWAN Test App and learn how the <strong>Event Loop</strong> handles LoRa and LoRaWAN Events by calling NimBLE Porting Layer.</p>
|
||
<p><em>What is NimBLE Porting Layer?</em></p>
|
||
<p><strong>NimBLE Porting Layer</strong> is a multithreading library that works on several operating systems…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/sx1262#multithreading-with-nimble-porting-layer"><strong>“Multithreading with NimBLE Porting Layer”</strong></a></li>
|
||
</ul>
|
||
<p>It provides <strong>Timers and Event Queues</strong> that are used by the LoRa and LoRaWAN Libraries.</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-run5a.png" alt="Timers and Event Queues" /></p>
|
||
<p><em>What’s inside our Event Loop?</em></p>
|
||
<p>Our <strong>Event Loop</strong> forever reads LoRa and LoRaWAN Events from an <strong>Event Queue</strong> and handles them.</p>
|
||
<p>The Event Queue is created in our LoRa SX1262 Library as explained here…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/sx1262#event-queue"><strong>“Event Queue”</strong></a></li>
|
||
</ul>
|
||
<p>The Main Function of our LoRaWAN Test App calls this function to run the <strong>Event Loop</strong>: <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L611-L655">lorawan_test_main.c</a></p>
|
||
<div class="example-wrap"><pre class="language-c"><code>/// Event Loop that dequeues Events from the Event Queue and processes the Events
|
||
static void handle_event_queue(void *arg) {
|
||
|
||
// Loop forever handling Events from the Event Queue
|
||
for (;;) {
|
||
|
||
// Get the next Event from the Event Queue
|
||
struct ble_npl_event *ev = ble_npl_eventq_get(
|
||
&event_queue, // Event Queue
|
||
BLE_NPL_TIME_FOREVER // No Timeout (Wait forever for event)
|
||
);</code></pre></div>
|
||
<p>This code runs in the <strong>Foreground Thread</strong> of our NuttX App.</p>
|
||
<p>Here we loop forever, <strong>waiting for Events</strong> from the Event Queue.</p>
|
||
<p>When we receive an Event, we <strong>remove the Event</strong> from the Event Queue…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // If no Event due to timeout, wait for next Event.
|
||
// Should never happen since we wait forever for an Event.
|
||
if (ev == NULL) { printf("."); continue; }
|
||
|
||
// Remove the Event from the Event Queue
|
||
ble_npl_eventq_remove(&event_queue, ev);</code></pre></div>
|
||
<p>We call the <strong>Event Handler Function</strong> that was registered with the Event…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // Trigger the Event Handler Function
|
||
ble_npl_event_run(ev);</code></pre></div>
|
||
<ul>
|
||
<li>
|
||
<p>For SX1262 Interrupts: We call <a href="https://lupyuen.github.io/articles/sx1262#radioondioirq"><strong>RadioOnDioIrq</strong></a> to handle the packet transmitted / received notification</p>
|
||
</li>
|
||
<li>
|
||
<p>For Timer Events: We call the <strong>Timeout Function</strong> defined in the Timer</p>
|
||
</li>
|
||
</ul>
|
||
<p>The rest of the Event Loop handles <strong>LoRaWAN Events</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // For LoRaWAN: Process the LoRaMAC events
|
||
LmHandlerProcess( );</code></pre></div>
|
||
<p><strong>LmHandlerProcess</strong> handles <strong>Join Network Events</strong> in the LoRaMAC Layer of our LoRaWAN Library.</p>
|
||
<p>If we have joined the LoRaWAN Network, we <strong>transmit data</strong> to the network…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // For LoRaWAN: If we have joined the network, do the uplink
|
||
if (!LmHandlerIsBusy( )) {
|
||
UplinkProcess( );
|
||
}</code></pre></div>
|
||
<p>(<a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L361-L373"><strong>UplinkProcess</strong></a> calls <a href="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L305-L337"><strong>PrepareTxFrame</strong></a>, which we have seen earlier)</p>
|
||
<p>The last part of the Event Loop will handle Low Power Mode in future…</p>
|
||
<div class="example-wrap"><pre class="language-c"><code> // For LoRaWAN: Handle Low Power Mode
|
||
CRITICAL_SECTION_BEGIN( );
|
||
if( IsMacProcessPending == 1 ) {
|
||
// Clear flag and prevent MCU to go into low power modes.
|
||
IsMacProcessPending = 0;
|
||
} else {
|
||
// The MCU wakes up through events
|
||
// TODO: BoardLowPowerHandler( );
|
||
}
|
||
CRITICAL_SECTION_END( );
|
||
}
|
||
}</code></pre></div>
|
||
<p>And we loop back perpetually, waiting for Events and handling them.</p>
|
||
<p>That’s how we handle LoRa and LoRaWAN Events with NimBLE Porting Layer!</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-npl1.png" alt="Handling LoRaWAN Events with NimBLE Porting Layer" /></p>
|
||
<h1 id="troubleshoot-lorawan"><a class="doc-anchor" href="#troubleshoot-lorawan">§</a>13 Troubleshoot LoRaWAN</h1>
|
||
<p><em>The Join Network Request / Join Accept Response / Data Packet doesn’t appear in the LoRaWAN Gateway…</em></p>
|
||
<p><em>What can we check?</em></p>
|
||
<ol>
|
||
<li>
|
||
<p>In the output of our LoRaWAN Test App, verify the <strong>Sync Word</strong> (must be 3444), <strong>Device EUI</strong> (MSB First), <strong>Join EUI</strong> (MSB First) and <strong>LoRa Frequency</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>RadioSetPublicNetwork: public syncword=3444
|
||
DevEui : 4B-C1-5E-E7-37-7B-B1-5B
|
||
JoinEui : 00-00-00-00-00-00-00-00
|
||
RadioSetChannel: freq=923400000</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/83be5da091273bb39bad6e77cc91b68d">(See the Output Log)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-run2a.png" alt="LoRa Frequency, Sync Word, Device EUI and Join EUI" /></p>
|
||
</li>
|
||
<li>
|
||
<p>Verify the <strong>App Key</strong> (MSB First) in <a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/peripherals/soft-se/se-identity.h#L65-L79"><strong>se-identity.h</strong></a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#device-eui-join-eui-and-app-key"><strong>“Device EUI, Join EUI and App Key”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>On our LoRaWAN Gateway, scan the log for <strong>Message Integrity Code</strong> errors (“invalid MIC”)…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>grep MIC /var/log/syslog
|
||
|
||
chirpstack-application-server[568]:
|
||
level=error
|
||
msg="invalid MIC"
|
||
dev_eui=4bc15ee7377bb15b
|
||
type=DATA_UP_MIC</code></pre></div>
|
||
<p>This is usually caused by incorrect Device EUI, Join EUI or App Key.</p>
|
||
<p><a href="https://lupyuen.github.io/articles/wisgate#message-integrity-code">(More about Message Integrity Code)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>On our LoRaWAN Gateway, scan the log for <strong>Nonce Errors</strong> (“validate dev-nonce error”)…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>grep nonce /var/log/syslog
|
||
|
||
chirpstack-application-server[5667]:
|
||
level=error
|
||
msg="validate dev-nonce error"
|
||
dev_eui=4bc15ee7377bb15b
|
||
type=OTAA
|
||
|
||
chirpstack-network-server[5749]:
|
||
time="2021-12-26T06:12:48Z"
|
||
level=error
|
||
msg="uplink: processing uplink frame error"
|
||
ctx_id=bb756ec1-9ee3-4903-a13d-656356d98fd5
|
||
error="validate dev-nonce error: object already exists"</code></pre></div>
|
||
<p>This means that a <strong>Duplicate Nonce</strong> has been detected.</p>
|
||
<p>Check that we’re using a Strong Random Number Generator with Entropy Pool…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#appendix-random-number-generator-with-entropy-pool"><strong>“Random Number Generator with Entropy Pool”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Another way to check for <strong>Duplicate Nonce</strong>: Click…</p>
|
||
<p><strong>Applications</strong> → <strong>app</strong> → <strong>device_otaa_class_a</strong> → <strong>Device Data</strong></p>
|
||
<p>Look for <strong>“validate dev-nonce error”</strong>…</p>
|
||
<p><img src="https://lupyuen.github.io/images/auto-nonce.png" alt="Duplicate LoRaWAN Nonce" /></p>
|
||
</li>
|
||
<li>
|
||
<p>Disable all <strong>Info Logging</strong> on NuttX</p>
|
||
<p>(See <strong>“LoRaWAN is Time Sensitive”</strong> below)</p>
|
||
</li>
|
||
<li>
|
||
<p>Verify the <strong>Message Size</strong> for the Data Rate</p>
|
||
<p>(See <strong>“Empty LoRaWAN Message”</strong> below)</p>
|
||
</li>
|
||
<li>
|
||
<p>If we <strong>fail to join</strong> the LoRaWAN Network, see these tips…</p>
|
||
<p><a href="https://gist.github.com/lupyuen/c03870b103f51649dcf608ffb1bc9e6b"><strong>“Troubleshoot LoRaWAN on NuttX”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>More troubleshooting tips…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/wisgate#troubleshoot-lorawan"><strong>“Troubleshoot LoRaWAN”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<h2 id="lorawan-is-time-sensitive"><a class="doc-anchor" href="#lorawan-is-time-sensitive">§</a>13.1 LoRaWAN is Time Sensitive</h2>
|
||
<p><strong>Warning:</strong> LoRaWAN is Time Sensitive!</p>
|
||
<p>Our LoRaWAN Library needs to <strong>handle Events in a timely manner</strong>… Or the protocol fails.</p>
|
||
<p>This is the normal flow for the <strong>Join Network Request</strong>…</p>
|
||
<div><table><thead><tr><th style="text-align: left"><em>Our Device</em></th><th style="text-align: left"><em>LoRaWAN Gateway</em></th></tr></thead><tbody>
|
||
<tr><td style="text-align: left">Join Network Request →</td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left">Transmit OK Interrupt</td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left">Switch to Receive Mode</td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left"></td><td style="text-align: left">← Join Accept Response</td></tr>
|
||
<tr><td style="text-align: left">Handle Join Response</td><td style="text-align: left"></td></tr>
|
||
</tbody></table>
|
||
</div>
|
||
<p>Watch what happens if <strong>our device gets too busy</strong>…</p>
|
||
<div><table><thead><tr><th style="text-align: left"><em>Our Device</em></th><th style="text-align: left"><em>LoRaWAN Gateway</em></th></tr></thead><tbody>
|
||
<tr><td style="text-align: left">Join Network Request →</td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left">Transmit OK Interrupt</td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left"><strong>(Busy Busy)</strong></td><td style="text-align: left">← Join Accept Response</td></tr>
|
||
<tr><td style="text-align: left"><strong>Switch to Receive Mode</strong></td><td style="text-align: left"></td></tr>
|
||
<tr><td style="text-align: left"><strong>Join Response missing!</strong></td><td style="text-align: left"></td></tr>
|
||
</tbody></table>
|
||
</div>
|
||
<p>This might happen if our device is busy <strong>writing debug logs</strong> to the console.</p>
|
||
<p><a href="https://gist.github.com/lupyuen/1d96b24c6bf5164cba652d903eedb9d1">(LoRaWAN Gateway returns the Join Accept Response in a One-Second Window)</a></p>
|
||
<p>Thus we should <strong>disable Info Logging</strong> on NuttX…</p>
|
||
<ol>
|
||
<li>
|
||
<p>In <strong>menuconfig</strong>, select <strong>“Build Setup”</strong> → <strong>“Debug Options”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Uncheck</strong> the following…</p>
|
||
<ul>
|
||
<li><strong>Enable Info Debug Output</strong></li>
|
||
<li><strong>GPIO Info Output</strong></li>
|
||
<li><strong>SPI Info Output</strong></li>
|
||
</ul>
|
||
</li>
|
||
</ol>
|
||
<p>(It’s OK to enable Debug Assertions, Error Output and Warning Output)</p>
|
||
<p>Since LoRaWAN is Time Sensitive, we ought to <a href="https://lupyuen.github.io/articles/lorawan3#spi-with-dma"><strong>optimise SPI Data Transfers with DMA</strong></a>.</p>
|
||
<ul>
|
||
<li><a href="https://gist.github.com/lupyuen/1d96b24c6bf5164cba652d903eedb9d1"><strong>Why LoRaWAN is Time Critical</strong></a></li>
|
||
</ul>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-tx.png" alt="LoRaWAN is Time Sensitive" /></p>
|
||
<p><a href="https://gist.github.com/lupyuen/8f012856b9eb6b9a762160afd83df7f8">(Source)</a></p>
|
||
<h2 id="empty-lorawan-message"><a class="doc-anchor" href="#empty-lorawan-message">§</a>13.2 Empty LoRaWAN Message</h2>
|
||
<p><em>What happens when we send a message that’s too large?</em></p>
|
||
<p>Our LoRaWAN Library will transmit an <strong>Empty Message Payload!</strong></p>
|
||
<p>We’ll see this in the LoRaWAN Gateway…</p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-chirpstack5.png" alt="Empty Message Payload" /></p>
|
||
<p><a href="https://gist.github.com/lupyuen/0d301216bbf937147778bb57ab0ccf89">(Output Log)</a></p>
|
||
<p>In the output for our LoRaWAN Test App, look for <strong>“maxSize”</strong> to verify the <strong>Maximum Message Size</strong> for our Data Rate and LoRaWAN Region…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>PrepareTxFrame: Transmit to LoRaWAN: Hi NuttX (9 bytes)
|
||
PrepareTxFrame: status=0, maxSize=11, currentSize=11</code></pre></div>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#message-size">(More about Message Size)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-tx4a.png" alt="Checking message size" /></p>
|
||
<p><a href="https://gist.github.com/lupyuen/5fc07695a6c4bb48b5e4d10eb05ca9bf">(Source)</a></p>
|
||
<h1 id="spi-with-dma"><a class="doc-anchor" href="#spi-with-dma">§</a>14 SPI With DMA</h1>
|
||
<p>Today we have successfully tested the LoRaWAN Library on <a href="https://lupyuen.github.io/articles/pinedio2"><strong>PineDio Stack BL604 RISC-V Board</strong></a> (pic below) and its onboard Semtech SX1262 Transceiver.</p>
|
||
<p>The NuttX implementation of <strong>SPI on BL602 and BL604</strong> might need some enhancements…</p>
|
||
<ul>
|
||
<li>
|
||
<p>NuttX on BL602 / BL604 executes <strong>SPI Data Transfer with Polling</strong> (not DMA)</p>
|
||
<p><a href="https://github.com/lupyuen/nuttx/blob/lorawan/arch/risc-v/src/bl602/bl602_spi.c#L734-L803">(See this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>LoRaWAN is <strong>Time Sensitive</strong>, as explained earlier. SPI with Polling might cause <strong>incoming packets to be dropped</strong>.</p>
|
||
<p>(SPI with DMA is probably better for LoRaWAN)</p>
|
||
</li>
|
||
<li>
|
||
<p>We’re testing NuttX and LoRaWAN on <a href="https://lupyuen.github.io/articles/pinedio2"><strong>PineDio Stack BL604</strong></a>, which comes with an onboard <strong>ST7789 SPI Display</strong>.</p>
|
||
<p><strong>ST7789 works better with DMA</strong> when blasting pixels to the display.</p>
|
||
</li>
|
||
<li>
|
||
<p>We might have <strong>contention between ST7789 and SX1262</strong> if we do SPI with Polling</p>
|
||
<p>(How would we multitask LoRaWAN with Display Updates?)</p>
|
||
</li>
|
||
</ul>
|
||
<p>Hence we might need to <strong>implement SPI with DMA</strong> real soon on BL602 and BL604.</p>
|
||
<p>We could port the implementation of SPI DMA from <strong>BL602 IoT SDK</strong> to NuttX…</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/spi#lli_list_init-create-dma-linked-list"><strong>“Create DMA Linked List”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/spi#hal_spi_dma_trans-execute-spi-transfer-with-dma"><strong>“Execute SPI Transfer with DMA”</strong></a></p>
|
||
</li>
|
||
</ul>
|
||
<p><strong>UPDATE:</strong> SPI DMA is now supported on BL602 NuttX…</p>
|
||
<ul>
|
||
<li><a href="https://lupyuen.github.io/articles/spi2#appendix-spi-dma-on-bl602-nuttx"><strong>“SPI DMA on BL602 NuttX”</strong></a></li>
|
||
</ul>
|
||
<p><img src="https://lupyuen.github.io/images/spi2-pinedio1.jpg" alt="Inside PineDio Stack BL604" /></p>
|
||
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>15 What’s Next</h1>
|
||
<p>We’re ready to build a <strong>complete IoT Sensor Device</strong> with NuttX!</p>
|
||
<p>Now that LoRaWAN is up, we’ll carry on in the next few articles…</p>
|
||
<ul>
|
||
<li>
|
||
<p>Implement <a href="https://github.com/intel/tinycbor"><strong>CBOR on NuttX</strong></a> for compressing Sensor Data…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/cbor2"><strong>“Encode Sensor Data with CBOR on Apache NuttX OS”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Transmit the compressed Sensor Data to <a href="https://lupyuen.github.io/articles/ttn"><strong>The Things Network</strong></a> over LoRaWAN</p>
|
||
<p>(Pic below)</p>
|
||
</li>
|
||
<li>
|
||
<p>We’ll read BL602’s <a href="https://lupyuen.github.io/articles/tsen"><strong>Internal Temperature Sensor</strong></a> to get real Sensor Data…</p>
|
||
<p><a href="https://github.com/lupyuen/bl602_adc_test"><strong>“ADC and Internal Temperature Sensor Library”</strong></a></p>
|
||
</li>
|
||
</ul>
|
||
<p><em>We’re porting plenty of code to NuttX: LoRa, LoRaWAN and NimBLE Porting Layer. Do we expect any problems?</em></p>
|
||
<p>Yep we might have issues keeping our LoRaWAN Stack in sync with Semtech’s version. <a href="https://lupyuen.github.io/articles/lorawan3#notes">(But we shall minimise the changes)</a></p>
|
||
<p>We have ported the <a href="https://lupyuen.github.io/articles/nuttx#rust-on-nuttx"><strong>Rust Embedded HAL</strong></a> to NuttX. Here’s what we’ve done…</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/rust2"><strong>“Rust on Apache NuttX OS”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/rusti2c"><strong>“Rust talks I2C on Apache NuttX RTOS”</strong></a></p>
|
||
</li>
|
||
</ul>
|
||
<p>Many Thanks to my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsors</strong></a> for supporting my work! This article wouldn’t have been possible without your support.</p>
|
||
<ul>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/sponsor">Sponsor me a coffee</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://www.reddit.com/r/Lora/comments/ruu3jf/lorawan_on_apache_nuttx_os/">Discuss this article on Reddit</a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/book">Read “The RISC-V BL602 / BL604 Book”</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>
|
||
<p><em>Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…</em></p>
|
||
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/lorawan3.md"><code>lupyuen.github.io/src/lorawan3.md</code></a></p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-ttn.png" alt="NuttX transmits a CBOR Payload to The Things Network Over LoRaWAN" /></p>
|
||
<p><em>NuttX transmits a CBOR Payload to The Things Network Over LoRaWAN</em></p>
|
||
<h1 id="notes"><a class="doc-anchor" href="#notes">§</a>16 Notes</h1>
|
||
<ol>
|
||
<li>
|
||
<p>This article is the expanded version of <a href="https://twitter.com/MisterTechBlog/status/1473593455699841027">this Twitter Thread</a></p>
|
||
</li>
|
||
<li>
|
||
<p>We’re <strong>porting plenty of code</strong> to NuttX: LoRa, LoRaWAN and NimBLE Porting Layer. Do we expect any problems?</p>
|
||
<ul>
|
||
<li>
|
||
<p>If we implement LoRa and LoRaWAN as <strong>NuttX Drivers</strong>, we’ll have to scrub the code to comply with the <a href="https://nuttx.apache.org/docs/latest/contributing/coding_style.html"><strong>NuttX Coding Conventions</strong></a>.</p>
|
||
<p>This makes it <strong>harder to update</strong> the LoRaWAN Driver when there are changes in the LoRaWAN Spec. (Like for a new LoRaWAN Region)</p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan#appendix-lora-carrier-sensing">(Here’s an example)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Alternatively we may implement LoRa and LoRaWAN as <strong>External Libraries</strong>, similar to <a href="https://github.com/lupyuen/nuttx-apps/tree/master/wireless/bluetooth/nimble"><strong>NimBLE for NuttX</strong></a>.</p>
|
||
<p>(The <a href="https://github.com/lupyuen/nuttx-apps/blob/master/wireless/bluetooth/nimble/Makefile#L33"><strong>Makefile</strong></a> downloads the External Library during build)</p>
|
||
<p>But then we won’t get a proper NuttX Driver that exposes the ioctl() interface to NuttX Apps.</p>
|
||
</li>
|
||
</ul>
|
||
<p>Conundrum. Lemme know your thoughts!</p>
|
||
</li>
|
||
<li>
|
||
<p>How do other Embedded Operating Systems implement LoRaWAN?</p>
|
||
<ul>
|
||
<li>
|
||
<p><strong>Mynewt</strong> embeds a <a href="https://github.com/apache/mynewt-core/tree/master/net/lora/node"><strong>Partial Copy</strong></a> of Semtech’s LoRaWAN Stack into its source tree.</p>
|
||
</li>
|
||
<li>
|
||
<p><strong>Zephyr</strong> maintains a <a href="https://github.com/zephyrproject-rtos/loramac-node"><strong>Complete Fork</strong></a> of the entire LoRaWAN Repo by Semtech. Which gets embedded during the Zephyr build.</p>
|
||
</li>
|
||
</ul>
|
||
<p>We’re adopting the Zephyr approach to <strong>keep our LoRaWAN Stack in sync</strong> with Semtech’s.</p>
|
||
</li>
|
||
<li>
|
||
<p>We have already ported LoRaWAN to <strong>BL602 IoT SDK</strong> <a href="https://lupyuen.github.io/articles/lorawan">(see this)</a>, why are we porting again to NuttX?</p>
|
||
<p>Regrettably BL602 IoT SDK has been revamped (without warning) to the <strong>new “hosal” HAL</strong> <a href="https://twitter.com/MisterTechBlog/status/1456259223323508748">(see this)</a>, and the LoRaWAN Stack will <strong>no longer work</strong> on the revamped BL602 IoT SDK.</p>
|
||
<p>For easier maintenance, we shall <strong>code our BL602 and BL604 projects with Apache NuttX OS</strong> instead.</p>
|
||
<p>(Which won’t get revamped overnight!)</p>
|
||
</li>
|
||
<li>
|
||
<p>Will <strong>NuttX become the official OS</strong> for PineDio Stack BL604 when it goes on sale?</p>
|
||
<p>It might! But first let’s get LoRaWAN and ST7789 Display running together on PineDio Stack.</p>
|
||
</li>
|
||
<li>
|
||
<p>LoRaWAN on NuttX is a great way to <strong>test a new gadget</strong> like PineDio Stack BL604!</p>
|
||
<p>Today we have tested: SPI Bus, GPIO Input / Output / Interrupt, Multithreading, Timers and Message Queues!</p>
|
||
</li>
|
||
<li>
|
||
<p>Is there another solution for the <strong>Nonce Quirk?</strong></p>
|
||
<p>We could store the Last Used Nonce into <strong>Non-Volatile Memory</strong> to be sure that we don’t reuse the Nonce.</p>
|
||
<p><a href="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/nuttx.c#L68-L97">(See this)</a></p>
|
||
</li>
|
||
</ol>
|
||
<h1 id="appendix-posix-timers-and-message-queues"><a class="doc-anchor" href="#appendix-posix-timers-and-message-queues">§</a>17 Appendix: POSIX Timers and Message Queues</h1>
|
||
<p>NimBLE Porting Layer needs <strong>POSIX Timers and Message Queues</strong> (plus more) to work. Follow the steps below to enable the features in <strong>menuconfig</strong>…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Select <strong>“RTOS Features”</strong> → <strong>“Disable NuttX Interfaces”</strong></p>
|
||
<p>Uncheck <strong>“Disable POSIX Timers”</strong></p>
|
||
<p>Uncheck <strong>“Disable POSIX Message Queue Support”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Select <strong>“RTOS Features”</strong> → <strong>“Clocks and Timers”</strong></p>
|
||
<p>Check <strong>“Support CLOCK_MONOTONIC”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Select <strong>“RTOS Features”</strong> → <strong>“Work Queue Support”</strong></p>
|
||
<p>Check <strong>“High Priority (Kernel) Worker Thread”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Select <strong>“RTOS Features”</strong> → <strong>“Signal Configuration”</strong></p>
|
||
<p>Check <strong>“Support SIGEV_THHREAD”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
|
||
</li>
|
||
</ol>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-config1.jpg" alt="Enable POSIX Timers and Message Queues in menuconfig" /></p>
|
||
<h1 id="appendix-random-number-generator-with-entropy-pool"><a class="doc-anchor" href="#appendix-random-number-generator-with-entropy-pool">§</a>18 Appendix: Random Number Generator with Entropy Pool</h1>
|
||
<p>Our LoRaWAN Library generates Nonces by calling a <strong>Random Number Generator with Entropy Pool</strong>.</p>
|
||
<p>Follow these steps to enable the <strong>Entropy Pool</strong> in <strong>menuconfig</strong>…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Select <strong>“Crypto API”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Check <strong>“Crypto API Support”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Check <strong>“Entropy Pool and Strong Random Number Generator”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
|
||
</li>
|
||
</ol>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-nonce3a.png" alt="Enable Entropy Pool in menuconfig" /></p>
|
||
<p>Then we enable the <strong>Random Number Generator</strong>…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Select <strong>“Device Drivers”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Check <strong>“Enable /dev/urandom”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Select <strong>“/dev/urandom algorithm”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Check <strong>“Entropy Pool”</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Hit <strong>“Exit”</strong> until the Top Menu appears. (“NuttX/x64_64 Configuration”)</p>
|
||
</li>
|
||
</ol>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-nonce4a.jpg" alt="Select Entropy Pool in menuconfig" /></p>
|
||
<h1 id="appendix-build-flash-and-run-nuttx"><a class="doc-anchor" href="#appendix-build-flash-and-run-nuttx">§</a>19 Appendix: Build, Flash and Run NuttX</h1>
|
||
<p><em>(For BL602 and ESP32)</em></p>
|
||
<p>Below are the steps to build, flash and run NuttX on BL602 and ESP32.</p>
|
||
<p>The instructions below will work on <strong>Linux (Ubuntu)</strong>, <strong>WSL (Ubuntu)</strong> and <strong>macOS</strong>.</p>
|
||
<p><a href="https://nuttx.apache.org/docs/latest/quickstart/install.html">(Instructions for other platforms)</a></p>
|
||
<p><a href="https://popolon.org/gblog3/?p=1977&lang=en">(See this for Arch Linux)</a></p>
|
||
<h2 id="build-nuttx"><a class="doc-anchor" href="#build-nuttx">§</a>19.1 Build NuttX</h2>
|
||
<p>Follow these steps to build NuttX for BL602 or ESP32…</p>
|
||
<ol>
|
||
<li>
|
||
<p>Install the build prerequisites…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/nuttx#install-prerequisites"><strong>“Install Prerequisites”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>Assume that we have downloaded the <strong>NuttX Source Code</strong> and configured the <strong>LoRaWAN Settings</strong>…</p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#download-source-code"><strong>“Download Source Code”</strong></a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#device-eui-join-eui-and-app-key"><strong>“Device EUI, Join EUI and App Key”</strong></a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#lorawan-frequency"><strong>“LoRaWAN Frequency”</strong></a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/lorawan3#build-the-firmware"><strong>“Build the Firmware”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p>To build NuttX, enter this command…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>make</code></pre></div></li>
|
||
<li>
|
||
<p>We should see…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>LD: nuttx
|
||
CP: nuttx.hex
|
||
CP: nuttx.bin</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/8f725c278c25e209c1654469a2855746">(See the complete log for BL602)</a></p>
|
||
</li>
|
||
<li>
|
||
<p><strong>For WSL:</strong> Copy the <strong>NuttX Firmware</strong> to the <strong>c:\blflash</strong> directory in the Windows File System…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## /mnt/c/blflash refers to c:\blflash in Windows
|
||
mkdir /mnt/c/blflash
|
||
cp nuttx.bin /mnt/c/blflash</code></pre></div>
|
||
<p>For WSL we need to run <strong>blflash</strong> under plain old Windows CMD (not WSL) because it needs to access the COM port.</p>
|
||
</li>
|
||
<li>
|
||
<p>In case of problems, refer to the <strong>NuttX Docs</strong>…</p>
|
||
<p><a href="https://nuttx.apache.org/docs/latest/platforms/risc-v/bl602/index.html"><strong>“BL602 NuttX”</strong></a></p>
|
||
<p><a href="https://nuttx.apache.org/docs/latest/platforms/xtensa/esp32/index.html"><strong>“ESP32 NuttX”</strong></a></p>
|
||
<p><a href="https://nuttx.apache.org/docs/latest/quickstart/install.html"><strong>“Installing NuttX”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<blockquote>
|
||
<p><img src="https://lupyuen.github.io/images/nuttx-build2.png" alt="Building NuttX" /></p>
|
||
</blockquote>
|
||
<h2 id="flash-nuttx"><a class="doc-anchor" href="#flash-nuttx">§</a>19.2 Flash NuttX</h2>
|
||
<p><strong>For ESP32:</strong> <a href="https://nuttx.apache.org/docs/latest/platforms/xtensa/esp32/index.html#flashing"><strong>See instructions here</strong></a> <a href="https://popolon.org/gblog3/?p=1977&lang=en">(Also check out this article)</a></p>
|
||
<p><strong>For BL602:</strong> Follow these steps to install <strong>blflash</strong>…</p>
|
||
<ol>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/flash#install-rustup"><strong>“Install rustup”</strong></a></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://lupyuen.github.io/articles/flash#download-and-build-blflash"><strong>“Download and build blflash”</strong></a></p>
|
||
</li>
|
||
</ol>
|
||
<p>We assume that our Firmware Binary File <strong>nuttx.bin</strong> has been copied to the <strong>blflash</strong> folder.</p>
|
||
<p>Set BL602 / BL604 to <strong>Flashing Mode</strong> and restart the board…</p>
|
||
<p><strong>For PineDio Stack BL604:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Set the <strong>GPIO 8 Jumper</strong> to <strong>High</strong> <a href="https://lupyuen.github.io/images/pinedio-high.jpg">(Like this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Disconnect the USB cable and reconnect</p>
|
||
<p>Or use the Improvised Reset Button <a href="https://lupyuen.github.io/articles/pinedio#appendix-improvised-reset-button-for-pinedio-stack">(Here’s how)</a></p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For PineCone BL602:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Set the <strong>PineCone Jumper (IO 8)</strong> to the <strong><code>H</code> Position</strong> <a href="https://lupyuen.github.io/images/pinecone-jumperh.jpg">(Like this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Press the Reset Button</p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For BL10:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Connect BL10 to the USB port</p>
|
||
</li>
|
||
<li>
|
||
<p>Press and hold the <strong>D8 Button (GPIO 8)</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Press and release the <strong>EN Button (Reset)</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Release the D8 Button</p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For <a href="https://docs.ai-thinker.com/en/wb2">Ai-Thinker Ai-WB2</a>, Pinenut and MagicHome BL602:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Disconnect the board from the USB Port</p>
|
||
</li>
|
||
<li>
|
||
<p>Connect <strong>GPIO 8</strong> to <strong>3.3V</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Reconnect the board to the USB port</p>
|
||
</li>
|
||
</ol>
|
||
<p>Enter these commands to flash <strong>nuttx.bin</strong> to BL602 / BL604 over UART…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>## For Linux: Change "/dev/ttyUSB0" to the BL602 / BL604 Serial Port
|
||
blflash flash nuttx.bin \
|
||
--port /dev/ttyUSB0
|
||
|
||
## For macOS: Change "/dev/tty.usbserial-1410" to the BL602 / BL604 Serial Port
|
||
blflash flash nuttx.bin \
|
||
--port /dev/tty.usbserial-1410 \
|
||
--initial-baud-rate 230400 \
|
||
--baud-rate 230400
|
||
|
||
## For Windows: Change "COM5" to the BL602 / BL604 Serial Port
|
||
blflash flash c:\blflash\nuttx.bin --port COM5</code></pre></div>
|
||
<p><a href="https://gist.github.com/lupyuen/9c0dbd75bb6b8e810939a36ffb5c399f">(See the Output Log)</a></p>
|
||
<p>For WSL: Do this under plain old Windows CMD (not WSL) because <strong>blflash</strong> needs to access the COM port.</p>
|
||
<p><a href="https://github.com/apache/nuttx/issues/4336">(Flashing WiFi apps to BL602 / BL604? Remember to use <strong>bl_rfbin</strong>)</a></p>
|
||
<p><a href="https://lupyuen.github.io/articles/flash#flash-the-firmware">(More details on flashing firmware)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/nuttx-flash2.png" alt="Flashing NuttX" /></p>
|
||
<h2 id="run-nuttx"><a class="doc-anchor" href="#run-nuttx">§</a>19.3 Run NuttX</h2>
|
||
<p><strong>For ESP32:</strong> Use Picocom to connect to ESP32 over UART…</p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>picocom -b 115200 /dev/ttyUSB0</code></pre></div>
|
||
<p><a href="https://popolon.org/gblog3/?p=1977&lang=en">(More about this)</a></p>
|
||
<p><strong>For BL602:</strong> Set BL602 / BL604 to <strong>Normal Mode</strong> (Non-Flashing) and restart the board…</p>
|
||
<p><strong>For PineDio Stack BL604:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Set the <strong>GPIO 8 Jumper</strong> to <strong>Low</strong> <a href="https://lupyuen.github.io/images/pinedio-low.jpg">(Like this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Disconnect the USB cable and reconnect</p>
|
||
<p>Or use the Improvised Reset Button <a href="https://lupyuen.github.io/articles/pinedio#appendix-improvised-reset-button-for-pinedio-stack">(Here’s how)</a></p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For PineCone BL602:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Set the <strong>PineCone Jumper (IO 8)</strong> to the <strong><code>L</code> Position</strong> <a href="https://lupyuen.github.io/images/pinecone-jumperl.jpg">(Like this)</a></p>
|
||
</li>
|
||
<li>
|
||
<p>Press the Reset Button</p>
|
||
</li>
|
||
</ol>
|
||
<p><strong>For BL10:</strong></p>
|
||
<ol>
|
||
<li>Press and release the <strong>EN Button (Reset)</strong></li>
|
||
</ol>
|
||
<p><strong>For <a href="https://docs.ai-thinker.com/en/wb2">Ai-Thinker Ai-WB2</a>, Pinenut and MagicHome BL602:</strong></p>
|
||
<ol>
|
||
<li>
|
||
<p>Disconnect the board from the USB Port</p>
|
||
</li>
|
||
<li>
|
||
<p>Connect <strong>GPIO 8</strong> to <strong>GND</strong></p>
|
||
</li>
|
||
<li>
|
||
<p>Reconnect the board to the USB port</p>
|
||
</li>
|
||
</ol>
|
||
<p>After restarting, connect to BL602 / BL604’s UART Port at 2 Mbps like so…</p>
|
||
<p><strong>For Linux:</strong></p>
|
||
<div class="example-wrap"><pre class="language-bash"><code>screen /dev/ttyUSB0 2000000</code></pre></div>
|
||
<p><strong>For macOS:</strong> Use CoolTerm (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||
<p><strong>For Windows:</strong> Use <code>putty</code> (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||
<p><strong>Alternatively:</strong> Use the Web Serial Terminal (<a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
|
||
<p>Press Enter to reveal the <strong>NuttX Shell</strong>…</p>
|
||
<div class="example-wrap"><pre class="language-text"><code>NuttShell (NSH) NuttX-10.2.0-RC0
|
||
nsh></code></pre></div>
|
||
<p>Congratulations NuttX is now running on BL602 / BL604!</p>
|
||
<p><a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">(More details on connecting to BL602 / BL604)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/nuttx-boot2.png" alt="Running NuttX" /></p>
|
||
<p><strong>macOS Tip:</strong> Here’s the script I use to build, flash and run NuttX on macOS, all in a single step: <a href="https://gist.github.com/lupyuen/cc21385ecc66b5c02d15affd776a64af">run.sh</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/spi2-script.png" alt="Script to build, flash and run NuttX on macOS" /></p>
|
||
<p><a href="https://gist.github.com/lupyuen/cc21385ecc66b5c02d15affd776a64af">(Source)</a></p>
|
||
<p><img src="https://lupyuen.github.io/images/lorawan3-title2.jpg" alt="PineDio Stack BL604 RISC-V Board (left) talking LoRaWAN to RAKwireless WisGate LoRaWAN Gateway (right)" /></p>
|
||
|
||
|
||
<!-- Begin scripts/rustdoc-after.html: Post-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
|
||
|
||
<!-- Begin Theme Picker and Prism Theme -->
|
||
<script src="../theme.js"></script>
|
||
<script src="../prism.js"></script>
|
||
<!-- Theme Picker and Prism Theme -->
|
||
|
||
<!-- End scripts/rustdoc-after.html -->
|
||
|
||
|
||
</body>
|
||
</html> |