<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Blues Blog - Wireless IoT for the Masses]]></title><description><![CDATA[Blues Blog - Wireless IoT for the Masses]]></description><link>https://blues.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 25 Sep 2026 11:07:42 GMT</lastBuildDate><atom:link href="https://blues.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Sending and Receiving Binary Files with the Notecard and Notehub]]></title><description><![CDATA[The Notecard was originally designed as a low-bandwidth and low-power device that effortlessly syncs user-defined JSON payloads (e.g. key/value pairs of strings, booleans, and integers) with any cloud endpoint. However, one of the most-repeated reque...]]></description><link>https://blues.hashnode.dev/sending-and-receiving-binary-files-with-the-notecard-and-notehub</link><guid isPermaLink="true">https://blues.hashnode.dev/sending-and-receiving-binary-files-with-the-notecard-and-notehub</guid><category><![CDATA[arduino]]></category><category><![CDATA[iot]]></category><category><![CDATA[C]]></category><dc:creator><![CDATA[Rob Lauer]]></dc:creator><pubDate>Tue, 28 Nov 2023 16:14:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1701187933999/db42cd16-adcc-47d4-9a50-0342704d34a2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Notecard was originally designed as a <a target="_blank" href="https://dev.blues.io/notecard/notecard-walkthrough/low-bandwidth-design/#low-bandwidth-design">low-bandwidth</a> and <a target="_blank" href="https://dev.blues.io/notecard/notecard-walkthrough/low-power-design/#low-power-design">low-power</a> device that effortlessly syncs user-defined JSON payloads (e.g. key/value pairs of strings, booleans, and integers) with any cloud endpoint. However, one of the most-repeated requests we heard from customers is the ability to <em>also</em> sync larger payloads of binary data (most often in the form of images).</p>
<p>With the release of the <a target="_blank" href="https://dev.blues.io/notecard/notecard-firmware-updates/#v5-3-1-september-18th-2023">Notecard developer firmware release v5.3.1</a>, this is now possible using the new <a target="_blank" href="https://dev.blues.io/api-reference/notecard-api/card-requests/#card-binary">card.binary APIs</a>. Even better, our firmware libraries for <a target="_blank" href="https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library/">Arduino/C</a> and <a target="_blank" href="https://dev.blues.io/tools-and-sdks/firmware-libraries/python-library/">Python</a> abstract away the complexity of the <code>card.binary</code> requests for you.</p>
<p>In this article, I'm going to show an example in Arduino/C of how to:</p>
<ol>
<li><p>Request a JPEG image from a cloud endpoint.</p>
</li>
<li><p>Write that JPEG image to a Micro SD card.</p>
</li>
<li><p>Send that same JPEG image back to a different cloud endpoint.</p>
</li>
</ol>
<h2 id="heading-hardware-setup">Hardware Setup</h2>
<p>For this example, I'm using the following hardware:</p>
<ul>
<li><p><a target="_blank" href="https://shop.blues.io/collections/swan/products/swan">Blues Swan</a> (STM32L4-based host MCU)</p>
</li>
<li><p><a target="_blank" href="https://shop.blues.io/collections/swan/products/stlink-v3mini">STLINK Programmer/Debugger</a> (optional, but makes working with STM32-based MCUs more pleasant)</p>
</li>
<li><p><a target="_blank" href="https://shop.blues.io/products/notecard">Blues Notecard Cellular</a> (any Notecard will do!)</p>
</li>
<li><p><a target="_blank" href="https://shop.blues.io/products/carr-al">Blues Notecarrier A</a> (a development board for interfacing with the Notecard)</p>
</li>
<li><p><a target="_blank" href="https://www.adafruit.com/product/254">Adafruit's Micro SD breakout board</a></p>
</li>
<li><p><a target="_blank" href="https://shop.blues.io/collections/accessories/products/5-000-mah-lipo-battery">2 x LiPo batteries</a></p>
</li>
</ul>
<h3 id="heading-wiring-the-micro-sd-breakout-to-the-swan">Wiring the Micro SD Breakout to the Swan</h3>
<p>First, you'll want to wire up the Swan to the Micro SD breakout. The following table outlines the wiring needed to connect the two:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>SD Breakout</td><td>Blues Swan</td></tr>
</thead>
<tbody>
<tr>
<td>GND</td><td>GND</td></tr>
<tr>
<td>3v</td><td>3V3</td></tr>
<tr>
<td>CLK</td><td>CK</td></tr>
<tr>
<td>DO</td><td>MI</td></tr>
<tr>
<td>DI</td><td>MO</td></tr>
<tr>
<td>CS</td><td>A3</td></tr>
</tbody>
</table>
</div><h3 id="heading-attaching-the-notecarrier-to-the-swan">Attaching the Notecarrier to the Swan</h3>
<p>Next, attach the Notecarrier A to the Swan via a Qwiic cable. Note that the Swan and Notecarrier A <em>also</em> need to be powered separately either over Micro USB or an attached LiPo battery, as Qwiic doesn't deliver enough power to boot the Notecard.</p>
<p>Your hardware should look something like the following (with or without the STLINK debugger of course):</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--eqRbDXBb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5954s20rudz3avygvv4b.jpg"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eqRbDXBb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5954s20rudz3avygvv4b.jpg" alt="wiring sd breakout to swan and notecarrier" /></a></p>
<h2 id="heading-format-the-micro-sd-card">Format the Micro SD Card</h2>
<p>If you haven't already, be sure the Micro SD card you're using is already formatted as either FAT16 (if &lt; 2GB) or FAT32. If not, you can use a USB SD card reader with your PC to format it using the SD Association's Memory Card Formatter (available on <a target="_blank" href="https://www.sdcard.org/downloads/formatter/">macOS/Win</a> and <a target="_blank" href="https://www.sdcard.org/downloads/sd-memory-card-formatter-for-linux/">Linux</a>).</p>
<p>Now it's time to write the sketch!</p>
<blockquote>
<p>Looking for a shortcut? <a target="_blank" href="https://gist.github.com/rdlauer/4f50bbb44a1b10100a0bc62b7cc820b2">Check out this gist</a> to see the completed sketch.</p>
</blockquote>
<h2 id="heading-setup-the-notecard-and-sd-card-breakout">Setup the Notecard and SD Card Breakout</h2>
<p>Include the necessary libraries in your project, including <a target="_blank" href="https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library/">note-arduino</a> for the Notecard and the <a target="_blank" href="https://github.com/arduino-libraries/SD">Arduino SD Library</a> for the Micro SD breakout board. Also, add the variables needed to interface with the components:</p>
<pre><code class="lang-bash"><span class="hljs-comment">#include &lt;Arduino.h&gt;</span>
<span class="hljs-comment">#include &lt;Wire.h&gt;</span>
<span class="hljs-comment">#include &lt;Notecard.h&gt;</span>
<span class="hljs-comment">#include &lt;SD.h&gt;</span>

Notecard notecard;
File myImageFile;
Sd2Card card;
SdVolume volume;
SdFile root;
const int chipSelect = A3;
HardwareSerial stlinkSerial(PIN_VCP_RX, PIN_VCP_TX);
<span class="hljs-comment">#define usbSerial Serial</span>

int ledPin = LED_BUILTIN; // references onboard LED on Swan
</code></pre>
<h2 id="heading-initialize-the-notecard-and-micro-sd-breakout">Initialize the Notecard and Micro SD Breakout</h2>
<p>Most everything else occurs in the <code>setup()</code> method.</p>
<p>Set the <code>pinMode</code> of the Swan's onboard LED to <code>OUTPUT</code> so we can blink it later. Also, if you're using the STLINK, be sure to initialize it so you can see output in the serial monitor.</p>
<pre><code class="lang-bash">pinMode(ledPin, OUTPUT);

Wire.begin();

// <span class="hljs-keyword">if</span> using STLINK debugger
stlinkSerial.begin(115200);
const size_t usb_timeout_ms = 5000;
<span class="hljs-keyword">for</span> (const size_t start_ms = millis(); !stlinkSerial &amp;&amp; (millis() - start_ms) &lt; usb_timeout_ms;)
  ;

notecard.setDebugOutputStream(stlinkSerial);
</code></pre>
<p>Next, initialize the Notecard, <strong>link your Notecard to your project in Notehub</strong> via the <code>product</code> key, and reset the Notecard's binary storage area to make sure we have the space required to save an image.</p>
<blockquote>
<p>If you haven't <a target="_blank" href="https://notehub.io/">set up a Notehub account yet</a>, you should, because it's free to use (up to 5,000 events routed per month!) and only takes an email address.</p>
</blockquote>
<pre><code class="lang-bash">notecard.begin();

