lupyuen.org/articles/lorawan3.html

1436 lines
No EOL
87 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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="Whats Next">15 Whats 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 were 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 well 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>Thats a near-identical fork of <strong>Semtechs 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>Well 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 Semtechs 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>Semtechs 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 Semtechs <strong>Radio Interface</strong> thats 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>Well 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 thats assigned to our LoRaWAN Device</p>
<p><strong>Join EUI:</strong> Identifies the LoRaWAN Network that were 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 well 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>Lets 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>Whats “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>dont 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 doesnt have a Secure Element right?</em></p>
<p>Thats 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 its probably OK during development.</p>
<h1 id="lorawan-frequency"><a class="doc-anchor" href="#lorawan-frequency">§</a>5 LoRaWAN Frequency</h1>
<p>Lets 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 &quot;No active region defined, LORAMAC_REGION_AS923 will be used as default.&quot;
#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>Lets 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 &quot;esp32-devkitc&quot; 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 &quot;esp32-devkitc&quot; 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>(Well 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 &quot;esp32-devkitc&quot; 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>Were 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 well 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>Lets 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>Lets 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(
&amp;LmHandlerCallbacks, // Callback Functions
&amp;LmHandlerParams // LoRaWAN Parameters
) != LORAMAC_HANDLER_SUCCESS ) {
printf( &quot;LoRaMac wasn&#39;t properly initialized\n&quot; );
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, &amp;LmhpComplianceParams );
LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL );
LmHandlerPackageRegister( PACKAGE_ID_REMOTE_MCAST_SETUP, NULL );
LmHandlerPackageRegister( PACKAGE_ID_FRAGMENTATION, &amp;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 havent 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>(Well talk about the LoRaWAN Event Loop later)</p>
<p>Lets 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 thats 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 dont 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 weve joined the LoRaWAN Network, were 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&#39;t joined the LoRaWAN Network, try again later
if (LmHandlerIsBusy()) { puts(&quot;PrepareTxFrame: Busy&quot;); return; }</code></pre></div>
<p>If we havent joined a LoRaWAN Network yet, this function will return. (And well 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[] = &quot;Hi NuttX&quot;;
printf(&quot;PrepareTxFrame: Transmit to LoRaWAN: %s (%d bytes)\n&quot;, 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) &lt;= 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
&amp;txInfo // Returns max message size
);
printf(&quot;PrepareTxFrame: status=%d, maxSize=%d, currentSize=%d\n&quot;, status, txInfo.MaxPossibleApplicationDataSize, txInfo.CurrentPossiblePayloadSize);
assert(status == LORAMAC_STATUS_OK);</code></pre></div>
<p>(Whats the Maximum Message Size? Well 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(
&amp;appData, // Transmit Request
LmHandlerParams.IsTxConfirmed // 0 for Unconfirmed
);
assert(sendStatus == LORAMAC_HANDLER_SUCCESS);
puts(&quot;PrepareTxFrame: Transmit OK&quot;);
}</code></pre></div>
<p><em>Why is our Data Packet marked Unconfirmed?</em></p>
<p>Our Data Packet is marked Unconfirmed because we <strong>dont expect an acknowledgement</strong> from the LoRaWAN Gateway.</p>
<p>This is the typical mode for <strong>IoT Sensor Devices</strong>, which dont 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>Whats 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 theres 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>(Well 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>Lets 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>Lets 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>Whats a Nonce? Its 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 its 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 wont 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 dont have a Secure Element, lets <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(&quot;/dev/urandom&quot;, O_RDONLY);
assert(fd &gt; 0);
// Read the random number
read(fd, randomNum, sizeof(uint32_t));
close(fd);
printf(&quot;SecureElementRandomNumber: 0x%08lx\n&quot;, *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( &amp;devNonce );
CryptoNvm-&gt;DevNonce = devNonce;
#else
// Init Nonce to 1
CryptoNvm-&gt;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 thats how we generate Random Nonces whenever we restart our device! (Pic below)</p>
<p><em>What happens if we dont 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>Lets 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>Whats 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(
&amp;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(&quot;.&quot;); continue; }
// Remove the Event from the Event Queue
ble_npl_eventq_remove(&amp;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>Thats 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 doesnt 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=&quot;invalid MIC&quot;
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=&quot;validate dev-nonce error&quot;
dev_eui=4bc15ee7377bb15b
type=OTAA
chirpstack-network-server[5749]:
time=&quot;2021-12-26T06:12:48Z&quot;
level=error
msg=&quot;uplink: processing uplink frame error&quot;
ctx_id=bb756ec1-9ee3-4903-a13d-656356d98fd5
error=&quot;validate dev-nonce error: object already exists&quot;</code></pre></div>
<p>This means that a <strong>Duplicate Nonce</strong> has been detected.</p>
<p>Check that were 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>(Its 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 thats too large?</em></p>
<p>Our LoRaWAN Library will transmit an <strong>Empty Message Payload!</strong></p>
<p>Well 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>Were 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 Whats Next</h1>
<p>Were ready to build a <strong>complete IoT Sensor Device</strong> with NuttX!</p>
<p>Now that LoRaWAN is up, well 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>Well read BL602s <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>Were 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 Semtechs 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. Heres what weve 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 wouldnt 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>Were <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>, well 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">(Heres 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 wont 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 Semtechs 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>Were adopting the Zephyr approach to <strong>keep our LoRaWAN Stack in sync</strong> with Semtechs.</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 wont 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 lets 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 dont 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&amp;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&amp;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">(Heres 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 &quot;/dev/ttyUSB0&quot; to the BL602 / BL604 Serial Port
blflash flash nuttx.bin \
--port /dev/ttyUSB0
## For macOS: Change &quot;/dev/tty.usbserial-1410&quot; 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 &quot;COM5&quot; 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&amp;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">(Heres 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 / BL604s 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&gt;</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> Heres 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>