<p>Last article we got <strong>LoRa</strong> (the long-range, low-bandwidth wireless network) running on <ahref="https://lupyuen.github.io/articles/nuttx"><strong>Apache NuttX OS</strong></a>…</p>
<p>We shall test LoRaWAN on NuttX with Bouffalo Lab’s <ahref="https://lupyuen.github.io/articles/pinecone"><strong>BL602 and BL604 RISC-V SoCs</strong></a>.</p>
<p>(It will probably run on <strong>ESP32</strong>, since we’re calling standard NuttX Interfaces)</p>
<p><imgsrc="https://lupyuen.github.io/images/sx1262-library5.jpg"alt="Porting LoRaWAN to NuttX OS"/></p>
<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>
<p>LoRaWAN works <strong>slightly differently across the world regions</strong>, to comply with Local Wireless Regulations: Radio Frequency, Maximum Airtime (Duty Cycle), <ahref="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>Look for these lines in <ahref="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>Next find this in the same file <ahref="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>
<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 <ahref="https://encyclopedia.kaspersky.com/glossary/secure-element/"><strong>Secure Element</strong></a> hardware.</p>
<p>Our LoRaWAN Library supports two kinds of Secure Elements: <ahref="https://github.com/lupyuen/LoRaMac-node-nuttx/tree/master/src/peripherals/atecc608a-tnglora-se"><strong>Microchip ATECC608A</strong></a> and <ahref="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 <ahref="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>Do the same for the LoRaMAC Handler: <ahref="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/apps/LoRaMac/common/LmHandler/LmHandler.c#L41-L47"><strong>LmHandler.c</strong></a></p>
<p>Check that the <strong>Semtech SX1262 Pins</strong> are configured correctly in <ahref="https://github.com/lupyuen/incubator-nuttx/blob/lorawan/boards/risc-v/bl602/bl602evb/include/board.h#L36-L95"><strong>board.h</strong></a> or <ahref="https://github.com/lupyuen/incubator-nuttx/blob/lorawan/boards/xtensa/esp32/esp32-devkitc/src/esp32_gpio.c#L43-L67"><strong>esp32_gpio.c</strong></a>…</p>
<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>Enable <strong>POSIX Timers and Message Queues</strong> (for NimBLE Porting Layer)…</p>
<p><ahref="https://lupyuen.github.io/articles/lorawan3#appendix-posix-timers-and-message-queues"><strong>“POSIX Timers and Message Queues”</strong></a></p>
<p>Enable <strong>Random Number Generator with Entropy Pool</strong> (for LoRaWAN Nonces)…</p>
<p><ahref="https://lupyuen.github.io/articles/lorawan3#appendix-random-number-generator-with-entropy-pool"><strong>“Random Number Generator with Entropy Pool”</strong></a></p>
<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>
<p>Let’s dive into the code for our <strong>LoRaWAN Test App</strong>: <ahref="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L260-L303">lorawan_test_main.c</a></p>
<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>
<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>
<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: <ahref="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L305-L336">lorawan_test_main.c</a></p>
<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>
<tr><tdalign="center"><strong>AS923</strong></td><tdalign="center">DR 2 <br> DR 3</td><tdalign="center">11 bytes <br> 53 bytes</td></tr>
<tr><tdalign="center"><strong>AU915</strong></td><tdalign="center">DR 2 <br> DR 3</td><tdalign="center">11 bytes <br> 53 bytes</td></tr>
<tr><tdalign="center"><strong>EU868</strong></td><tdalign="center">DR 2 <br> DR 3</td><tdalign="center">51 bytes <br> 115 bytes</td></tr>
<tr><tdalign="center"><strong>US915</strong></td><tdalign="center">DR 2 <br> DR 3</td><tdalign="center">125 bytes <br> 222 bytes</td></tr>
</tbody></table>
</div>
<p><ahref="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><imgsrc="https://lupyuen.github.io/images/lorawan3-tx5a.png"alt="Setting LoRaWAN Data Rate to 3"/></p>
<p>We must comply with Local Wireless Regulations for <ahref="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>
<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>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 <ahref="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L47-L56">lorawan_test_main.c</a></p>
<p><strong>APP_TX_DUTYCYCLE</strong> is used to compute the Timeout Interval of our <strong>Transmit Timer</strong>: <ahref="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L260-L303">lorawan_test_main.c</a></p>
<p><ahref="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>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>
<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>
<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>
<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 <ahref="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>
<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>: <ahref="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/nuttx.c#L140-L152">nuttx.c</a></p>
<p>The above code is called by our LoRaWAN Library when preparing a <strong>Join Network Request</strong>: <ahref="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/mac/LoRaMacCrypto.c#L980-L996">LoRaMacCrypto.c</a></p>
<p>To enable Random Nonces, we define <strong>USE_RANDOM_DEV_NONCE</strong> as 1 in <ahref="https://github.com/lupyuen/LoRaMac-node-nuttx/blob/master/src/mac/LoRaMacCrypto.h#L58-L65">LoRaMacCrypto.h</a></p>
<divclass="example-wrap"><preclass="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
<li><ahref="https://lupyuen.github.io/articles/lorawan3#appendix-random-number-generator-with-entropy-pool"><strong>“Random Number Generator with Entropy Pool”</strong></a></li>
<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><ahref="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><imgsrc="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 LoRaWAN Test App as explained here…</p>
<p>The Main Function of our LoRaWAN Test App calls this function to run the <strong>Event Loop</strong>: <ahref="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L611-L655">lorawan_test_main.c</a></p>
<divclass="example-wrap"><preclass="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>
<divclass="example-wrap"><preclass="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; }
<p>For SX1262 Interrupts: We call <ahref="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>
<p>Our #NuttX App sent an empty #LoRaWAN Message because our message is too long for LoRaWAN Data Rate 2 (max 11 bytes) … Let’s increase the Data Rate to 3</p>
<p>Yep we might have issues keeping our LoRaWAN Stack in sync with Semtech’s version. <ahref="https://lupyuen.github.io/articles/lorawan3#notes">(But we shall minimise the changes)</a></p>
<p>Many Thanks to my <ahref="https://github.com/sponsors/lupyuen"><strong>GitHub Sponsors</strong></a> for supporting my work! This article wouldn’t have been possible without your support.</p>
<ul>
<li>
<p><ahref="https://github.com/sponsors/lupyuen">Sponsor me a coffee</a></p>
</li>
<li>
<p><ahref="https://lupyuen.github.io/articles/book">Read “The RISC-V BL602 / BL604 Book”</a></p>
</li>
<li>
<p><ahref="https://lupyuen.github.io">Check out my articles</a></p>
<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 <ahref="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><ahref="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 <ahref="https://github.com/lupyuen/incubator-nuttx-apps/tree/master/wireless/bluetooth/nimble"><strong>NimBLE for NuttX</strong></a>.</p>
<p>(The <ahref="https://github.com/lupyuen/incubator-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 <ahref="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 <ahref="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>The Zephyr approach is probably the best way 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><ahref="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><ahref="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 NuttX become the official OS for PineDio Stack BL604 when it goes on sale?</p>
<p>It might! But first let’s get LoRaWAN (and ST7789) running on PineDio Stack.</p>
<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>
<h1id="appendix-random-number-generator-with-entropy-pool"class="section-header"><ahref="#appendix-random-number-generator-with-entropy-pool">18 Appendix: Random Number Generator with Entropy Pool</a></h1>
<h1id="appendix-build-flash-and-run-nuttx"class="section-header"><ahref="#appendix-build-flash-and-run-nuttx">19 Appendix: Build, Flash and Run NuttX</a></h1>
<p><strong>For ESP32:</strong><ahref="https://nuttx.apache.org/docs/latest/platforms/xtensa/esp32/index.html#flashing"><strong>See instructions here</strong></a><ahref="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>
<p><ahref="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><ahref="https://lupyuen.github.io/images/pinedio-high.jpg">(Like this)</a></p>
</li>
<li>
<p>Press the Reset Button</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><ahref="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 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>
<divclass="example-wrap"><preclass="language-bash"><code># TODO: Change ~/blflash to the full path of blflash
cd ~/blflash
# For Linux:
sudo cargo run flash nuttx.bin \
--port /dev/ttyUSB0
# For macOS:
cargo run flash nuttx.bin \
--port /dev/tty.usbserial-1420 \
--initial-baud-rate 230400 \
--baud-rate 230400
# For Windows: Change COM5 to the BL602 / BL604 Serial Port
cargo run flash nuttx.bin --port COM5</code></pre></div>
<p><ahref="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><ahref="https://github.com/apache/incubator-nuttx/issues/4336">(Flashing WiFi apps to BL602 / BL604? Remember to use <strong>bl_rfbin</strong>)</a></p>
<p><ahref="https://lupyuen.github.io/articles/flash#flash-the-firmware">(More details on flashing firmware)</a></p>
<p><ahref="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><ahref="https://lupyuen.github.io/images/pinedio-low.jpg">(Like this)</a></p>
</li>
<li>
<p>Press the Reset Button</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><ahref="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 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 macOS:</strong> Use CoolTerm (<ahref="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
<p><strong>For Windows:</strong> Use <code>putty</code> (<ahref="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">See this</a>)</p>
<p><strong>Alternatively:</strong> Use the Web Serial Terminal (<ahref="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>
<p><strong>macOS Tip:</strong> Here’s the script I use to build, flash and run NuttX on macOS, all in a single step: <ahref="https://gist.github.com/lupyuen/cc21385ecc66b5c02d15affd776a64af">run.sh</a></p>
<p><imgsrc="https://lupyuen.github.io/images/spi2-script.png"alt="Script to build, flash and run NuttX on macOS"/></p>
<p>Switching a #NuttX GPIO Interrupt Pin to Trigger On Rising Edge … Crashes with an Assertion Failure … I’ll submit a NuttX Issue, meanwhile I have disabled the assertion</p>
<p>NimBLE Porting Layer doesn’t work for multiple Callout Timers on #NuttX OS, unless we loop the thread … Will submit a Pull Request to Apache NimBLE 👍</p>