J *req = notecard.newRequest(<span class="hljs-string">"hub.set"</span>);
JAddStringToObject(req, <span class="hljs-string">"product"</span>, <span class="hljs-string">"com.your.project:uid"</span>);
JAddStringToObject(req, <span class="hljs-string">"mode"</span>, <span class="hljs-string">"continuous"</span>);
JAddBoolToObject(req, <span class="hljs-string">"sync"</span>, <span class="hljs-literal">true</span>);
notecard.sendRequestWithRetry(req, 5);

// <span class="hljs-built_in">wait</span> until we are connected to notehub
// this is REQUIRED when sending binary data!
bool connected = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">while</span> (!connected)
{
  req = notecard.newRequest(<span class="hljs-string">"hub.status"</span>);
  J *rsp = notecard.requestAndResponse(req);
  connected = JGetBool(rsp, <span class="hljs-string">"connected"</span>);
  delay(2000);
}

// Reset the Notecard<span class="hljs-string">'s binary storage area, so we can start fresh
NoteBinaryStoreReset();</span>
</code></pre>
<p>Next, initialize the connected Micro SD card:</p>
<pre><code class="lang-bash">// init the SD card <span class="hljs-keyword">for</span> usage
stlinkSerial.println(<span class="hljs-string">"Initializing SD card..."</span>);

<span class="hljs-keyword">if</span> (!card.init(SPI_HALF_SPEED, chipSelect))
{
  stlinkSerial.println(<span class="hljs-string">"SD card initialization failed!"</span>);
  <span class="hljs-keyword">while</span> (1);
}

// try to open the <span class="hljs-string">'volume'</span>/<span class="hljs-string">'partition'</span> - it should be FAT16 or FAT32
<span class="hljs-keyword">if</span> (!volume.init(card))
{
  stlinkSerial.println(<span class="hljs-string">"Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card"</span>);
  <span class="hljs-keyword">while</span> (1);
}
</code></pre>
<p>That's it for our initialization tasks. Next up, it's time to request an image file!</p>
<h2 id="heading-request-an-image-from-the-cloud">Request an Image from the Cloud</h2>
<p>For security reasons, the Notecard cannot connect directly to any arbitrary cloud endpoint. It doesn't have a public IP address, nor would you want it to have one. This is where Notehub comes into play, as the Notecard and Notehub work together to form a secure "off the public Internet" link.</p>
<p>To request a binary file from a cloud end point, we'll have to set up a <a target="_blank" href="https://dev.blues.io/notehub/notehub-walkthrough/#routing-data-with-notehub">Notehub Route</a>.</p>
<blockquote>
<p>Routes allow you to send data from Notehub to a public cloud like AWS, Azure, or Google Cloud, a messaging platform like MQTT, or a custom HTTP/HTTPS endpoint. Routes are defined in a Notehub for a single project, and can target any number of Notecard fleets and devices.</p>
</blockquote>
<p>Let's say you have a cloud endpoint that will return binary data (i.e. an image file). Make sure you have the URL of the endpoint available.</p>
<p>In Notehub, navigate to the <strong>Routes</strong> menu option, and create a new <strong>Proxy for Notecard Web Requests</strong> route:</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--wn5HL1zM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwvwiqfrdad2fs2w106w.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wn5HL1zM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pwvwiqfrdad2fs2w106w.png" alt="create proxy route" /></a></p>
<p>Supply an arbitrary <strong>Route Name</strong> and the <strong>URL</strong> that Notehub should use to fetch the image with a <code>GET</code>. Also, you'll need to add an arbitrary <strong>Alias</strong> string, which is how you'll reference this Route in the sketch below.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--9VO28frJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00ri3d4tvyuqtythtwg1.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9VO28frJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00ri3d4tvyuqtythtwg1.png" alt="set up proxy route" /></a></p>
<p><strong>That's it!</strong></p>
<p>Back in your sketch, you'll use the <a target="_blank" href="https://dev.blues.io/api-reference/notecard-api/web-requests/#web-get">web.get API</a> to call the newly-created Notehub Route (called <code>GetImage</code>) and store whatever binary data is returned in flash on the Notecard:</p>
<pre><code class="lang-bash">J *req = NoteNewRequest(<span class="hljs-string">"web.get"</span>))
JAddStringToObject(req, <span class="hljs-string">"route"</span>, <span class="hljs-string">"GetImage"</span>);
JAddBoolToObject(req, <span class="hljs-string">"binary"</span>, <span class="hljs-literal">true</span>);

<span class="hljs-keyword">if</span> (!NoteRequest(req))
{
  notecard.logDebug(<span class="hljs-string">"Error receiving image\n"</span>);
}

uint32_t data_len = 0;
NoteBinaryStoreDecodedLength(&amp;data_len);

uint32_t rx_buffer_len = NoteBinaryCodecMaxEncodedLength(data_len);
uint8_t *rx_buffer = (uint8_t *)malloc(rx_buffer_len);

// Receive the data
NoteBinaryStoreReceive(reinterpret_cast&lt;uint8_t *&gt;(rx_buffer), rx_buffer_len, 0, data_len);
notecard.logDebugf(<span class="hljs-string">"\n[INFO] Received %d bytes.\n"</span>, data_len);
</code></pre>
<h2 id="heading-save-the-image-to-the-micro-sd-card">Save the Image to the Micro SD Card</h2>
<p>Next, you'll want to write the saved binary buffer from the Notecard to the Micro SD card, using methods provided by the Arduino library:</p>
<pre><code class="lang-bash">// save the buffer to the specified file name on the SD card
SD.begin(chipSelect);
SD.remove(<span class="hljs-string">"logo.jpg"</span>); // remove the file <span class="hljs-keyword">if</span> it already exists
myImageFile = SD.open(<span class="hljs-string">"logo.jpg"</span>, FILE_WRITE);

<span class="hljs-keyword">if</span> (myImageFile)
{
  myImageFile.write(rx_buffer, data_len);
  myImageFile.close();
  stlinkSerial.println(<span class="hljs-string">"Completed writing the file!\n"</span>);
}
</code></pre>
<p>You can now pull the SD card out of the breakout board and pop it into your PC to see if the file was saved correctly (in my case I downloaded the Blues logo of course).</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--Akq-ZL94--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rv7y36bn32mrtlf592ox.jpg"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Akq-ZL94--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rv7y36bn32mrtlf592ox.jpg" alt="blues logo" /></a></p>
<p><em>Yes you'll just have to take my word that it worked!</em></p>
<h2 id="heading-send-the-image-back-to-a-cloud-endpoint">Send the Image Back to a Cloud Endpoint</h2>
<p>So far you've downloaded a file and saved it to SD, but what about uploading it back to another cloud endpoint?</p>
<p>Welcome to the magic of Notehub Routes (yes, they work both ways)!</p>
<p>Back in Notehub, again navigate to the <strong>Routes</strong> menu option, and create a new <strong>General HTTP/HTTPS Request/Response</strong> route (this is different than the <strong>Proxy for Notecard Web Requests</strong> route type, as this time we will be generating a payload locally using <a target="_blank" href="https://dev.blues.io/api-reference/glossary/#note">Notes</a> to send data from the Notecard to Notehub).</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--V1D3mXEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yioq8d3df2v4gu0u4upx.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V1D3mXEp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yioq8d3df2v4gu0u4upx.png" alt="create https route" /></a></p>
<p>Provide an arbitrary Route Name and the remote URL that Notehub will use to <code>POST</code> the binary data. In my case, I used the venerable <a target="_blank" href="https://dev.blues.io/notecard/notecard-walkthrough/low-bandwidth-design/#low-bandwidth-design">webhook.site</a> for an easy way to inspect outbound data to the cloud.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--F1otauPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdq2wa1pdbmlj9w3j2r0.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F1otauPN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdq2wa1pdbmlj9w3j2r0.png" alt="configure https route" /></a></p>
<p>Continue to the <strong>Filters</strong> section and be sure that you are only routing data from the Notecard that appears in the <code>binary.qo</code> Notefile (this is the name of the Notefile that will contain the binary payload).</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pkp6Dq57--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8pomzbux56xc7y899le.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pkp6Dq57--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8pomzbux56xc7y899le.png" alt="configure https route" /></a></p>
<p><strong>All set?</strong> Back to your sketch!</p>
<pre><code class="lang-bash">const uint32_t notecard_binary_area_offset = 0;
NoteBinaryStoreTransmit(reinterpret_cast&lt;uint8_t *&gt;(rx_buffer), data_len, sizeof(rx_buffer), notecard_binary_area_offset);
notecard.logDebugf(<span class="hljs-string">"\n[INFO] Transmitted %d bytes.\n"</span>, data_len);

