lupyuen.org/articles/cbor.html

1102 lines
No EOL
61 KiB
HTML
Raw 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>Encode Sensor Data with CBOR on BL602</title>
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title"
content="Encode Sensor Data with CBOR on BL602"
data-rh="true">
<meta property="og:description"
content="How we compress Sensor Data with CBOR... And transmit over LoRaWAN on the BL602 / BL604 RISC-V SoC"
data-rh="true">
<meta property="og:image"
content="https://lupyuen.github.io/images/cbor-title.jpg">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical" href="https://lupyuen.org/articles/cbor.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">Encode Sensor Data with CBOR on BL602</h1>
<nav id="rustdoc"><ul>
<li><a href="#encode-sensor-data-with-tinycbor" title="Encode Sensor Data with TinyCBOR">1 Encode Sensor Data with TinyCBOR</a><ul>
<li><a href="#output-buffer-and-cbor-encoder" title="Output Buffer and CBOR Encoder">1.1 Output Buffer and CBOR Encoder</a><ul></ul></li>
<li><a href="#create-map-encoder" title="Create Map Encoder">1.2 Create Map Encoder</a><ul></ul></li>
<li><a href="#encode-key-and-value" title="Encode Key and Value">1.3 Encode Key and Value</a><ul></ul></li>
<li><a href="#close-map-encoder" title="Close Map Encoder">1.4 Close Map Encoder</a><ul></ul></li>
<li><a href="#get-encoded-output" title="Get Encoded Output">1.5 Get Encoded Output</a><ul></ul></li>
<li><a href="#magic-happens" title="Magic Happens">1.6 Magic Happens</a><ul></ul></li></ul></li>
<li><a href="#add-another-field" title="Add Another Field">2 Add Another Field</a><ul>
<li><a href="#modify-map-encoder" title="Modify Map Encoder">2.1 Modify Map Encoder</a><ul></ul></li>
<li><a href="#encode-first-key-and-value" title="Encode First Key and Value">2.2 Encode First Key and Value</a><ul></ul></li>
<li><a href="#encode-second-key-and-value" title="Encode Second Key and Value">2.3 Encode Second Key and Value</a><ul></ul></li>
<li><a href="#watch-the-magic" title="Watch the Magic">2.4 Watch the Magic</a><ul></ul></li></ul></li>
<li><a href="#cbor-data-types" title="CBOR Data Types">3 CBOR Data Types</a><ul>
<li><a href="#numbers" title="Numbers">3.1 Numbers</a><ul></ul></li>
<li><a href="#strings" title="Strings">3.2 Strings</a><ul></ul></li>
<li><a href="#other-types" title="Other Types">3.3 Other Types</a><ul></ul></li></ul></li>
<li><a href="#floating-point-numbers" title="Floating-Point Numbers">4 Floating-Point Numbers</a><ul>
<li><a href="#encode-floats-as-integers" title="Encode Floats as Integers">4.1 Encode Floats as Integers</a><ul></ul></li>
<li><a href="#accuracy-and-precision" title="Accuracy and Precision">4.2 Accuracy and Precision</a><ul></ul></li></ul></li>
<li><a href="#lorawan-with-cbor" title="LoRaWAN With CBOR">5 LoRaWAN With CBOR</a><ul>
<li><a href="#encode-sensor-data" title="Encode Sensor Data">5.1 Encode Sensor Data</a><ul></ul></li>
<li><a href="#send-lorawan-packet" title="Send LoRaWAN Packet">5.2 Send LoRaWAN Packet</a><ul></ul></li>
<li><a href="#cbor-in-action" title="CBOR In Action">5.3 CBOR In Action</a><ul></ul></li></ul></li>
<li><a href="#decode-cbor" title="Decode CBOR">6 Decode CBOR</a><ul></ul></li>
<li><a href="#whats-next" title="Whats Next">7 Whats Next</a><ul></ul></li>
<li><a href="#notes" title="Notes">8 Notes</a><ul></ul></li>
<li><a href="#appendix-build-and-run-cbor-firmware" title="Appendix: Build and Run CBOR Firmware">9 Appendix: Build and Run CBOR Firmware</a><ul>
<li><a href="#build-cbor-firmware" title="Build CBOR Firmware">9.1 Build CBOR Firmware</a><ul></ul></li>
<li><a href="#flash-cbor-firmware" title="Flash CBOR Firmware">9.2 Flash CBOR Firmware</a><ul></ul></li>
<li><a href="#run-cbor-firmware" title="Run CBOR Firmware">9.3 Run CBOR Firmware</a><ul></ul></li></ul></li>
<li><a href="#appendix-add-tinycbor-to-your-project" title="Appendix: Add TinyCBOR to Your Project">10 Appendix: Add TinyCBOR to Your Project</a><ul></ul></li>
<li><a href="#appendix-build-and-run-lorawan-firmware" title="Appendix: Build and Run LoRaWAN Firmware">11 Appendix: Build and Run LoRaWAN Firmware</a><ul>
<li><a href="#build-lorawan-firmware" title="Build LoRaWAN Firmware">11.1 Build LoRaWAN Firmware</a><ul></ul></li>
<li><a href="#flash-lorawan-firmware" title="Flash LoRaWAN Firmware">11.2 Flash LoRaWAN Firmware</a><ul></ul></li>
<li><a href="#run-lorawan-firmware" title="Run LoRaWAN Firmware">11.3 Run LoRaWAN Firmware</a><ul></ul></li>
<li><a href="#enter-lorawan-commands" title="Enter LoRaWAN Commands">11.4 Enter LoRaWAN Commands</a><ul></ul></li></ul></li>
<li><a href="#appendix-porting-tinycbor-to-bl602" title="Appendix: Porting TinyCBOR to BL602">12 Appendix: Porting TinyCBOR to BL602</a><ul></ul></li></ul></nav><p>📝 <em>5 Oct 2021</em></p>
<p>Suppose were creating an IoT Gadget that transmits <strong>Sensor Data</strong> from a <strong>Temperature Sensor</strong> and a <strong>Light Sensor</strong></p>
<div class="example-wrap"><pre class="language-json"><code>{
&quot;t&quot;: 1234,
&quot;l&quot;: 2345
}</code></pre></div>
<p>(Located in a Greenhouse perhaps)</p>
<p>And were transmitting over a <strong>low-power wireless network</strong> like LoRa, Zigbee or Bluetooth LE.</p>
<p>We could transmit <strong>19 bytes of JSON</strong>. But theres a more compact way to do it….</p>
<p><a href="https://en.wikipedia.org/wiki/CBOR"><strong>Concise Binary Object Representation (CBOR)</strong></a>, which works like a binary, compressed form of JSON.</p>
<p>And we need only <strong>11 bytes of CBOR</strong>!</p>
<p><img src="https://lupyuen.github.io/images/cbor-title.jpg" alt="Encoding Sensor Data with CBOR on BL602" /></p>
<p>Today well learn to encode Sensor Data with the <strong>TinyCBOR Library</strong> that we have ported to the <a href="https://lupyuen.github.io/articles/pinecone"><strong>BL602</strong></a> and <a href="https://lupyuen.github.io/articles/pinedio2"><strong>BL604</strong></a> RISC-V SoCs…</p>
<ul>
<li><a href="https://github.com/lupyuen/tinycbor-bl602"><strong>lupyuen/tinycbor-bl602</strong></a></li>
</ul>
<p>The library has been tested on <a href="https://lupyuen.github.io/articles/pinedio2"><strong>PineDio Stack BL604</strong></a>, but it should work on <strong>any BL602 or BL604 Board</strong>: <a href="https://docs.ai-thinker.com/en/wb2"><strong>Ai-Thinker Ai-WB2</strong></a>, PineCone BL602, Pinenut, DT-BL10, MagicHome BL602, …</p>
<p><em>Must we scrimp and save every single byte?</em></p>
<p>Yes, <strong>every single byte matters</strong> for low-power wireless networks!</p>
<ol>
<li>
<p>Low-power wireless networks operate on Radio Frequency Bands that are <strong>shared with many other gadgets</strong>.</p>
<p>They are prone to <strong>collisions and interference</strong>.</p>
<p>The <strong>smaller the data packet</strong>, the higher the chance that it will be <strong>transmitted successfully</strong>!</p>
</li>
<li>
<p>When we transmit LoRa packets to <strong>The Things Network</strong> (the free public global LoRa network), were limited by their <a href="https://lupyuen.github.io/articles/ttn#fair-use-of-the-things-network"><strong>Fair Use Policy</strong></a>.</p>
<p><a href="https://lupyuen.github.io/articles/ttn#fair-use-of-the-things-network">(Roughly <strong>12 bytes</strong> per message, assuming 10 messages per hour)</a></p>
<p>JSON is too big for this. But CBOR works well!</p>
<p>In a while well watch the TinyCBOR Library in action for encoding Sensor Data in The Things Network.</p>
</li>
</ol>
<h1 id="encode-sensor-data-with-tinycbor"><a class="doc-anchor" href="#encode-sensor-data-with-tinycbor">§</a>1 Encode Sensor Data with TinyCBOR</h1>
<p>We begin by encoding one data field into CBOR…</p>
<div class="example-wrap"><pre class="language-json"><code>{
&quot;t&quot;: 1234
}</code></pre></div>
<p>We call this a <strong>CBOR Map</strong> that maps a <strong>Key</strong> (“<code>t</code>”) to a <strong>Value</strong> (<code>1234</code>)…</p>
<blockquote>
<p><img src="https://lupyuen.github.io/images/cbor-map.png" alt="CBOR Map with 1 Key-Value Pair" /></p>
</blockquote>
<p>Lets look at the code from our firmware that encodes the above into CBOR…</p>
<ul>
<li><a href="https://github.com/lupyuen/bl_iot_sdk/tree/master/customer_app/pinedio_cbor"><strong>pinedio_cbor Firmware</strong></a></li>
</ul>
<h2 id="output-buffer-and-cbor-encoder"><a class="doc-anchor" href="#output-buffer-and-cbor-encoder">§</a>1.1 Output Buffer and CBOR Encoder</h2>
<p>First we create an <strong>Output Buffer</strong> that will hold the encoded CBOR data: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/pinedio_cbor/pinedio_cbor/demo.c#L9-L66">pinedio_cbor/demo.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>/// Test CBOR Encoding for { &quot;t&quot;: 1234 }
static void test_cbor(char *buf, int len, int argc, char **argv) {
// Max output size is 50 bytes (which fits in a LoRa packet)
uint8_t output[50];</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/ttn#fair-use-of-the-things-network">(50 bytes is the max packet size for The Things Network AS923 DR2)</a></p>
<p><strong>Output Buffer Size</strong> is important: Calls to the <strong>TinyCBOR library will fail</strong> if we run out of buffer space!</p>
<p>Next we define the <strong>CBOR Encoder</strong> (from TinyCBOR) that will encode our data…</p>
<div class="example-wrap"><pre class="language-c"><code> // Our CBOR Encoder and Map Encoder
CborEncoder encoder, mapEncoder;</code></pre></div>
<p>As well as the <strong>Map Encoder</strong> that will encode our CBOR Map.</p>
<p>We <strong>initialise the CBOR Encoder</strong> like so…</p>
<div class="example-wrap"><pre class="language-c"><code> // Init our CBOR Encoder
cbor_encoder_init(
&amp;encoder, // CBOR Encoder
output, // Output Buffer
sizeof(output), // Output Buffer Size
0 // Options (always 0)
);</code></pre></div><h2 id="create-map-encoder"><a class="doc-anchor" href="#create-map-encoder">§</a>1.2 Create Map Encoder</h2>
<p>Now we create the <strong>Map Encoder</strong> that will encode our CBOR Map…</p>
<div class="example-wrap"><pre class="language-c"><code> // Create a Map Encoder that maps keys to values
CborError res = cbor_encoder_create_map(
&amp;encoder, // CBOR Encoder
&amp;mapEncoder, // Map Encoder
1 // Number of Key-Value Pairs
);
assert(res == CborNoError);</code></pre></div>
<p>The last parameter (<code>1</code>) is important: It must match the <strong>Number of Key-Value Pairs</strong> (like <code>"t": 1234</code>) that we shall encode.</p>
<h2 id="encode-key-and-value"><a class="doc-anchor" href="#encode-key-and-value">§</a>1.3 Encode Key and Value</h2>
<p>We encode the <strong>Key</strong> (“<code>t</code>”) into the CBOR Map…</p>
<div class="example-wrap"><pre class="language-c"><code> // First Key-Value Pair: Map the Key
res = cbor_encode_text_stringz(
&amp;mapEncoder, // Map Encoder
&quot;t&quot; // Key
);
assert(res == CborNoError);</code></pre></div>
<p>Followed by the <strong>Value</strong> (<code>1234</code>)…</p>
<div class="example-wrap"><pre class="language-c"><code> // First Key-Value Pair: Map the Value
res = cbor_encode_int(
&amp;mapEncoder, // Map Encoder
1234 // Value
);
assert(res == CborNoError);</code></pre></div>
<p><strong>cbor_encode_int</strong> encodes <strong>64-bit Signed Integers</strong>.</p>
<p>(Well look at other data types in a while)</p>
<h2 id="close-map-encoder"><a class="doc-anchor" href="#close-map-encoder">§</a>1.4 Close Map Encoder</h2>
<p>Were done with our CBOR Map, so we <strong>close the Map Encoder</strong></p>
<div class="example-wrap"><pre class="language-c"><code> // Close the Map Encoder
res = cbor_encoder_close_container(
&amp;encoder, // CBOR Encoder
&amp;mapEncoder // Map Encoder
);
assert(res == CborNoError);</code></pre></div>
<p>Our CBOR Encoding is complete!</p>
<h2 id="get-encoded-output"><a class="doc-anchor" href="#get-encoded-output">§</a>1.5 Get Encoded Output</h2>
<p>To work with the Encoded CBOR Output, we need to know <strong>how many bytes</strong> have been encoded…</p>
<div class="example-wrap"><pre class="language-c"><code> // How many bytes were encoded
size_t output_len = cbor_encoder_get_buffer_size(
&amp;encoder, // CBOR Encoder
output // Output Buffer
);
printf(&quot;CBOR Output: %d bytes\r\n&quot;, output_len);</code></pre></div>
<p>For the demo we <strong>dump the encoded CBOR data</strong> to the console…</p>
<div class="example-wrap"><pre class="language-c"><code> // Dump the encoded CBOR output (6 bytes):
// 0xa1 0x61 0x74 0x19 0x04 0xd2
for (int i = 0; i &lt; output_len; i++) {
printf(&quot; 0x%02x\r\n&quot;, output[i]);
}
}</code></pre></div>
<p>And thats how we call the TinyCBOR Library to work with CBOR data!</p>
<p>Lets watch what happens when we run the firmware…</p>
<blockquote>
<p><img src="https://lupyuen.github.io/images/cbor-code.png" alt="Calling the TinyCBOR Library" /></p>
</blockquote>
<h2 id="magic-happens"><a class="doc-anchor" href="#magic-happens">§</a>1.6 Magic Happens</h2>
<p>Follow the steps in the Appendix to <strong>build, flash and run</strong> the CBOR Firmware…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/cbor#appendix-build-and-run-cbor-firmware"><strong>“Build and Run CBOR Firmware”</strong></a></li>
</ul>
<p>At the BL602 / BL604 Command Prompt, enter…</p>
<div class="example-wrap"><pre class="language-bash"><code>test_cbor</code></pre></div>
<p>Well see 6 bytes of <strong>Encoded CBOR Output</strong></p>
<div class="example-wrap"><pre class="language-text"><code>CBOR Output: 6 bytes
0xa1
0x61
0x74
0x19
0x04
0xd2</code></pre></div>
<p>We have just compressed <strong>10 bytes of JSON</strong></p>
<div class="example-wrap"><pre class="language-json"><code>{
&quot;t&quot;: 1234
}</code></pre></div>
<p>Into <strong>6 bytes of CBOR</strong>.</p>
<p>We have scrimped and saved <strong>4 bytes</strong>!</p>
<p><img src="https://lupyuen.github.io/images/cbor-output2.png" alt="Encoded CBOR Output" /></p>
<h1 id="add-another-field"><a class="doc-anchor" href="#add-another-field">§</a>2 Add Another Field</h1>
<p>Now we <strong>add another field</strong> to our CBOR Encoding…</p>
<div class="example-wrap"><pre class="language-json"><code>{
&quot;t&quot;: 1234,
&quot;l&quot;: 2345
}</code></pre></div>
<p>And watch how our program changes to accommodate the second field.</p>
<blockquote>
<p><img src="https://lupyuen.github.io/images/cbor-map2.png" alt="CBOR Map with 2 Key-Value Pairs" /></p>
</blockquote>
<h2 id="modify-map-encoder"><a class="doc-anchor" href="#modify-map-encoder">§</a>2.1 Modify Map Encoder</h2>
<p>We begin the same way as before: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/pinedio_cbor/pinedio_cbor/demo.c#L68-L139">pinedio_cbor/demo.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>/// Test CBOR Encoding for { &quot;t&quot;: 1234, &quot;l&quot;: 2345 }
static void test_cbor2( ... ) {
// Max output size is 50 bytes (which fits in a LoRa packet)
uint8_t output[50];
// Our CBOR Encoder and Map Encoder
CborEncoder encoder, mapEncoder;
// Init our CBOR Encoder
cbor_encoder_init( ... );</code></pre></div>
<p>Now we <strong>create the Map Encoder</strong> with a tiny modification…</p>
<div class="example-wrap"><pre class="language-c"><code> // Create a Map Encoder that maps keys to values
CborError res = cbor_encoder_create_map(
&amp;encoder, // CBOR Encoder
&amp;mapEncoder, // Map Encoder
2 // Number of Key-Value Pairs
);
assert(res == CborNoError);</code></pre></div>
<p>We changed the <strong>Number of Key-Value Pairs</strong> to <code>2</code>.</p>
<p>(Previously it was <code>1</code>)</p>
<h2 id="encode-first-key-and-value"><a class="doc-anchor" href="#encode-first-key-and-value">§</a>2.2 Encode First Key and Value</h2>
<p>We encode the <strong>First Key and Value</strong> the same way as before…</p>
<div class="example-wrap"><pre class="language-c"><code> // First Key-Value Pair: Map the Key
res = cbor_encode_text_stringz(
&amp;mapEncoder, // Map Encoder
&quot;t&quot; // Key
);
assert(res == CborNoError);
// First Key-Value Pair: Map the Value
res = cbor_encode_int(
&amp;mapEncoder, // Map Encoder
1234 // Value
);
assert(res == CborNoError);</code></pre></div>
<p>(Yep no changes above)</p>
<h2 id="encode-second-key-and-value"><a class="doc-anchor" href="#encode-second-key-and-value">§</a>2.3 Encode Second Key and Value</h2>
<p>This part is new: We encode the <strong>Second Key and Value</strong> (“<code>l</code>” and <code>2345</code>)…</p>
<div class="example-wrap"><pre class="language-c"><code> // Second Key-Value Pair: Map the Key
res = cbor_encode_text_stringz(
&amp;mapEncoder, // Map Encoder
&quot;l&quot; // Key
);
assert(res == CborNoError);
// Second Key-Value Pair: Map the Value
res = cbor_encode_int(
&amp;mapEncoder, // Map Encoder
2345 // Value
);
assert(res == CborNoError);</code></pre></div>
<p>And the rest of the code is the same…</p>
<div class="example-wrap"><pre class="language-c"><code> // Close the Map Encoder
res = cbor_encoder_close_container( ... );
// How many bytes were encoded
size_t output_len = cbor_encoder_get_buffer_size( ... );
// Dump the encoded CBOR output (11 bytes):
// 0xa2 0x61 0x74 0x19 0x04 0xd2 0x61 0x6c 0x19 0x09 0x29
for (int i = 0; i &lt; output_len; i++) {
printf(&quot; 0x%02x\r\n&quot;, output[i]);
}
}</code></pre></div>
<p>Recap: To add a data field to our CBOR Encoding, we…</p>
<ol>
<li>
<p>Modify the call to <strong>cbor_encoder_create_map</strong> and update the <strong>Number of Key-Value Pairs</strong> (<code>2</code>)</p>
</li>
<li>
<p>Add the new <strong>Key and Value</strong> (“<code>l</code>” and <code>2345</code>) to the CBOR Map</p>
</li>
</ol>
<p>Everything else stays the same.</p>
<blockquote>
<p><img src="https://lupyuen.github.io/images/cbor-code2.png" alt="Add a second field" /></p>
</blockquote>
<h2 id="watch-the-magic"><a class="doc-anchor" href="#watch-the-magic">§</a>2.4 Watch the Magic</h2>
<p>Follow the steps in the Appendix to <strong>build, flash and run</strong> the CBOR Firmware…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/cbor#appendix-build-and-run-cbor-firmware"><strong>“Build and Run CBOR Firmware”</strong></a></li>
</ul>
<p>At the BL602 / BL604 Command Prompt, enter…</p>
<div class="example-wrap"><pre class="language-bash"><code>test_cbor2</code></pre></div>
<p>This time well see 11 bytes of <strong>Encoded CBOR Output</strong></p>
<div class="example-wrap"><pre class="language-text"><code>CBOR Output: 11 bytes
0xa2
0x61
0x74
0x19
0x04
0xd2
0x61
0x6c
0x19
0x09
0x29</code></pre></div>
<p>We have just compressed <strong>19 bytes of JSON</strong> into <strong>11 bytes of CBOR</strong>.</p>
<p><strong>8 bytes</strong> saved!</p>
<p>If we wish to call TinyCBOR from an existing BL602 / BL604 project, check the Appendix…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/cbor#appendix-add-tinycbor-to-your-project"><strong>“Add TinyCBOR to Your Project”</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/cbor-title.jpg" alt="Encoding Sensor Data with CBOR on BL602" /></p>
<h1 id="cbor-data-types"><a class="doc-anchor" href="#cbor-data-types">§</a>3 CBOR Data Types</h1>
<p><em>Weve been encoding 64-bit Signed Integers. What other Data Types can we encode?</em></p>
<p>Below are the <strong>CBOR Data Types</strong> and their respective <strong>Encoder Functions</strong> from the TinyCBOR Library…</p>
<h2 id="numbers"><a class="doc-anchor" href="#numbers">§</a>3.1 Numbers</h2>
<ul>
<li>
<p><strong>Signed Integer</strong> (64 bits): <a href="https://intel.github.io/tinycbor/current/a00046.html#gabbf6e10fd963d673f5ad293dff4a67a9">cbor_encode_int</a></p>
<p>(We called this earlier. Works for positive and negative integers)</p>
</li>
<li>
<p><strong>Unsigned Integer</strong> (64 bits): <a href="https://intel.github.io/tinycbor/current/a00046.html#ga2b898ce6f5821c5aba8b6f0020c4b5ba">cbor_encode_uint</a></p>
<p>(Positive integers only)</p>
</li>
<li>
<p><strong>Negative Integer</strong> (64 bits): <a href="https://intel.github.io/tinycbor/current/a00046.html#ga0e84daa854e0480f4a3758bcb46b9b60">cbor_encode_negative_int</a></p>
<p>(Negative integers only)</p>
</li>
<li>
<p><strong>Floating-Point Number</strong> (16, 32 or 64 bits):</p>
<p>(See the next chapter)</p>
</li>
</ul>
<h2 id="strings"><a class="doc-anchor" href="#strings">§</a>3.2 Strings</h2>
<ul>
<li>
<p><strong>Null-Terminated String</strong>: <a href="https://intel.github.io/tinycbor/current/a00046.html#ga6df3eff486535322f66584dc5431f9e9">cbor_encode_text_stringz</a></p>
<p>(We called this earlier to encode our Keys)</p>
</li>
<li>
<p><strong>Text String</strong>: <a href="https://intel.github.io/tinycbor/current/a00046.html#ga4fa673c63e85b1fd6f8067aca4ccdde4">cbor_encode_text_string</a></p>
<p>(For strings that are not null-terminated)</p>
</li>
<li>
<p><strong>Byte String</strong>: <a href="https://intel.github.io/tinycbor/current/a00046.html#ga1260b72bb0f067fd3c68d49a6b5f58d0">cbor_encode_byte_string</a></p>
<p>(For strings containing binary data)</p>
</li>
</ul>
<h2 id="other-types"><a class="doc-anchor" href="#other-types">§</a>3.3 Other Types</h2>
<ul>
<li>
<p><strong>Boolean</strong>: <a href="https://intel.github.io/tinycbor/current/a00046.html#ga857154b97cad978f4afb3e2f809051bd">cbor_encode_boolean</a></p>
</li>
<li>
<p><strong>Null</strong>: <a href="https://intel.github.io/tinycbor/current/a00046.html#ga30b769ff1da73ed8b4536f551347c5ed">cbor_encode_null</a></p>
</li>
<li>
<p><strong>Undefined</strong>: <a href="https://intel.github.io/tinycbor/current/a00046.html#ga9d9f0668e2cf69352a45095006efab4f">cbor_encode_undefined</a></p>
</li>
</ul>
<p>For the complete list of CBOR Encoder Functions, refer to the TinyCBOR docs…</p>
<ul>
<li><a href="https://intel.github.io/tinycbor/current/a00046.html"><strong>TinyCBOR: Encoding To CBOR</strong></a></li>
</ul>
<p>CBOR Data Types are explained in the CBOR Specification…</p>
<ul>
<li><a href="https://www.rfc-editor.org/rfc/rfc8949.html#name-cbor-data-models"><strong>CBOR Data Models</strong></a></li>
</ul>
<p>To experiment with CBOR Encoding and Decoding, try the <a href="http://cbor.me/"><strong>CBOR Playground</strong></a></p>
<p><img src="https://lupyuen.github.io/images/grafana-cbor5.png" alt="CBOR Playground" /></p>
<h1 id="floating-point-numbers"><a class="doc-anchor" href="#floating-point-numbers">§</a>4 Floating-Point Numbers</h1>
<p>The CBOR spec says that there are <a href="https://www.rfc-editor.org/rfc/rfc8949.html#name-floating-point-numbers-and-"><strong>3 ways to encode floats</strong></a></p>
<ul>
<li>
<p><a href="https://en.m.wikipedia.org/wiki/Half-precision_floating-point_format"><strong>Half-Precision Float</strong></a> (16 bits): <a href="https://intel.github.io/tinycbor/current/a00046.html#gad8e5a125cfaceb9a32528e620e003bc6">cbor_encode_half_float</a></p>
<p>(<strong>3.3</strong> significant decimal digits. <a href="https://en.m.wikipedia.org/wiki/Half-precision_floating-point_format#IEEE_754_half-precision_binary_floating-point_format:_binary16">See this</a>)</p>
</li>
<li>
<p><a href="https://en.m.wikipedia.org/wiki/Single-precision_floating-point_format"><strong>Single-Precision Float</strong></a> (32 bits): <a href="https://intel.github.io/tinycbor/current/a00046.html#gae981ee934ef22ce4c5b52f8069e1b15c">cbor_encode_float</a></p>
<p>(<strong>6 to 9</strong> significant decimal digits. <a href="https://en.m.wikipedia.org/wiki/Single-precision_floating-point_format#IEEE_754_single-precision_binary_floating-point_format:_binary32">See this</a>)</p>
</li>
<li>
<p><a href="https://en.m.wikipedia.org/wiki/Double-precision_floating-point_format"><strong>Double-Precision Float</strong></a> (64 bits): <a href="https://intel.github.io/tinycbor/current/a00046.html#ga211aa80dc5b793ee8dd74d24cb9e7ca6">cbor_encode_double</a></p>
<p>(<strong>15 to 17</strong> significant decimal digits. <a href="https://en.m.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64">See this</a>)</p>
</li>
</ul>
<p><em>How do we select the proper float encoding?</em></p>
<p>Suppose were encoding Temperature Data (like <code>12.34</code> ºC) that could range from <strong><code>0.00</code> ºC to <code>99.99</code> ºC</strong>.</p>
<p>This means that we need <strong>4 significant decimal digits</strong>.</p>
<p>Which is too many for a Half-Precision Float (16 bits), but OK for a <strong>Single-Precision Float</strong> (32 bits).</p>
<p>Thus we need <strong>5 bytes</strong> to encode the Temperature Data. (Including the CBOR Initial Byte)</p>
<h2 id="encode-floats-as-integers"><a class="doc-anchor" href="#encode-floats-as-integers">§</a>4.1 Encode Floats as Integers</h2>
<p><em>Huh? If we encode an integer like <code>1234</code>, we need only <strong>3 bytes</strong>!</em></p>
<p>Thats why in this article we <strong>scale up 100 times</strong> for the Temperature Data and <strong>encode as an integer</strong> instead.</p>
<p>(So <code>1234</code> actually means <code>12.34</code> ºC)</p>
<p><strong>2 bytes</strong> saved!</p>
<p>(Our scaling of Sensor Data is similar to <a href="https://en.wikipedia.org/wiki/Fixed-point_arithmetic#Fixed-point_representation">Fixed-Point Representation</a>)</p>
<h2 id="accuracy-and-precision"><a class="doc-anchor" href="#accuracy-and-precision">§</a>4.2 Accuracy and Precision</h2>
<p><em>Is it meaningful to record temperatures that are accurate to 0.01 ºC?</em></p>
<p><em>How much accuracy do we need for Sensor Data anyway?</em></p>
<p>The accuracy for our Sensor Data depends on…</p>
<ol>
<li>
<p>Our monitoring requirements, and</p>
</li>
<li>
<p>Accuracy of our sensors</p>
</li>
</ol>
<p>Learn more about Accuracy and Precision of Sensor Data…</p>
<ul>
<li><a href="https://kotahi.net/iots-lesser-known-power-good-enough-data-accuracy/">IoTs Lesser Known Power: “Good Enough” Data Accuracy</a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/ttn-title.jpg" alt="PineDio Stack BL604 RISC-V Board (foreground) talking to The Things Network via RAKWireless RAK7248 LoRaWAN Gateway (background)" /></p>
<p><em>PineDio Stack BL604 RISC-V Board (foreground) talking to The Things Network via RAKWireless RAK7248 LoRaWAN Gateway (background)</em></p>
<h1 id="lorawan-with-cbor"><a class="doc-anchor" href="#lorawan-with-cbor">§</a>5 LoRaWAN With CBOR</h1>
<p>Lets watch CBOR in action on a real wireless network… As <a href="https://lupyuen.github.io/articles/lorawan2"><strong>PineDio Stack BL604</strong></a> talks to <a href="https://lupyuen.github.io/articles/ttn"><strong>The Things Network over LoRaWAN</strong></a>!</p>
<p>In a while we shall run this LoRaWAN Command…</p>
<div class="example-wrap"><pre class="language-bash"><code>las_app_tx_cbor 2 0 1234 2345</code></pre></div>
<p>This means…</p>
<ul>
<li>
<p>Transmit a LoRaWAN Packet to <strong>Port 2</strong></p>
</li>
<li>
<p>That contains the values <strong><code>t=1234</code></strong> (Temperature), <strong><code>l=2345</code></strong> (Light Level)</p>
</li>
<li>
<p><code>0</code> means that this is an <strong>Unconfirmed Message</strong></p>
<p>(Because were not expecting an acknowledgement)</p>
</li>
</ul>
<p>Our CBOR Encoding happens inside the <strong>las_app_tx_cbor</strong> function: <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/pinedio_lorawan/pinedio_lorawan/lorawan.c#L893-L1050">pinedio_lorawan/lorawan.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>/// Transmit CBOR payload to LoRaWAN. The command
/// las_app_tx_cbor 2 0 1234 2345
/// Will transmit the CBOR payload
/// { &quot;t&quot;: 1234, &quot;l&quot;: 2345 }
/// To port 2, unconfirmed (0).
void las_cmd_app_tx_cbor( ... ) {
...
// Get the &quot;t&quot; value from command args
uint16_t t = parse_ull_bounds(argv[3], 0, 65535, &amp;rc);
// Get the &quot;l&quot; value from command args
uint16_t l = parse_ull_bounds(argv[4], 0, 65535, &amp;rc);</code></pre></div>
<p>In the code above we get the values of <strong><code>t</code></strong> (Temperature Sensor) and <strong><code>l</code></strong> (Light Sensor) from the command line arguments.</p>
<p>(Our sensors are simulated for now)</p>
<p>Watch how we encode “<code>t</code>” and “<code>l</code>” and transmit them…</p>
<h2 id="encode-sensor-data"><a class="doc-anchor" href="#encode-sensor-data">§</a>5.1 Encode Sensor Data</h2>
<p>This part looks super familiar: We initialise our <strong>CBOR Encoder and Map Encoder</strong></p>
<div class="example-wrap"><pre class="language-c"><code> // Encode into CBOR for { &quot;t&quot;: ????, &quot;l&quot;: ???? }
// Max output size is 50 bytes (which fits in a LoRa packet)
uint8_t output[50];
// Our CBOR Encoder and Map Encoder
CborEncoder encoder, mapEncoder;
// Init our CBOR Encoder
cbor_encoder_init( ... );
// Create a Map Encoder that maps keys to values (2 pairs)
CborError res = cbor_encoder_create_map( ... );</code></pre></div>
<p>Next we encode the <strong>Key and Value for “<code>t</code></strong></p>
<div class="example-wrap"><pre class="language-c"><code> // First Key-Value Pair: Map the Key (&quot;t&quot;)
res = cbor_encode_text_stringz(
&amp;mapEncoder, // Map Encoder
&quot;t&quot; // Key
);
assert(res == CborNoError);
// First Key-Value Pair: Map the Value
res = cbor_encode_int(
&amp;mapEncoder, // Map Encoder
t // Value
);
assert(res == CborNoError);</code></pre></div>
<p>Then we encode the <strong>Key and Value for “<code>l</code></strong></p>
<div class="example-wrap"><pre class="language-c"><code> // Second Key-Value Pair: Map the Key (&quot;l&quot;)
res = cbor_encode_text_stringz(
&amp;mapEncoder, // Map Encoder
&quot;l&quot; // Key
);
assert(res == CborNoError);
// Second Key-Value Pair: Map the Value
res = cbor_encode_int(
&amp;mapEncoder, // Map Encoder
l // Value
);
assert(res == CborNoError);</code></pre></div>
<p>And we <strong>close the Map Encoder</strong></p>
<div class="example-wrap"><pre class="language-c"><code> // Close the Map Encoder
res = cbor_encoder_close_container( ... );
// How many bytes were encoded
size_t output_len = cbor_encoder_get_buffer_size( ... );</code></pre></div><h2 id="send-lorawan-packet"><a class="doc-anchor" href="#send-lorawan-packet">§</a>5.2 Send LoRaWAN Packet</h2>
<p>Were ready to transmit our encoded Sensor Data!</p>
<p>First we <strong>allocate a Packet Buffer</strong> for our LoRaWAN Packet…</p>
<div class="example-wrap"><pre class="language-c"><code> // Validate the output size
if (lora_app_mtu() &lt; output_len) { return; } // Output too big
// Attempt to allocate a Packet Buffer
struct pbuf *om = lora_pkt_alloc(output_len);
if (!om) { return; } // Unable to allocate Packet Buffer</code></pre></div>
<p>Next we <strong>copy our encoded Sensor Data</strong> into the Packet Buffer…</p>
<div class="example-wrap"><pre class="language-c"><code> // Copy the encoded CBOR into the Packet Buffer
rc = pbuf_copyinto(om, 0, output, output_len);
assert(rc == 0);</code></pre></div>
<p>Finally we <strong>transmit the Packet Buffer</strong></p>
<div class="example-wrap"><pre class="language-c"><code> // Send the Packet Buffer
rc = lora_app_port_send(port, mcps_type, om);
// Omitted: Check the return code</code></pre></div>
<p>Thats how we encode Sensor Data and transmit over LoRaWAN!</p>
<p><img src="https://lupyuen.github.io/images/cbor-code3.png" alt="Encoding Sensor Data and transmitting over LoRaWAN" /></p>
<h2 id="cbor-in-action"><a class="doc-anchor" href="#cbor-in-action">§</a>5.3 CBOR In Action</h2>
<p>Follow the instructions in the Appendix to <strong>build, flash and run</strong> the LoRaWAN Firmware…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/cbor#appendix-build-and-run-lorawan-firmware"><strong>“Build and Run LoRaWAN Firmware”</strong></a></li>
</ul>
<p>At the BL602 / BL604 Command Prompt, enter this command…</p>
<div class="example-wrap"><pre class="language-bash"><code>las_app_tx_cbor 2 0 1234 2345</code></pre></div>
<p>This means…</p>
<ul>
<li>
<p>Transmit a LoRaWAN Packet to <strong>Port 2</strong></p>
</li>
<li>
<p>That contains the values <strong><code>t=1234</code></strong> (Temperature), <strong><code>l=2345</code></strong> (Light Level)</p>
</li>
<li>
<p><code>0</code> means that this is an <strong>Unconfirmed Message</strong></p>
<p>(Because were not expecting an acknowledgement)</p>
</li>
</ul>
<p>Our Sensor Data has been transmitted via LoRaWAN to The Things Network!</p>
<p><em>How do we see the Sensor Data in The Things Network?</em></p>
<p>We could use <strong>Grafana</strong>, the open source tool for data visualisation…</p>
<p><img src="https://lupyuen.github.io/images/cbor-grafana.jpg" alt="Sensor Data visualised with Grafana" /></p>
<p>Check out this article for the details…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/grafana"><strong>“Grafana Data Source for The Things Network”</strong></a></li>
</ul>
<p>See also this demo of PineDio Stack with Roblox and The Things Network…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/roblox#digital-twin-demo"><strong>“IoT Digital Twin with Roblox and The Things Network”</strong></a></li>
</ul>
<h1 id="decode-cbor"><a class="doc-anchor" href="#decode-cbor">§</a>6 Decode CBOR</h1>
<p><em>For decoding CBOR packets, can we call the TinyCBOR Library?</em></p>
<p>Sure, we can call the <strong>Decoder Functions</strong> in the TinyCBOR Library…</p>
<ul>
<li>
<p><a href="https://intel.github.io/tinycbor/current/a00047.html"><strong>TinyCBOR: Parsing CBOR streams</strong></a></p>
</li>
<li>
<p><a href="https://intel.github.io/tinycbor/current/a00048.html"><strong>TinyCBOR: Converting CBOR to text</strong></a></p>
</li>
<li>
<p><a href="https://intel.github.io/tinycbor/current/a00049.html"><strong>TinyCBOR: Converting CBOR to JSON</strong></a></p>
</li>
</ul>
<p>If were transmitting CBOR packets to a server (or cloud), we can decode them with a <strong>CBOR Library for Node.js, Go, Rust,</strong></p>
<ul>
<li><a href="https://cbor.io/impls.html"><strong>CBOR Implementations</strong></a></li>
</ul>
<p>We can decode CBOR Payloads in <strong>The Things Network</strong> with a CBOR Payload Formatter…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/payload"><strong>“CBOR Payload Formatter for The Things Network”</strong></a></li>
</ul>
<p>For Grafana we used a <strong>Go Library for CBOR</strong></p>
<ul>
<li><a href="https://lupyuen.github.io/articles/grafana#decode-cbor-in-go"><strong>“Decode CBOR in Go”</strong></a></li>
</ul>
<p>Theres even a CBOR Library for <strong>Roblox and Lua Scripting</strong></p>
<ul>
<li><a href="https://github.com/lupyuen/roblox-the-things-network#decode-base64-and-cbor-in-roblox"><strong>“Decode Base64 and CBOR in Roblox”</strong></a></li>
</ul>
<p>TinyCBOR is available on various <strong>Embedded Operating Systems</strong></p>
<ul>
<li>
<p><a href="https://github.com/apache/mynewt-core/tree/master/encoding/tinycbor"><strong>Apache Mynewt</strong></a></p>
</li>
<li>
<p><a href="https://doc.riot-os.org/group__pkg__tinycbor.html"><strong>RIOT</strong></a></p>
</li>
<li>
<p><a href="https://docs.zephyrproject.org/latest/reference/kconfig/CONFIG_TINYCBOR.html"><strong>Zephyr</strong></a></p>
</li>
</ul>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>7 Whats Next</h1>
<p>For the next article we shall take a quick detour and explore PineDio Stack transmitting Sensor Data to <a href="https://github.com/lupyuen/roblox-the-things-network"><strong>Roblox via The Things Network</strong></a>.</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/roblox"><strong>“IoT Digital Twin with Roblox and The Things Network”</strong></a></li>
</ul>
<p>Then we shall head back and transmit BL602 / BL604s <strong>Internal Temperature Sensor Data</strong> to The Things Network.</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/tsen"><strong>“Internal Temperature Sensor on BL602”</strong></a></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/RISCV/comments/q1ir5x/encode_sensor_data_with_cbor_on_bl602/">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/cbor.md"><code>lupyuen.github.io/src/cbor.md</code></a></p>
<h1 id="notes"><a class="doc-anchor" href="#notes">§</a>8 Notes</h1>
<ol>
<li>This article is the expanded version of <a href="https://twitter.com/MisterTechBlog/status/1441626008931602433">this Twitter Thread</a></li>
</ol>
<h1 id="appendix-build-and-run-cbor-firmware"><a class="doc-anchor" href="#appendix-build-and-run-cbor-firmware">§</a>9 Appendix: Build and Run CBOR Firmware</h1>
<p>Here are the steps to build, flash and run the <strong>CBOR Firmware for BL602 and BL604</strong></p>
<ul>
<li><a href="https://github.com/lupyuen/bl_iot_sdk/tree/master/customer_app/pinedio_cbor"><strong>bl_iot_sdk/customer_app/pinedio_cbor</strong></a></li>
</ul>
<p>(If we wish to add the TinyCBOR Library to an existing BL602 / BL604 project, check the next chapter)</p>
<h2 id="build-cbor-firmware"><a class="doc-anchor" href="#build-cbor-firmware">§</a>9.1 Build CBOR Firmware</h2>
<p>Download the firmware…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Download the master branch of lupyuen&#39;s bl_iot_sdk
git clone --recursive --branch master https://github.com/lupyuen/bl_iot_sdk</code></pre></div>
<p>Build the Firmware Binary File <code>pinedio_cbor.bin</code></p>
<div class="example-wrap"><pre class="language-bash"><code>## TODO: Change this to the full path of bl_iot_sdk
export BL60X_SDK_PATH=$HOME/bl_iot_sdk
export CONFIG_CHIP_NAME=BL602
cd bl_iot_sdk/customer_app/pinedio_cbor
make
## For WSL: Copy the firmware to /mnt/c/blflash, which refers to c:\blflash in Windows
mkdir /mnt/c/blflash
cp build_out/pinedio_cbor.bin /mnt/c/blflash</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/pinecone#building-firmware">More details on building bl_iot_sdk</a></p>
<h2 id="flash-cbor-firmware"><a class="doc-anchor" href="#flash-cbor-firmware">§</a>9.2 Flash CBOR Firmware</h2>
<p>Follow these steps to install <code>blflash</code></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 <code>pinedio_cbor.bin</code> has been copied to the <code>blflash</code> 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 <code>pinedio_cbor.bin</code> to BL602 / BL604 over UART…</p>
<div class="example-wrap"><pre class="language-bash"><code>## For Linux:
blflash flash build_out/pinedio_cbor.bin \
--port /dev/ttyUSB0
## For macOS:
blflash flash build_out/pinedio_cbor.bin \
--port /dev/tty.usbserial-1420 \
--initial-baud-rate 230400 \
--baud-rate 230400
## For Windows: Change COM5 to the BL602 / BL604 Serial Port
blflash flash c:\blflash\pinedio_cbor.bin --port COM5</code></pre></div>
<p>(For WSL: Do this under plain old Windows CMD, not WSL, because blflash needs to access the COM port)</p>
<p><a href="https://lupyuen.github.io/articles/flash#flash-the-firmware">More details on flashing firmware</a></p>
<h2 id="run-cbor-firmware"><a class="doc-anchor" href="#run-cbor-firmware">§</a>9.3 Run CBOR Firmware</h2>
<p>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><a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">More details on connecting to BL602 / BL604</a></p>
<h1 id="appendix-add-tinycbor-to-your-project"><a class="doc-anchor" href="#appendix-add-tinycbor-to-your-project">§</a>10 Appendix: Add TinyCBOR to Your Project</h1>
<p>Here are the steps for <strong>adding the TinyCBOR Library</strong> to an existing BL602 or BL604 project…</p>
<ul>
<li><a href="https://github.com/lupyuen/tinycbor-bl602"><strong>lupyuen/tinycbor-bl602</strong></a></li>
</ul>
<p>We assume theres an existing <strong>bl_iot_sdk</strong> folder.</p>
<p>Add <strong>tinycbor-bl602</strong> as a submodule under <strong>bl_iot_sdk/components/3rdparty</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>cd bl_iot_sdk/components/3rdparty
git submodule add https://github.com/lupyuen/tinycbor-bl602</code></pre></div>
<p>Edit the <strong>Makefile</strong> for our project…</p>
<div class="example-wrap"><pre class="language-text"><code>## Insert this line into the COMPONENTS block
COMPONENTS_TINYCBOR := tinycbor-bl602
...
## Insert this line into INCLUDE_COMPONENTS block
INCLUDE_COMPONENTS += $(COMPONENTS_TINYCBOR)
...
## This should appear after INCLUDE_COMPONENTS block
include $(BL60X_SDK_PATH)/make_scripts_riscv/project.mk</code></pre></div>
<p><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/pinedio_cbor/Makefile#L21-L36">(See a sample Makefile)</a></p>
<p>Include <strong>“cbor.h”</strong> in our source file…</p>
<div class="example-wrap"><pre class="language-c"><code>##include &quot;cbor.h&quot; // For Tiny CBOR Library</code></pre></div>
<p>And start coding with TinyCBOR!</p>
<p><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/pinedio_cbor/pinedio_cbor/demo.c">(See a sample source file)</a></p>
<h1 id="appendix-build-and-run-lorawan-firmware"><a class="doc-anchor" href="#appendix-build-and-run-lorawan-firmware">§</a>11 Appendix: Build and Run LoRaWAN Firmware</h1>
<p>Here are the steps to build, flash and run the <strong>LoRaWAN Firmware for PineDio Stack BL604</strong></p>
<ul>
<li><a href="https://github.com/lupyuen/bl_iot_sdk/tree/master/customer_app/pinedio_lorawan"><strong>bl_iot_sdk/customer_app/pinedio_lorawan</strong></a></li>
</ul>
<h2 id="build-lorawan-firmware"><a class="doc-anchor" href="#build-lorawan-firmware">§</a>11.1 Build LoRaWAN Firmware</h2>
<p>Download the <a href="https://github.com/lupyuen/bl_iot_sdk/tree/master/customer_app/pinedio_lorawan"><strong>LoRaWAN firmware and driver source code</strong></a></p>
<div class="example-wrap"><pre class="language-bash"><code>## Download the master branch of lupyuen&#39;s bl_iot_sdk
git clone --recursive --branch master https://github.com/lupyuen/bl_iot_sdk</code></pre></div>
<p>In the <code>customer_app/pinedio_lorawan</code> folder, edit <a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/pinedio_lorawan/Makefile"><code>Makefile</code></a> and find this setting…</p>
<div class="example-wrap"><pre class="language-text"><code>CFLAGS += -DCONFIG_LORA_NODE_REGION=1</code></pre></div>
<p>Change “<code>1</code>” to your LoRa Region…</p>
<div><table><thead><tr><th style="text-align: left">Value</th><th style="text-align: left">Region</th></tr></thead><tbody>
<tr><td style="text-align: left">0</td><td style="text-align: left">No region</td></tr>
<tr><td style="text-align: left">1</td><td style="text-align: left">AS band on 923MHz</td></tr>
<tr><td style="text-align: left">2</td><td style="text-align: left">Australian band on 915MHz</td></tr>
<tr><td style="text-align: left">3</td><td style="text-align: left">Chinese band on 470MHz</td></tr>
<tr><td style="text-align: left">4</td><td style="text-align: left">Chinese band on 779MHz</td></tr>
<tr><td style="text-align: left">5</td><td style="text-align: left">European band on 433MHz</td></tr>
<tr><td style="text-align: left">6</td><td style="text-align: left">European band on 868MHz</td></tr>
<tr><td style="text-align: left">7</td><td style="text-align: left">South Korean band on 920MHz</td></tr>
<tr><td style="text-align: left">8</td><td style="text-align: left">India band on 865MHz</td></tr>
<tr><td style="text-align: left">9</td><td style="text-align: left">North American band on 915MHz</td></tr>
<tr><td style="text-align: left">10</td><td style="text-align: left">North American band on 915MHz with a maximum of 16 channels</td></tr>
</tbody></table>
</div>
<p>The <strong>GPIO Pin Numbers</strong> for LoRa SX1262 are defined in…</p>
<div class="example-wrap"><pre class="language-text"><code>components/3rdparty/lora-sx1262/include/sx126x-board.h</code></pre></div>
<p>They have been configured for PineDio Stack. (So no changes needed)</p>
<p>Build the Firmware Binary File <code>pinedio_lorawan.bin</code></p>
<div class="example-wrap"><pre class="language-bash"><code>## TODO: Change this to the full path of bl_iot_sdk
export BL60X_SDK_PATH=$HOME/bl_iot_sdk
export CONFIG_CHIP_NAME=BL602
cd bl_iot_sdk/customer_app/pinedio_lorawan
make
## For WSL: Copy the firmware to /mnt/c/blflash, which refers to c:\blflash in Windows
mkdir /mnt/c/blflash
cp build_out/pinedio_lorawan.bin /mnt/c/blflash</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/pinecone#building-firmware">More details on building bl_iot_sdk</a></p>
<h2 id="flash-lorawan-firmware"><a class="doc-anchor" href="#flash-lorawan-firmware">§</a>11.2 Flash LoRaWAN Firmware</h2>
<p>Follow these steps to install <code>blflash</code></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 <code>pinedio_lorawan.bin</code> has been copied to the <code>blflash</code> 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 <code>pinedio_lorawan.bin</code> to BL602 / BL604 over UART…</p>
<div class="example-wrap"><pre class="language-bash"><code>## For Linux:
blflash flash build_out/pinedio_lorawan.bin \
--port /dev/ttyUSB0
## For macOS:
blflash flash build_out/pinedio_lorawan.bin \
--port /dev/tty.usbserial-1420 \
--initial-baud-rate 230400 \
--baud-rate 230400
## For Windows: Change COM5 to the BL602 / BL604 Serial Port
blflash flash c:\blflash\pinedio_lorawan.bin --port COM5</code></pre></div>
<p>(For WSL: Do this under plain old Windows CMD, not WSL, because blflash needs to access the COM port)</p>
<p><a href="https://lupyuen.github.io/articles/flash#flash-the-firmware">More details on flashing firmware</a></p>
<h2 id="run-lorawan-firmware"><a class="doc-anchor" href="#run-lorawan-firmware">§</a>11.3 Run LoRaWAN Firmware</h2>
<p>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><a href="https://lupyuen.github.io/articles/flash#watch-the-firmware-run">More details on connecting to BL602 / BL604</a></p>
<h2 id="enter-lorawan-commands"><a class="doc-anchor" href="#enter-lorawan-commands">§</a>11.4 Enter LoRaWAN Commands</h2>
<p>Lets enter the LoRaWAN Commands to join The Things Network and transmit a Data Packet!</p>
<ol>
<li>
<p>Log on to <strong>The Things Network</strong>. Browse to our Device and copy these values…</p>
<p><strong>JoinEUI</strong> (Join Extended Unique Identifier)</p>
<p><strong>DevEUI</strong> (Device Extended Unique Identifier)</p>
<p><strong>AppKey</strong> (Application Key)</p>
<p><a href="https://lupyuen.github.io/articles/ttn#join-device-to-the-things-network">(Instructions here)</a></p>
</li>
<li>
<p>In the BL602 / BL604 terminal, press Enter to reveal the command prompt.</p>
</li>
<li>
<p>First we start the <strong>Background Task</strong> that will handle LoRa packets…</p>
<p>Enter this command…</p>
<div class="example-wrap"><pre class="language-text"><code>create_task</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/lora2#event-queue">(<code>create_task</code> is explained here)</a></p>
</li>
<li>
<p>Next we initialise the <strong>LoRa SX1262 and LoRaWAN Drivers</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>init_lorawan</code></pre></div>
<p><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/pinedio_lorawan/pinedio_lorawan/lorawan.c#L168-L174">(<code>init_lorawan</code> is defined here)</a></p>
</li>
<li>
<p>Set the <strong>DevEUI</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>las_wr_dev_eui 0xAB:0xBA:0xDA:0xBA:0xAB:0xBA:0xDA:0xBA</code></pre></div>
<p>Change “<code>0xAB:0xBA:...</code>” to your <strong>DevEUI</strong></p>
<p>(Remember to change the <strong><code>,</code></strong> delimiter to <strong><code>:</code></strong>)</p>
</li>
<li>
<p>Set the <strong>JoinEUI</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>las_wr_app_eui 0x00:0x00:0x00:0x00:0x00:0x00:0x00:0x00</code></pre></div>
<p>Change “<code>0x00:0x00:...</code>” to your <strong>JoinEUI</strong></p>
<p>(Yep change the <strong><code>,</code></strong> delimiter to <strong><code>:</code></strong>)</p>
</li>
<li>
<p>Set the <strong>AppKey</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>las_wr_app_key 0xAB:0xBA:0xDA:0xBA:0xAB:0xBA:0xDA:0xBA0xAB:0xBA:0xDA:0xBA:0xAB:0xBA:0xDA:0xBA</code></pre></div>
<p>Change “<code>0xAB:0xBA:...</code>” to your <strong>AppKey</strong></p>
<p>(Again change <strong><code>,</code></strong> to <strong><code>:</code></strong>)</p>
</li>
<li>
<p>We send a request to <strong>join The Things Network</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>las_join 1</code></pre></div>
<p><code>1</code>” means try only once.</p>
<p><a href="https://lupyuen.github.io/articles/lorawan#join-network-request">(<code>las_join</code> is explained here)</a></p>
</li>
<li>
<p>We open an <strong>Application Port</strong> that will connect to The Things Network…</p>
<div class="example-wrap"><pre class="language-bash"><code>las_app_port open 2</code></pre></div>
<p><code>2</code>” is the Application Port Number</p>
<p><a href="https://lupyuen.github.io/articles/lorawan#open-lorawan-port">(<code>las_app_port</code> is explained here)</a></p>
</li>
<li>
<p>Finally we <strong>send a data packet to The Things Network</strong> over LoRaWAN…</p>
<div class="example-wrap"><pre class="language-bash"><code>las_app_tx_cbor 2 0 1234 2345</code></pre></div>
<p>This means…</p>
<ul>
<li>
<p>Transmit a LoRaWAN Packet to <strong>Port 2</strong></p>
</li>
<li>
<p>That contains the values <strong><code>t=1234</code></strong> (Temperature), <strong><code>l=2345</code></strong> (Light Level)</p>
</li>
<li>
<p><code>0</code> means that this is an <strong>Unconfirmed Message</strong></p>
<p>(Because were not expecting an acknowledgement)</p>
</li>
</ul>
<p>Our Sensor Data has been transmitted via LoRaWAN to The Things Network!</p>
<p><a href="https://youtu.be/BMMIIiZG6G0"><strong>Watch the demo video on YouTube</strong></a></p>
<p><a href="https://github.com/lupyuen/bl_iot_sdk/blob/master/customer_app/sdk_app_lorawan/README.md#output-log"><strong>See the output log</strong></a></p>
</li>
</ol>
<p>Check out this demo of PineDio Stack with Roblox and The Things Network…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/roblox#digital-twin-demo"><strong>“IoT Digital Twin with Roblox and The Things Network”</strong></a></li>
</ul>
<h1 id="appendix-porting-tinycbor-to-bl602"><a class="doc-anchor" href="#appendix-porting-tinycbor-to-bl602">§</a>12 Appendix: Porting TinyCBOR to BL602</h1>
<p>Below are the fixes we made while porting the TinyCBOR library to BL602 / BL604…</p>
<ul>
<li>
<p><a href="https://github.com/lupyuen/tinycbor-bl602/commit/971dca84b0b036a4ed44aa808e6eb18033161170">“Fix fall through”</a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/tinycbor-bl602/commit/c32bbc7696a54578f050467f1e182f4fd0f9bb9a">“Fix RetType, LenType”</a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/tinycbor-bl602/commit/65f857a3f2c8f0169ff215047fbcf7cd956eb55a">“Fix open_memstream”</a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/tinycbor-bl602/commit/0594d2f29646f65db22a60102d25c7aa675e9cae">“Dont use memstream”</a></p>
</li>
</ul>
<!-- 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>