<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>But if we’re building an <strong>IoT Sensor Device</strong> that will <strong>transmit data packets</strong> securely to a Local Area Network or to the internet, we need <strong>LoRaWAN</strong>.</p>
<p>We shall test LoRaWAN on NuttX with <ahref="https://lupyuen.github.io/articles/pinedio"><strong>PineDio Stack BL604 RISC-V Board</strong></a> (pic above) and its onboard Semtech SX1262 Transceiver.</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><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>
<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 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>(<ahref="https://github.com/lupyuen/lorawan_test/blob/main/lorawan_test_main.c#L361-L373"><strong>UplinkProcess</strong></a> calls <ahref="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>
<p><em>The Join Network Request / Join Accept Response / Data Packet doesn’t appear in the LoRaWAN Gateway…</em></p>
<p><em>What can we check?</em></p>
<ol>
<li>
<p>In the output of our LoRaWAN Test App, verify the <strong>Sync Word</strong> (must be 3444), <strong>Device EUI</strong> (MSB First), <strong>Join EUI</strong> (MSB First) and <strong>LoRa Frequency</strong>…</p>
<p><ahref="https://gist.github.com/lupyuen/83be5da091273bb39bad6e77cc91b68d">(See the Output Log)</a></p>
<p><imgsrc="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 <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><ahref="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…</p>
<divclass="example-wrap"><preclass="language-bash"><code>grep MIC /var/log/syslog
chirpstack-application-server[568]:
level=error
msg="invalid MIC"
dev_eui=4bc15ee7377bb15b
type=DATA_UP_MIC</code></pre></div>
<p>This is usually caused by incorrect Device EUI, Join EUI or App Key.</p>
<p>This means that a <strong>Duplicate Nonce</strong> has been detected.</p>
<p>Check that we’re using a Strong Random Number Generator with Entropy Pool…</p>
<p><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>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>
<divclass="example-wrap"><preclass="language-text"><code>PrepareTxFrame: Transmit to LoRaWAN: Hi NuttX (9 bytes)
<h1id="spi-with-dma"class="section-header"><ahref="#spi-with-dma">14 SPI With DMA</a></h1>
<p>Today we have successfully tested the LoRaWAN Library on <ahref="https://lupyuen.github.io/articles/pinedio"><strong>PineDio Stack BL604 RISC-V Board</strong></a> (pic below) and its onboard Semtech SX1262 Transceiver.</p>
<p>LoRaWAN is <strong>Time Sensitive</strong>, as explained earlier. SPI with Polling might cause <strong>incoming packets to be dropped</strong>.</p>
<p>(SPI with DMA is probably better for LoRaWAN)</p>
</li>
<li>
<p>We’re testing NuttX and LoRaWAN on <ahref="https://lupyuen.github.io/articles/pinedio"><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>
<p><ahref="https://lupyuen.github.io/articles/spi#hal_spi_dma_trans-execute-spi-transfer-with-dma"><strong>“Execute SPI Transfer with DMA”</strong></a></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>I’m still super curious about porting the <ahref="https://lupyuen.github.io/articles/nuttx#rust-on-nuttx"><strong>Rust Embedded HAL</strong></a> to NuttX. We might start soon with GPIO and SPI to see whether the concept is feasible.</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>
<p>This article is the expanded version of <ahref="https://twitter.com/MisterTechBlog/status/1473593455699841027">this Twitter Thread</a></p>
</li>
<li>
<p>We’re <strong>porting plenty of code</strong> to NuttX: LoRa, LoRaWAN and NimBLE Porting Layer. Do we expect any problems?</p>
<ul>
<li>
<p>If we implement LoRa and LoRaWAN as <strong>NuttX Drivers</strong>, we’ll have to scrub the code to comply with the <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>
<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>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>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>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>