// Send the binary data to Notehub
<span class="hljs-keyword">if</span> (J *req = notecard.newRequest(<span class="hljs-string">"note.add"</span>))
{
  JAddStringToObject(req, <span class="hljs-string">"file"</span>, <span class="hljs-string">"binary.qo"</span>);
  JAddBoolToObject(req, <span class="hljs-string">"binary"</span>, <span class="hljs-literal">true</span>);
  JAddBoolToObject(req, <span class="hljs-string">"live"</span>, <span class="hljs-literal">true</span>);
  notecard.sendRequest(req);
}

// Free the receive buffer
free(rx_buffer);

stlinkSerial.println(<span class="hljs-string">"Completed sending the file!\n"</span>);
</code></pre>
<p>Believe it or not, that's all that is required. In this code block, you are using the saved binary buffer that's already stored in flash on the Notecard (the image file just downloaded previously) and sending it to Notehub using the <a target="_blank" href="https://dev.blues.io/api-reference/notecard-api/note-requests/#note-add">note.add API</a>. Note that you're also supplying the <code>binary:true</code> and <code>live:true</code> arguments of <code>note.add</code> in order to pass the binary data from the Notecard to Notehub!</p>
<h2 id="heading-viewing-the-binary-payload-in-the-cloud">Viewing the Binary Payload in the Cloud</h2>
<p>Depending on how you set up the Notehub Route that <code>POST</code>s data, you should be able to see the binary data delivered. As mentioned earlier, I used <a target="_blank" href="https://dev.blues.io/notecard/notecard-walkthrough/low-bandwidth-design/#low-bandwidth-design">webhook.site</a> which lets me inspect delivered payloads. Here is a snippet of the binary file I delivered:</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--sF7RSGoU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjymh4y1ruw55rwc513l.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sF7RSGoU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjymh4y1ruw55rwc513l.png" alt="webhook post results" /></a></p>
<h2 id="heading-wrapping-up-with-a-blink-and-a-loop">Wrapping Up with a Blink and a Loop</h2>
<p>Since I like to get some visual indication that my sketch has run to completion, in the <code>loop()</code> method, I simply flash the Swan's onboard LED on and off:</p>
<pre><code class="lang-bash">void <span class="hljs-function"><span class="hljs-title">loop</span></span>()
{
  // Finally, blink the Swan<span class="hljs-string">'s LED when done!
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
}</span>
</code></pre>
<blockquote>
<p>For a quick view of the full Arduino sketch, <a target="_blank" href="https://gist.github.com/rdlauer/4f50bbb44a1b10100a0bc62b7cc820b2">consult this gist</a>.</p>
</blockquote>
<h2 id="heading-next-steps">Next Steps</h2>
<p>What I've shown here is a relatively simple way of downloading and uploading binary data with the Notecard. More likely you may be dealing with larger binary files that have to be processed in "chunks" that the Notecard can handle. Be sure to consult our <a target="_blank" href="https://github.com/blues/note-arduino/tree/master/examples/Example9_BinarySendReceiveChunked">additional example Arduino sketch</a> that shows how to accomplish this more advanced scenario.</p>
<p>If you haven't already, <a target="_blank" href="https://shop.blues.io/collections/blues-starter-kits">grab your own Blues Starter Kit</a> and see how easy it really can be to sync data from a physical device to the cloud (over Cellular, Wi-Fi, or LoRa!).</p>
<p>Happy Hacking! 💙</p>
]]></content:encoded></item><item><title><![CDATA[The Easiest Way to Upgrade Raspberry Pi OS from Bullseye to Bookworm]]></title><description><![CDATA[If you're like me, when a new version of an OS comes out (I don't care of it's macOS, Windows, or Raspberry Pi OS) I feel an irrational need to install it. I'm also exceptionally lazy, so I usually opt for the upgrade path versus a clean install.

To...]]></description><link>https://blues.hashnode.dev/the-easiest-way-to-upgrade-raspberry-pi-os-from-bullseye-to-bookworm</link><guid isPermaLink="true">https://blues.hashnode.dev/the-easiest-way-to-upgrade-raspberry-pi-os-from-bullseye-to-bookworm</guid><category><![CDATA[Raspberry Pi]]></category><dc:creator><![CDATA[Rob Lauer]]></dc:creator><pubDate>Tue, 07 Nov 2023 17:29:37 GMT</pubDate><content:encoded><![CDATA[<p>If you're like me, when a new version of an OS comes out (I don't care of it's macOS, Windows, or Raspberry Pi OS) I feel an irrational need to install it. <strong>I'm also exceptionally lazy</strong>, so I usually opt for the upgrade path versus a clean install.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--S395nWT5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ixw4ld3u0psovgophz8z.gif"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S395nWT5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ixw4ld3u0psovgophz8z.gif" alt="lazy gif" /></a></p>
<p>Today I want to take a look at how you can update a Raspberry Pi (or compatible single board computer) to the newly released <a target="_blank" href="https://www.raspberrypi.com/news/bookworm-the-new-version-of-raspberry-pi-os/">"Bookworm" version of Raspberry Pi OS</a>.</p>
<blockquote>
<p>Let me be crystal clear: This is an <strong>unsupported upgrade path</strong> that is not endorsed by the Raspberry Pi Foundation.</p>
</blockquote>
<p>But I also like to live dangerously.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--AXTl53tO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/76ix13qynpsscp8nx3jk.gif"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AXTl53tO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/76ix13qynpsscp8nx3jk.gif" alt="austin powers gif" /></a></p>
<p>Before continuing, I'm sure at least some of you are wondering: <em>What exactly is Bookworm?</em></p>
<h2 id="heading-what-is-bookworm">What is Bookworm?</h2>
<p>Bookworm is the codename for a <a target="_blank" href="https://raspberrytips.com/raspberry-pi-os-versions/">new version of Raspberry Pi OS</a>. Based on Debian GNU/Linux 12, Bookworm is a significant new release for Raspberry Pi enthusiasts.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--N0Q1PLHU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aj52s1g111vxsy1qjwc9.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N0Q1PLHU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aj52s1g111vxsy1qjwc9.png" alt="raspberry pi bookworm desktop" /></a></p>
<p>Arguably the three most important updates in Bookworm are:</p>
<ol>
<li><p>A switch from the X11 Windowing System to <a target="_blank" href="https://wayland.freedesktop.org/">Wayland</a>, which improves security and performance (and includes some pleasing desktop UI updates).</p>
</li>
<li><p>Adopting <a target="_blank" href="https://pipewire.org/">PipeWire</a> as the default audio backend (replacing PulseAudio).</p>
</li>
<li><p>Use of <a target="_blank" href="https://networkmanager.dev/">NetworkManager</a> as the default network suite (replacing dhcpcd).</p>
</li>
</ol>
<h2 id="heading-bullseye-to-bookworm-upgrade-options">Bullseye to Bookworm Upgrade Options</h2>
<p>The <em>good news</em> is there are multiple upgrade options from Bullseye to Bookworm.</p>
<p>The <em>bad news</em> is there are multiple upgrade options from Bullseye to Bookworm.</p>
<ul>
<li><p><a target="_blank" href="https://dev.to/blues/the-easiest-way-to-upgrade-raspberry-pi-os-from-bullseye-to-bookworm-c3m#the-easy-upgrade-path-from-bullseye-to-bookworm">Easy Upgrade Path</a></p>
</li>
<li><p><a target="_blank" href="https://dev.to/blues/the-easiest-way-to-upgrade-raspberry-pi-os-from-bullseye-to-bookworm-c3m#a-better-way-to-upgrade-to-bookworm">Better Upgrade Path</a></p>
</li>
<li><p><a target="_blank" href="https://dev.to/blues/the-easiest-way-to-upgrade-raspberry-pi-os-from-bullseye-to-bookworm-c3m#the-best-way-to-upgrade-to-bookworm">Best "Upgrade" Path</a> (the officially recommended path, but that's not why you're here)</p>
</li>
</ul>
<blockquote>
<p><strong>WARNING:</strong> Regardless of the path you take, be sure to <strong>make a full backup</strong> of the SD card you're using for your current Raspberry Pi installation. Yes, just in case.</p>
</blockquote>
<h2 id="heading-the-easy-upgrade-path-from-bullseye-to-bookworm">The Easy Upgrade Path from Bullseye to Bookworm</h2>
<p>Sorry, one more warning:</p>
<blockquote>
<p><strong>WARNING:</strong> These instructions are <strong>only valid for upgrading from Bullseye</strong>. If you are currently running Buster, you'll have to install Bullseye first (but at that point, seriously, just install a fresh version of Bookworm and ignore the rest of this blog post, <a target="_blank" href="https://dev.to/blues/the-easiest-way-to-upgrade-raspberry-pi-os-from-bullseye-to-bookworm-c3m#easy-cellular-lora-and-wi-fi-on-the-pi">except for the ending</a>).</p>
</blockquote>
<p>Ready? Backup finished? Anxious to get started? <strong>Let's begin!</strong></p>
<ol>
<li>Perform a <strong>full upgrade</strong> of your existing Bullseye installation. Open a Terminal and enter these commands, sequentially:</li>
</ol>
<pre><code class="lang-bash">   sudo apt update
   sudo apt full-upgrade
   sudo reboot
</code></pre>
<ol>
<li>After rebooting, enter the following command to edit <code>sources.list</code>, which is how your Pi references the archives for the Debian repositories:</li>
</ol>
<pre><code class="lang-bash">   sudo nano /etc/apt/sources.list
</code></pre>
<ol>
<li>Update the first line, replacing <code>bullseye</code> with <code>bookworm</code> and adding <code>non-free-firmware</code> to the end of the line. Your <code>sources.list</code> file should end up looking something like this:</li>
</ol>
<pre><code class="lang-bash">   deb http://raspbian.raspberrypi.org/raspbian/ bookworm main contrib non-free rpi non-free-firmware
</code></pre>
<ol>
<li>Type <code>ctrl-x</code> to exit <code>nano</code>, making sure you save the file on exit. Next, you need to open <code>raspi.list</code> (to update the Debian Bookworm base) with this command:</li>
</ol>
<pre><code class="lang-bash">   sudo nano /etc/apt/sources.list.d/raspi.list
</code></pre>
<ol>
<li><p>In this file, you only need to change <code>bullseye</code> to <code>bookworm</code>. Again, hit <code>ctrl-x</code>, save your changes, and exit.</p>
</li>
<li><p>Perform <em>another</em> full upgrade, but this time sourcing from the Bookworm repositories!</p>
</li>
</ol>
<pre><code class="lang-bash">   sudo apt update
   sudo apt full-upgrade
   sudo reboot
</code></pre>
<ol>
<li>And now you wait. And wait some more.</li>
</ol>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--BCdGO2oK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjicvau0gxhayptjzn98.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BCdGO2oK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjicvau0gxhayptjzn98.png" alt="update raspberry pi" /></a></p>
<ol>
<li>After this final reboot, confirm that you're now running an <em>almost complete</em> version of Bookworm with this command:</li>
</ol>
<pre><code class="lang-bash">   cat /etc/os-release
</code></pre>
<p>Success! 🥳</p>
<p>However, while it <em>looks and feels</em> like this is a full upgrade to Bookworm, some smarter people than I have pointed out that changes like PipeWire and NetworkManager may not be properly configured via this route. That's what the "Better Way" is for:</p>
<h2 id="heading-a-better-way-to-upgrade-to-bookworm">A Better Way to Upgrade to Bookworm</h2>
<p>Yes, there is a better way. It's not for the faint of heart though.</p>
<p>A brave soul on GitHub is actively updating a series of commands for a <strong>full in-place upgrade</strong> from Bullseye to Bookworm <a target="_blank" href="https://gist.github.com/jauderho/6b7d42030e264a135450ecc0ba521bd8">in this gist</a>.</p>
<p>As of the publishing of this blog post in November 2023, here is the full list of commands. Godspeed.</p>
<pre><code class="lang-plaintext">### WARNING: READ CAREFULLY BEFORE ATTEMPTING ###
#
# Credit to anfractuosity and fgimenezm for figuring out additional details for kernels
#

# Make sure everything is up-to-date
sudo apt-get update &amp;&amp; sudo apt-get dist-upgrade

# Point to bookworm repos instead
sudo sed -i -e 's/bullseye/bookworm/g' /etc/apt/sources.list
sudo sed -i -e 's/bullseye/bookworm/g' /etc/apt/sources.list.d/raspi.list

# Do actual update
sudo apt update &amp;&amp; sudo apt -y full-upgrade &amp;&amp; sudo apt -y clean &amp;&amp; sudo apt -y autoremove

# Reboot
sudo reboot

# Remove old config files after doing sanity checks
sudo apt purge ?config-files

### Switch to the new kernels ###
# Prep
sudo dpkg --purge --force-depends raspberrypi-kernel raspberrypi-bootloader
sudo umount /boot
sudo fsck -y /boot
sudo mkdir /boot/firmware
sudo sed -i.bak -e "s#boot#boot/firmware#" /etc/fstab
sudo systemctl daemon-reload
sudo mount /boot/firmware
sudo apt install raspi-firmware

# Actually install the kernels. Make sure you pick the right version for your Pi
# sudo apt install linux-image-rpi-v8 linux-headers-rpi-v8      # 64bit
# sudo apt install linux-image-rpi-v7l linux-headers-rpi-v7l    # 32bit
# sudo apt install linux-image-rpi-v6 linux-headers-rpi-v6      

# Append auto_initramfs=1 to the bottom of file where it says [all]
sudo sed -i.bak '$ a\auto_initramfs=1' /boot/firmware/config.txt 

# Reboot
sudo reboot

# Verify using "uname -a" (correct as of 10/2023)
# Old kernel
# Linux raspberrypi 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023 aarch64 GNU/Linux
# New kernel
# Linux raspberrypi 6.1.0-rpi4-rpi-v8 #1 SMP PREEMPT Debian 1:6.1.54-1+rpt2 (2023-10-05) aarch64 GNU/Linux

# If you are not converted to using NetworkManager, you might lose networking upon reboot. 
# Check the ARP table to see what the new DHCP assigned IP address is. You may have to manually set the IP address again
# Thanks to solsticedhiver for identifying this
#
# Switch to NetworkManager from dhcpcd
sudo systemctl enable --now NetworkManager
sudo systemctl disable --now dhcpcd
#
# Set up static IP. Adjust as necessary 
sudo nmcli -p connection show 
sudo nmcli -p connection show "Wired connection 1"
sudo nmcli con mod "Wired connection 1" ipv4.method manual ipv4.addresses 192.168.1.5/24 ipv4.gateway 192.168.1.1

# Reboot
sudo reboot

#
# Bonus steps
#
# Install btop
sudo apt-get install btop
#
# Update /etc/ssh/sshd_config for up to date, secure by default config. Use ssh-audit to verify
KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org
HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ssh-ed25519
Ciphers chacha20-poly1305@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
</code></pre>
<h2 id="heading-the-best-way-to-upgrade-to-bookworm">The Best Way to "Upgrade" to Bookworm</h2>
<p>After all we just went through, do you really want to know what I think is the <strong>best way to upgrade to Bookworm</strong>?</p>
<p>Well, it's quite simple and foolproof:</p>
<ol>
<li><p>Backup all of the directories and files you want to keep from your Pi.</p>
</li>
<li><p>Download and install the <a target="_blank" href="https://www.raspberrypi.com/software/">Raspberry Pi Imager</a>.</p>
</li>
<li><p>Format an SD card and install a fresh copy of Bookworm.</p>
</li>
<li><p>Copy your files back to the Pi.</p>
</li>
<li><p>Success! 🥳</p>
</li>
</ol>
<h2 id="heading-easy-cellular-lora-and-wi-fi-on-the-pi">Easy Cellular, LoRa, and Wi-Fi on the Pi</h2>
<p>Now that you're done, if you're in the market for a new way to add <strong>swappable Cellular, LoRa, and Wi-Fi to your Raspberry Pi</strong>, take a look at the <a target="_blank" href="https://shop.blues.io/collections/notecard">Blues Notecard</a> and the <a target="_blank" href="https://shop.blues.io/products/carr-pi">Blues Notecarrier Pi HAT</a>.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--jZU1NMky--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/blues-wireless/image/fetch/f_auto%2Cc_limit%2Cw_1920%2Cq_auto/https://dev.blues.io/_next/static/media/rpi-and-notecarrier.88be48ad.webp"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jZU1NMky--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/blues-wireless/image/fetch/f_auto%2Cc_limit%2Cw_1920%2Cq_auto/https://dev.blues.io/_next/static/media/rpi-and-notecarrier.88be48ad.webp" alt="raspberry pi and blues notecard" /></a></p>
<p>The cellular-based Notecards provide <strong>500MB of prepaid data and 10 years of global cellular service</strong>. These are commonly used in edge deployments of Raspberry Pis that don't have access to reliable Wi-Fi connectivity.</p>
<p>Learn more about adding low-bandwidth cellular to the Raspberry Pi in <a target="_blank" href="https://www.hackster.io/blues-wireless/projects">many of these project tutorials on Hackster</a>.</p>
<p>Happy Hacking! 💙</p>
]]></content:encoded></item><item><title><![CDATA[Read and Write Images and Text Files with a Micro SD Card and Arduino]]></title><description><![CDATA[We've all been there. Well, at least I've certainly been there:
I want to perform what I assume to be a simple task with C (Arduino): "I just want to read and write some data to a Micro SD card! This can't be that difficult can it!?"
Five searches de...]]></description><link>https://blues.hashnode.dev/read-and-write-images-and-text-files-with-a-micro-sd-card-and-arduino</link><guid isPermaLink="true">https://blues.hashnode.dev/read-and-write-images-and-text-files-with-a-micro-sd-card-and-arduino</guid><category><![CDATA[iot]]></category><category><![CDATA[arduino]]></category><dc:creator><![CDATA[Rob Lauer]]></dc:creator><pubDate>Tue, 24 Oct 2023 18:23:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698171705170/32180756-7e13-4995-a9f9-67c09a5cba9e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We've all been there. Well, at least <em>I've</em> certainly been there:</p>
<p>I want to perform what I assume to be a simple task with C (Arduino): <em>"I just want to read and write some data to a Micro SD card! This can't be that difficult can it!?"</em></p>
<p>Five searches deep on Google, I realize there are far too many ways to skin this particular cat. Different components, different libraries, and even different libraries for the same components.</p>
<p>Time to take a deep breath and channel my internal Bob Ross...</p>
<p>GIF</p>
<p><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z3Or3lNv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e8r748ek2y5c2apq6pdk.gif" alt="bob ross" /></p>
<p>This article is meant cut out the extraneous info and provide a guide for what I consider to be the easiest way to use a Micro SD card with Arduino to read/write text and image files.</p>
<p>In this mini-project, I'm using the following hardware:</p>
<ol>
<li><p><a target="_blank" href="https://shop.blues.io/collections/swan/products/swan">STM32-based Blues Swan</a>.</p>
</li>
<li><p><a target="_blank" href="https://www.adafruit.com/product/254">Adafruit Micro SD breakout board</a>.</p>
</li>
<li><p><a target="_blank" href="https://www.amazon.com/SanDisk-Ultra-microSDXC-Memory-Adapter/dp/B073JWXGNT">SanDisk 32GB Micro SD card</a>.</p>
</li>
</ol>
<h2 id="heading-hardware-wiring">Hardware Wiring</h2>
<p>Since you're are only wiring a couple of components together this is a relatively simple step, but it's important to get the wiring correct:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>SD Breakout</td><td>Blues Swan</td></tr>
</thead>
<tbody>
<tr>
<td>GND</td><td>GND</td></tr>
<tr>
<td>3v</td><td>3V3</td></tr>
<tr>
<td>CLK</td><td>CK</td></tr>
<tr>
<td>DO</td><td>MI</td></tr>
<tr>
<td>DI</td><td>MO</td></tr>
<tr>
<td>CS</td><td>A3</td></tr>
</tbody>
</table>
</div><p>Your wiring should end up looking like something like this:</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--KfrCcupt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7twzz4pdgs9k21ugf6zn.jpg"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KfrCcupt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7twzz4pdgs9k21ugf6zn.jpg" alt="wiring blues swan to sd breakout board" /></a></p>
<h2 id="heading-prepping-the-micro-sd-card">Prepping the Micro SD Card</h2>
<p>You'll want to make sure the Micro SD card is formatted as either FAT16 (if &lt;= 2GB) or FAT32. If not, simply get ahold of a USB SD card reader and use your PC to format it using the SD Association's Memory Card Formatter (<a target="_blank" href="https://www.sdcard.org/downloads/formatter/">macOS/Win</a> and <a target="_blank" href="https://www.sdcard.org/downloads/sd-memory-card-formatter-for-linux/">Linux</a>).</p>
<p>Slide that Micro SD card back in the breakout board and we are ready to write some code.</p>
<h2 id="heading-arduino-micro-sd-breakout-library">Arduino Micro SD Breakout Library</h2>
<p>One of the reasons I chose <a target="_blank" href="https://www.adafruit.com/product/254">Adafruit's Micro SD breakout board</a> (aside from the generally high quality of their hardware) is because they offer well-supported and well-documented libraries. This one is no exception.</p>
<blockquote>
<p>Consult <a target="_blank" href="https://github.com/arduino-libraries/SD">this GitHub repository</a> for the latest version of the Arduino SD Library.</p>
</blockquote>
<p>Let's look at how we might perform some typical file-based tasks with an SD card, in particular:</p>
<ol>
<li><p>Writing and reading text files.</p>
</li>
<li><p>Writing and reading images (PNGs).</p>
</li>
</ol>
<h2 id="heading-writing-a-text-file">Writing a Text File</h2>
<p>Using a Micro SD card as a data logger is very common in our industry. They are inexpensive and a relatively stable medium.</p>
<blockquote>
<p>You didn't ask, but if you want my opinion, your data should only be saved to SD as a backup. I'll always recommend you sync data with the cloud using the <a target="_blank" href="https://shop.blues.io/collections/notecard">Cellular, Wi-Fi, or LoRa-based Notecard</a>, but I digress...</p>
</blockquote>
<p>The following sketch will initialize the SD card breakout board and write a string to a text file:</p>
<pre><code class="lang-plaintext">#include &lt;SD.h&gt;

File textFile;
Sd2Card card;
SdVolume volume;
const int chipSelect = A3;

void setup()
{
  Serial.begin(9600);

  Serial.println("Starting SD card init...");

  if (!card.init(SPI_HALF_SPEED, chipSelect))
  {
    Serial.println("SD card initialization failed!");
    while (1)
      ;
  }

  // try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card))
  {
    Serial.println("Could not find FAT16/FAT32 partition.");
    while (1)
      ;
  }

  SD.begin(chipSelect);

  Serial.println("SD initialization complete!");

  // open a new file for writing
  textFile = SD.open("hello.txt", FILE_WRITE);

  if (textFile) {
    textFile.println("Hello World!");
    textFile.close();
    Serial.println("Completed writing a text file!");
  }
}

void loop()
{
}
</code></pre>
<p>Note that there are a few things to be aware of when working with files:</p>
<ol>
<li><p>You'll want to keep filenames short and use the 8.3 format (e.g. <code>MYFILE.TXT</code>).</p>
</li>
<li><p>Filenames are NOT case sensitive, so in code, <code>myfile.txt</code> == <code>MYFILE.TXT</code>.</p>
</li>
<li><p>Always <code>close()</code> your files when done to make sure any pending writes are properly saved.</p>
</li>
</ol>
<h2 id="heading-reading-from-a-text-file">Reading from a Text File</h2>
<p>Now that you've written a simple text file to a Micro SD card, let's see how easy it can be to <strong>read data from an existing file</strong> (specifically the <code>hello.txt</code> file we just created).</p>
<p>Add the following code snippet <strong>after the file is created</strong>:</p>
<pre><code class="lang-plaintext">// open the existing file for reading
textFile = SD.open("hello.txt");
if (textFile)
{
  Serial.println("here's the data in hello.txt:");

  // read until there's nothing else in it:
  while (textFile.available())
  {
    Serial.write(textFile.read());
  }
  // close the file:
  textFile.close();
}
</code></pre>
<p>If you're using the serial monitor to see the output, you may be surprised to see "Hello World!" repeated many times. What's up with that?</p>
<pre><code class="lang-plaintext">Starting SD card init...
SD initialization complete!
Completed writing a text file!
here's the data in hello.txt:
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
</code></pre>
<p>This is because writing to an existing file <em>appends data</em> by default and doesn't overwrite existing data. In my case, I had clearly tested the write sequence a handful of times <strong>before</strong> I read the data out.</p>
<p>To fix this, you can always <code>remove()</code> the file first:</p>
<pre><code class="lang-plaintext">SD.remove("hello.txt");
</code></pre>
<h3 id="heading-other-useful-functions-when-working-with-files-on-sd">Other Useful Functions When Working with Files on SD</h3>
<p>Before writing to a file, you might want to see if it already exists by using the <code>SD.exists("thefile.txt")</code> function.</p>
<p>Create directories and subdirectories with the <code>SD.mkdir("/newdir")</code> function.</p>
<p>Use the aforementioned <code>SD.remove("thefile.txt")</code> function to delete files.</p>
<p>Be sure to consult <a target="_blank" href="https://learn.adafruit.com/adafruit-micro-sd-breakout-board-card-tutorial">Adafruit's full tutorial</a> for additional information on using this breakout board with a Micro SD card.</p>
<h2 id="heading-working-with-images">Working with Images</h2>
<p>Let's switch gears and ramp up the complexity a little bit by writing and reading images to/from the Micro SD card.</p>
<p>As with every complex Arduino task, we first need to ask: <strong>"Is there library for this?"</strong>. The answer is almost always "yes", and this time is no exception.</p>
<p>When working with image files on host microcontrollers with a relatively small amount of memory and processing power, we usually want to try to work with small files. Bitmap (.bmp) is a good format for lossless, but simple, images. Modern hosts are more often dealing with higher fidelity images that can't reasonably be sent uncompressed over the wire. This is where the usage of a format like JPEG or PNG come in handy.</p>
<p>Choosing the <strong>optimal type of compression to use will depend on the image itself</strong> (e.g. photos usually compress best with JPEG while drawings/line art are best compressed with PNG or WEBP).</p>
<p><em>Example of thermal camera images saved as BMP, PNG, and JPG:</em></p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--UxT0EwTp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/piz1yhybeoraum19104k.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UxT0EwTp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/piz1yhybeoraum19104k.png" alt="comparison of png to bmp to jpeg" /></a></p>
<p>And yes there are libraries for these options as well! <a target="_blank" href="https://github.com/bitbank2/JPEGenc">JPGenc</a> and <a target="_blank" href="https://github.com/bitbank2/JPEGdec">JPGdec</a> for encoding/decoding JPG images, and <a target="_blank" href="https://github.com/bitbank2/PNGenc">PNGenc</a> and <a target="_blank" href="https://github.com/bitbank2/PNGdec">PNGdec</a> for encoding/decoding PNGs.</p>
<blockquote>
<p>Looking for a functional example of image compression in action? <a target="_blank" href="https://www.hackster.io/rob-lauer/id-chicken-eggs-with-thermal-images-ml-and-cellular-iot-748c88">Check out this project on Hackster</a> that uses a thermal camera to capture images, show them on a TFT display, and save them to a Micro SD card.</p>
</blockquote>
<h2 id="heading-writing-an-image-to-a-micro-sd-card">Writing an Image to a Micro SD Card</h2>
<p>While this is a bit of a contrived example, let's strip down a code example to its bare minimum and look at how you might draw a basic image and save it to the SD card.</p>
<p>The following sketch creates a 128x128 image with a green border and green "x" going through it, like so, using the <code>PNGenc</code> library:</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--lZ69KG9H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/18ostp7z2ady52zvrlsl.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lZ69KG9H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/18ostp7z2ady52zvrlsl.png" alt="test image" /></a></p>
<pre><code class="lang-plaintext">#include &lt;PNGenc.h&gt;
#include &lt;SD.h&gt;

PNG png;
File myPNG;
Sd2Card card;
SdVolume volume;
const int chipSelect = A3;

void *myOpen(const char *filename)
{
    Serial.printf("Attempting to open %s\n", filename);
    myPNG = SD.open(filename, O_READ | O_WRITE | O_CREAT);
    return &amp;myPNG;
}
void myClose(PNGFILE *handle)
{
    File *f = (File *)handle-&gt;fHandle;
    f-&gt;close();
}
int32_t myRead(PNGFILE *handle, uint8_t *buffer, int32_t length)
{
    File *f = (File *)handle-&gt;fHandle;
    return f-&gt;read(buffer, length);
}
int32_t myWrite(PNGFILE *handle, uint8_t *buffer, int32_t length)
{
    File *f = (File *)handle-&gt;fHandle;
    return f-&gt;write(buffer, length);
}
int32_t mySeek(PNGFILE *handle, int32_t position)
{
    File *f = (File *)handle-&gt;fHandle;
    return f-&gt;seek(position);
}

#define WIDTH 128
#define HEIGHT 128

uint8_t ucPal[768] = {0, 0, 0, 0, 255, 0}; // black, green
uint8_t ucAlphaPal[256] = {0, 255};        // first color (black) is fully transparent

void setup()
{
    int rc, iDataSize, x, y;
    uint8_t ucLine[WIDTH];
    long l;

    Serial.begin(115200);

    Serial.println("Starting SD card init...");

    if (!card.init(SPI_HALF_SPEED, chipSelect))
    {
        Serial.println("SD card initialization failed!");
        while (1)
            ;
    }

    // try to open the 'volume'/'partition' - it should be FAT16 or FAT32
    if (!volume.init(card))
    {
        Serial.println("Could not find FAT16/FAT32 partition.");
        while (1)
            ;
    }

    SD.begin(chipSelect);

    Serial.println("SD initialization complete!");

    l = micros();

    rc = png.open("/testimg.png", myOpen, myClose, myRead, myWrite, mySeek);

    if (rc == PNG_SUCCESS)
    {
        rc = png.encodeBegin(WIDTH, HEIGHT, PNG_PIXEL_INDEXED, 8, ucPal, 3);
        png.setAlphaPalette(ucAlphaPal);
        if (rc == PNG_SUCCESS)
        {
            for (int y = 0; y &lt; HEIGHT &amp;&amp; rc == PNG_SUCCESS; y++)
            {
                // prepare a line of image to create a red box with an x on a transparent background
                if (y == 0 || y == HEIGHT - 1)
                {
                    memset(ucLine, 1, WIDTH); // top+bottom green lines
                }
                else
                {
                    memset(ucLine, 0, WIDTH);
                    ucLine[0] = ucLine[WIDTH - 1] = 1;     // left/right border
                    ucLine[y] = ucLine[WIDTH - 1 - y] = 1; // X in the middle
                }
                rc = png.addLine(ucLine);
            } // for y
            iDataSize = png.close();
            l = micros() - l;
            Serial.printf("%d bytes of data written to file in %d us\n", iDataSize, (int)l);
        }
    }
}

void loop()
{
}
</code></pre>
<p><em>This is a lightly-edited version of the</em> <a target="_blank" href="https://github.com/bitbank2/PNGenc/blob/master/examples/PNGenc_Test/PNGenc_Test.ino"><em>example sketch from the PNGenc</em></a> <em>repository.</em></p>
<blockquote>
<p>The devil is always in the details when dealing with images, as every project has differing requirements. You likely either have images pre-loaded on an SD card or you are capturing new images with a camera. If using a camera, you'll need a host MCU with enough memory to encode/compress/save the image AND you'll have to find a library specific to the camera you are using.</p>
</blockquote>
<h2 id="heading-reading-an-image-from-an-sd-card">Reading an Image from an SD Card</h2>
<p>Now that we can write a simple PNG image, let's see how we might <strong>read PNGs</strong> using the <code>PNGdec</code> library.</p>
<blockquote>
<p>Your mileage may vary, but at the time of this writing, both <code>PNGenc</code> and <code>PNGdec</code> cannot be used at the same time in the same project.</p>
</blockquote>
<p>The following sketch will <strong>read all PNG files from the root directory</strong> of an SD card and load them, one at a time, into memory. Again, this is not very functional because it doesn't actually "do" anything with the images, but it's a good first step:</p>
<pre><code class="lang-plaintext">#include &lt;PNGdec.h&gt;
#include &lt;SD.h&gt;

PNG png;
File myPNG;
Sd2Card card;
SdVolume volume;
const int chipSelect = A3;

void setup()
{

    Serial.begin(115200);

    Serial.println("Starting SD card init...");

    if (!card.init(SPI_HALF_SPEED, chipSelect))
    {
        Serial.println("SD card initialization failed!");
        while (1)
            ;
    }

    // try to open the 'volume'/'partition' - it should be FAT16 or FAT32
    if (!volume.init(card))
    {
        Serial.println("Could not find FAT16/FAT32 partition.");
        while (1)
            ;
    }

    SD.begin(chipSelect);

    Serial.println("SD initialization complete!");
}

void *myOpen(const char *filename, int32_t *size)
{
    Serial.printf("Attempting to open %s\n", filename);
    myPNG = SD.open(filename);
    *size = myPNG.size();
    return &amp;myPNG;
}
void myClose(void *handle)
{
    if (myPNG)
        myPNG.close();
}
int32_t myRead(PNGFILE *handle, uint8_t *buffer, int32_t length)
{
    if (!myPNG)
        return 0;
    return myPNG.read(buffer, length);
}
int32_t mySeek(PNGFILE *handle, int32_t position)
{
    if (!myPNG)
        return 0;
    return myPNG.seek(position);
}

// Function to draw pixels to the display
void PNGDraw(PNGDRAW *pDraw)
{
    uint16_t usPixels[320];

    png.getLineAsRGB565(pDraw, usPixels, PNG_RGB565_LITTLE_ENDIAN, 0xffffffff);
}

// Main loop, scan for all .PNG files on the card and display them
void loop()
{
    int rc, filecount = 0;
    File dir = SD.open("/");
    while (true)
    {
        File entry = dir.openNextFile();
        if (!entry)
            break;
        if (entry.isDirectory() == false)
        {
            const char *name = entry.name();
            const int len = strlen(name);
            if (len &gt; 3 &amp;&amp; strcmp(name + len - 3, "PNG") == 0)
            {
                Serial.print("File: ");
                Serial.println(name);
                rc = png.open((const char *)name, myOpen, myClose, myRead, mySeek, PNGDraw);
                if (rc == PNG_SUCCESS)
                {
                    Serial.printf("image specs: (%d x %d), %d bpp, pixel type: %d\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());
                    rc = png.decode(NULL, 0);
                    // do something...anything..with the PNG file!
                    png.close();
                }
            }
        }
        entry.close();
    }
}
</code></pre>
<p>And what does the output look like in the serial monitor?</p>
<pre><code class="lang-plaintext">File: TESTIMG.PNG
Attempting to open TESTIMG.PNG
image specs: (128 x 128), 8 bpp, pixel type: 3
</code></pre>
<p>Success! 🥳</p>
<h2 id="heading-summary">Summary</h2>
<p>Hopefully you've seen how easy your first steps can be when using a Micro SD card to read and write images and text files with Arduino/C.</p>
<p>As mentioned above, every project scenario is different (whether you're just reading images pre-loaded to the SD card, <a target="_blank" href="https://www.hackster.io/rob-lauer/id-chicken-eggs-with-thermal-images-ml-and-cellular-iot-748c88">creating PNGs from a thermal camera module</a>, or most likely taking a picture with an attached camera module and storing them as JPEGs).</p>
<p>Including a Micro SD card as a data logger is a simple and effective way to store data (which <strong>of course</strong> you'll later end up syncing to the cloud via <a target="_blank" href="https://shop.blues.io/collections/notecard">Cellular, Wi-Fi, or LoRa using the Notecard</a>!).</p>
<p>Happy Hacking! 💙</p>
]]></content:encoded></item><item><title><![CDATA[When to Use Tadiran (Li-SOCl2) vs Lithium Polymer (LiPo) Batteries in IoT Applications]]></title><description><![CDATA[As a product designer or embedded engineer, selecting the appropriate battery technology is crucial for ensuring the best performance/longevity combination to power your embedded device or IoT application.
In this article, we will perform a high-leve...]]></description><link>https://blues.hashnode.dev/when-to-use-tadiran-li-socl2-vs-lithium-polymer-lipo-batteries-in-iot-applications</link><guid isPermaLink="true">https://blues.hashnode.dev/when-to-use-tadiran-li-socl2-vs-lithium-polymer-lipo-batteries-in-iot-applications</guid><category><![CDATA[iot]]></category><category><![CDATA[battery]]></category><dc:creator><![CDATA[Rob Lauer]]></dc:creator><pubDate>Tue, 26 Sep 2023 15:03:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1695740522429/be94574c-6502-4fae-bd8b-7985f83133d4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a product designer or embedded engineer, selecting the appropriate battery technology is crucial for ensuring the best performance/longevity combination to power your embedded device or IoT application.</p>
<p>In this article, we will perform a high-level comparison of Tadiran (i.e. Li-SOCl<sub>2</sub>) batteries and Lithium Polymer (LiPo) batteries, helping you to decide which is best suited for your deployment scenario.</p>
<p><em>Specifically, we will answer the following questions:</em></p>
<ul>
<li><p>What are Tadiran Li-SOCl<sub>2</sub> batteries?</p>
</li>
<li><p>What are LiPo batteries?</p>
</li>
<li><p>What are the optimal temperature ranges for Tadiran and LiPo batteries?</p>
</li>
<li><p>What are the self-discharge rates of Tadiran and LiPo batteries?</p>
</li>
<li><p>What are some valid use cases for Tadiran vs LiPo batteries?</p>
</li>
<li><p>And why aren't we talking about alkaline batteries!?</p>
</li>
</ul>
<h2 id="heading-what-is-a-tadiran-battery">What is a Tadiran Battery?</h2>
<p><a target="_blank" href="https://tadiranbat.com/">Tadiran</a> is the brand name of a type of Lithium Thionyl Chloride (Li-SOCl<sub>2</sub>) battery. Tadiran Li-SOCl<sub>2</sub> batteries are known for their <strong>long shelf-life</strong> (up to 40 years in optimal conditions), relatively <strong>high voltage</strong> (3.6V), <strong>low self-discharge rates</strong> (~0.7% per year), and the ability to operate in <strong>extreme temperature ranges</strong> (-80°C to 125°C).</p>
<p><em>Yes, Tadiran likes to acknowledge these "extreme" capabilities in their marketing materials!</em></p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--bbCer3uI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufyd9wrli7ngnn4dtvnn.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bbCer3uI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ufyd9wrli7ngnn4dtvnn.png" alt="tadiran extreme conditions" /></a></p>
<blockquote>
<p>NOTE: While known for their Li-SOCl<sub>2</sub> batteries, Tadiran does offer Li-ion batteries as well. However, for the purposes of this article we are going to focus only on their Li-SOCl<sub>2</sub> batteries.</p>
</blockquote>
<p>While Li-SOCl<sub>2</sub> Tadirans are <strong>not rechargeable</strong>, they are often used in low-power, long-life applications such as remote monitoring systems, smart meters, asset tracking, and IoT deployments in relatively harsh environmental conditions.</p>
<h3 id="heading-how-do-li-socl2-tadiran-batteries-work">How do Li-SOCl<sub>2</sub> Tadiran Batteries Work?</h3>
<p>Li-SOCl<sub>2</sub> battery technology takes advantage of the high reactivity of lithium, the lightest of all metals, and a highly electronegative chlorinated compound, thionyl chloride (SOCl2).</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--hR3RqOir--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vxn7xcsf9b0r854op0o.jpg"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hR3RqOir--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vxn7xcsf9b0r854op0o.jpg" alt="li-socl2 battery structure" /></a></p>
<p><em>Two types of Li-SOCl<sub>2</sub> battery structures. Image credit</em> <a target="_blank" href="http://gebc-energy.com"><em>gebc-energy.com</em></a><em>.</em></p>
<p>In these batteries, lithium serves as the anode, and thionyl chloride serves as the cathode active material. When the battery discharges, the lithium is oxidized, and the thionyl chloride is reduced. The resulting reaction generates a high nominal voltage (again, typically around 3.6V).</p>
<h3 id="heading-pros-and-cons-of-li-socl2-tadiran-batteries">Pros and Cons of Li-SOCl<sub>2</sub> Tadiran Batteries</h3>
<p>Li-SOCl<sub>2</sub> Tadirans shine in scenarios where a long shelf-life, low self-discharge rate, high pulses, and ability to function in wildly varying temperature ranges are key requirements. Their high cell voltage also may allow <strong>fewer batteries to be used in a series</strong>, potentially saving space and reducing complexity in the design of device enclosures.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--M5HAMSSt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1d000rf0t2fa63x8aimr.jpg"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M5HAMSSt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1d000rf0t2fa63x8aimr.jpg" alt="tadiran plusespulse series" /></a></p>
<p><em>The Tadiran PlusesPulse series of Li-SOCl<sub>2</sub> batteries.</em></p>
<p>However, Li-SOCl<sub>2</sub> batteries are not without their limitations. Since they are not rechargeable, once they are depleted they must be manually replaced. Therefore, they aren't suitable for high-power applications that discharge rapidly, nor in remote scenarios where solar/wind/hydro power is readily available for recharging purposes. Likewise, Tadirans are not inexpensive! Li-SOCl<sub>2</sub> Tadiran batteries often cost considerably more than LiPo batteries when comparing $ per amp hour (though the total cost of ownership may vary, depending on the use case).</p>
<h3 id="heading-common-iot-applications-for-li-socl2-tadiran-batteries">Common IoT Applications for Li-SOCl<sub>2</sub> Tadiran Batteries</h3>
<p>Thanks to their unique properties, Li-SOCl<sub>2</sub> Tadiran batteries are often found in a variety of IoT deployments, including:</p>
<ul>
<li><p><strong>Utility Metering Systems:</strong> Gas, water, and electricity metering systems often use Tadirans due to their long operational lifespan and reliability.</p>
</li>
<li><p><strong>Emergency Devices:</strong> Devices like fire/smoke detectors and alarm systems often use these batteries due to their long shelf-life.</p>
</li>
<li><p><strong>Edge Computing Devices:</strong> Devices deployed in remote or difficult-to-access locations may want to use Tadrian batteries due to their high energy density and long operational life.</p>
</li>
</ul>
<p>When it comes to integrating with the <a target="_blank" href="https://blues.io/products/notecard/">Blues Notecard</a>, <a target="_blank" href="https://shop.blues.io/collections/accessories/products/tadiran-lithium-battery">this specific Tadiran</a> is ideal for the high current pulses the Notecard requires when used in a region that requires access to a GSM network:</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--QyRqaGcL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rw7yytit7gx3w67btzi6.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QyRqaGcL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rw7yytit7gx3w67btzi6.png" alt="tadiran blues" /></a></p>
<p><em>The Blues version of the Tadiran TLP-93111 battery.</em></p>
<blockquote>
<p><strong>NOTE:</strong> This battery is based on the Tadiran TLP-93111 and was modified for Blues to include a JST connector so that you can easily use this battery with any of our <a target="_blank" href="https://blues.io/products/notecarrier/">Notecarriers</a>.</p>
</blockquote>
<h2 id="heading-what-is-a-lithium-polymer-lipo-battery">What is a Lithium Polymer (LiPo) Battery?</h2>
<p>Lithium Polymer (LiPo) batteries are a type of rechargeable battery that utilize a <em>polymer electrolyte</em> instead of a <em>liquid electrolyte</em> found in other lithium-ion batteries. This unique composition allows LiPo batteries to be <strong>lightweight and flexible</strong>, making them a popular choice for low-cost powering of embedded/IoT devices, consumer electronics, and devices that require custom battery sizes.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--zuAafjN7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kk14q7u3hrqvszzxh6b9.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zuAafjN7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kk14q7u3hrqvszzxh6b9.png" alt="example lipo battery" /></a></p>
<p><em>An example LiPo battery.</em></p>
<h3 id="heading-how-do-lipo-batteries-work">How do LiPo Batteries Work?</h3>
<p>LiPo batteries work on the same fundamental principles as other lithium-ion batteries. They move lithium ions from the negative electrode (anode) to the positive electrode (cathode) during discharge - and move them back while charging. The anode in a LiPo battery is typically made of a type of lithium compound (e.g. lithium cobalt oxide), which can readily give up lithium ions.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--USG9Qsg4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/smjhqkewak4fj0fgzfse.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--USG9Qsg4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/smjhqkewak4fj0fgzfse.png" alt="lipo battery structure" /></a></p>
<p><em>LiPo battery structure. Image credit</em> <a target="_blank" href="http://dnkpower.com"><em>dnkpower.com</em></a><em>.</em></p>
<h3 id="heading-pros-and-cons-of-lipo-batteries">Pros and Cons of LiPo Batteries</h3>
<p>Like other lithium-ion batteries, LiPo batteries have a <strong>high energy density</strong>, meaning they can store a large amount of energy for their size and weight. Unlike Tadiran batteries, LiPo batteries <strong>can be recharged hundreds of times</strong>, making them a popular choice for many electronics devices. LiPo batteries are also <strong>lightweight</strong>, can deliver power quickly with high discharge rates, and as mentioned are very <strong>flexible in their shape and size</strong>.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--DbkpC5et--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0sfzve7gjbm6cnsou8s8.jpg"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DbkpC5et--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0sfzve7gjbm6cnsou8s8.jpg" alt="unique lipo battery shapes" /></a></p>
<p><em>LiPo batteries can be manufactured in virtually any shape or size. Image credit</em> <a target="_blank" href="http://lipolbattery.com"><em>lipolbattery.com</em></a><em>.</em></p>
<p>LiPo batteries are no silver bullet though - they do have some disadvantages! Care must be taken not to over-charge or over-discharge them, as this can significantly reduce their lifespan, or even cause them to catch fire. But it's important to note that most devices using LiPo batteries include safety circuits to prevent these issues from happening.</p>
<p>Finally, while LiPo batteries can be recharged many times, each charge and discharge cycle <em>slightly</em> reduces their capacity. After numerous cycles, the maximum capacity will be significantly reduced, at which point the battery may need to be replaced.</p>
<h3 id="heading-common-iot-applications-for-lipo-batteries">Common IoT Applications for LiPo Batteries</h3>
<p>While there is plenty of overlap between LiPo and Li-SOCl<sub>2</sub> Tadiran batteries in terms of use cases, lower-power and more cost-sensitive applications tend to use LiPo battery technology:</p>
<ul>
<li><p><strong>Remote Monitoring Systems:</strong> Remote systems for weather monitoring, wildlife tracking, and other environmental sensors often use LiPo batteries for their longevity, ability to be recharged, and operation in somewhat unpredictable conditions (but less "extreme" conditions than a Tadiran).</p>
</li>
<li><p><strong>Smart Agriculture:</strong> In smart farming, sensors and devices are used for monitoring soil moisture, weather conditions, and livestock tracking. These devices often use LiPo batteries because they can provide low-cost reliable operation for an extended period.</p>
</li>
<li><p><strong>Backing Up Home Automation Devices:</strong> Devices like smart locks, security cameras, and other IoT home automation systems are often line-powered, but also utilize LiPo batteries as a backup in case of a power outage.</p>
</li>
</ul>
<h2 id="heading-sowhich-should-i-choose">So...Which Should I Choose?</h2>
<p>The answer, as you may expect, is: "it depends on your use case!". Which of the following are most important for your deployment scenario?</p>
<h3 id="heading-operating-temperature-range">Operating Temperature Range</h3>
<p>When comparing the operating temperature ranges of Tadiran and LiPo batteries, <strong>Tadiran batteries have a clear advantage</strong> of -80°C to 125°C. Their Li-SOCl<sub>2</sub> chemistry enables them to perform well in extreme temperatures, making them suitable for applications in harsh environments.</p>
<p>While LiPo batteries have a more limited temperature range (-20°C to +60°C) compared to Tadiran batteries, they still offer a wide operating range suitable for many implementations.</p>
<p>✅ Tadiran Li-SOCl<sub>2</sub> ⛔️ LiPo</p>
<h3 id="heading-longevity-self-discharge-rate">Longevity (Self-Discharge Rate)</h3>
<p>The self-discharge rate of a battery refers to the percentage of charge it loses over time when not in use. This is an important factor to consider, especially for applications requiring long periods of inactivity. <strong>Tadiran batteries offer an extremely low self-discharge rate</strong> (~0.7% per year) while LiPo batteries self-discharge at a rate of up to 5% per month when not in use.</p>
<p>✅ Tadiran Li-SOCl<sub>2</sub> ⛔️ LiPo</p>
<h3 id="heading-flexibility">Flexibility</h3>
<p>The unique composition of LiPo batteries allows them to be not only lightweight (which is critical for many solutions) but also flexible and able to be shaped into virtually any size or shape. Tadirans, on the other hand, are limited to very specific shapes (though considering their higher voltages, may consume less physical space in an enclosure depending on the power requirements).</p>
<p>⛔️ Tadiran Li-SOCl<sub>2</sub> ✅ LiPo</p>
<h3 id="heading-cost">Cost</h3>
<p>Comparing the cost of Tadiran Li-SOCl<sub>2</sub> batteries and LiPo batteries can be complex because the true cost of a battery depends on many different factors. You need to factor in its capacity along with recharge cycles/total power delivered, and the specifics by manufacturer/model (not to mention quantity purchased!).</p>
<p>In general, Tadiran Li-SOCl<sub>2</sub> batteries are considered "specialty batteries" that aren't as widely available. They tend to be more expensive than LiPo batteries, as they possess the unique properties noted previously. When comparing total energy delivered between Tadiran and LiPo batteries, <strong>LiPo tends to come out ahead when it comes to total cost of ownership</strong>.</p>
<blockquote>
<p><strong>WARNING:</strong> Please note that the specifics can vary, and for an accurate and up-to-date comparison, you would need to look at the prices from various suppliers or directly from the manufacturers.</p>
</blockquote>
<p>⛔️ Tadiran Li-SOCl<sub>2</sub> ✅ LiPo</p>
<h2 id="heading-what-about-alkaline-batteries">What about Alkaline Batteries?</h2>
<p>Generally speaking, alkalines are the "worst of both worlds" in comparison with LiPo and Tadiran batteries. Aside from their low cost and wide availability, alkalines have a very low energy density (when compared to LiPo and Tadiran). This primarily impacts longevity, as devices won't last as long under comparable usage conditions. Alkalines also perform more poorly at high current-drain rates, aren't rechargeable, and their capacity decreases dramatically in low temperatures.</p>
<p><a target="_blank" href="https://res.cloudinary.com/practicaldev/image/fetch/s--vQz2IIpl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njd4clhiywyp5e1dx526.png"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vQz2IIpl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/njd4clhiywyp5e1dx526.png" alt="duracell aa batteries" /></a></p>
<p><em>The venerable Duracell AA alkaline battery. Image credit</em> <a target="_blank" href="https://www.duracell.com/"><em>Duracell</em></a><em>.</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>When choosing between Tadiran Li-SOCl<sub>2</sub> and LiPo batteries for an embedded device or IoT application, it is crucial to consider factors such as energy density, operating temperature range, self-discharge rate, and the specific requirements of your application.</p>
<p>Tadiran batteries excel in low-power, long-life applications, and in extreme temperature environments, while LiPo batteries are ideal for lower-cost deployments, scenarios where they may be recharged, and devices requiring custom battery shapes.</p>
<p>If you need help determining the best battery tech for your individual solution, feel free to reach out via the <a target="_blank" href="https://discuss.blues.io/">Blues community forum</a>. 🔋</p>
]]></content:encoded></item></channel></rss>