<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rob Lauer</title>
    <description>The latest articles on DEV Community by Rob Lauer (@rdlauer).</description>
    <link>https://dev.to/rdlauer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F88944%2Fc5c9ebfb-e5ff-4571-857f-e030e0f6030f.jpg</url>
      <title>DEV Community: Rob Lauer</title>
      <link>https://dev.to/rdlauer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rdlauer"/>
    <language>en</language>
    <item>
      <title>Announcing the Raspberry Pi 50000: The Future of Computing is Just a Mouse</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Fri, 14 Mar 2025 13:45:06 +0000</pubDate>
      <link>https://dev.to/rdlauer/announcing-the-raspberry-pi-50000-the-future-of-computing-is-just-a-mouse-jep</link>
      <guid>https://dev.to/rdlauer/announcing-the-raspberry-pi-50000-the-future-of-computing-is-just-a-mouse-jep</guid>
      <description>&lt;p&gt;&lt;em&gt;Banner image logo most certainly NOT provided by Raspberry Pi!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Welcome fellow Raspberry Pi nerds - and apologies in advance to the &lt;a href="https://www.raspberrypi.org/" rel="noopener noreferrer"&gt;Raspberry Pi Foundation&lt;/a&gt; for stealing their thunder on &lt;a href="https://en.wikipedia.org/wiki/Pi_Day" rel="noopener noreferrer"&gt;Pi Day&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;I am thrilled - no, &lt;em&gt;honored&lt;/em&gt; - to introduce the next evolution in single-board computing: the &lt;strong&gt;Raspberry Pi 50000&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Many of you are aware of the &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-500/" rel="noopener noreferrer"&gt;Raspberry Pi 500&lt;/a&gt;, the groundbreaking device that was literally just a keyboard (well, and the equivalent internals of a Raspberry Pi 5).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbss74vdatscrv9g4jn8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbss74vdatscrv9g4jn8l.png" alt="raspberry pi 500" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While I don't know if the Raspberry Pi Foundation heard your feedback after the release of the 500, I certainly did.&lt;/p&gt;

&lt;p&gt;You want to go &lt;em&gt;smaller&lt;/em&gt;. You want to be &lt;em&gt;less productive&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So, I dove into my lab and took a long, hard look at what computing really means and I made a bold decision. A decision that will redefine innovation. &lt;strong&gt;You want...a mouse.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I give you the Raspberry Pi 50000:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioutuus05bs1nukxfz3h.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioutuus05bs1nukxfz3h.jpg" alt="raspberry pi 50000 - yes, fake" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes. Just. A. Mouse.&lt;/p&gt;

&lt;p&gt;It's 2025. Keyboards and monitors are &lt;em&gt;barely&lt;/em&gt; used anymore. Remember when Apple dropped the headphone jack from the iPhone? &lt;strong&gt;This is like that, but even...more.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F256j9pumj4yp7j23qrhj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F256j9pumj4yp7j23qrhj.jpg" alt="apple headphone announcement" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you use it?&lt;/strong&gt; I assume you'll figure it out. Just like you figured out that the Raspberry Pi 500 was not, in fact, a standalone computer. You adapt. You evolve. I innovate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Inside the 50000?
&lt;/h2&gt;

&lt;p&gt;Prying open the "case" of the Raspberry Pi 50000, we can see the internals include a Raspberry Pi Pico (literally).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcw3wj61br8wtvcf6wi3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbcw3wj61br8wtvcf6wi3.jpg" alt="raspberry pi 50000 internals - yes, fake" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, and this is where we really blow your socks off, also inside this perfectly normal, borderline disposable mouse...is a &lt;a href="https://blues.com/products/notecard/" rel="noopener noreferrer"&gt;Blues Notecard&lt;/a&gt; for cellular data access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fisx6m9933sy88v5o7r05.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fisx6m9933sy88v5o7r05.jpg" alt="raspberry pi 50000 internals with blues notecard - yes, fake" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, cellular. Because in 2025, why would you need a screen or a keyboard or even a basic computer when you can have a mouse that has full 4G LTE connectivity?&lt;/p&gt;

&lt;p&gt;That's right, folks, you can now click your way to the future, no matter where you are. Need to record the number of clicks on your mouse? Maybe send the temperature inside your mouse to the cloud? Boom. Done.&lt;/p&gt;

&lt;p&gt;And let's talk specs. It's a mouse. It clicks. It scrolls. But deep inside this unassuming husk, there's that tiny cellular system-on-module that's &lt;em&gt;actually&lt;/em&gt; useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  So How Does the Raspberry Pi 50000 Work?
&lt;/h2&gt;

&lt;p&gt;Now you might be asking: "How do I use it?"&lt;/p&gt;

&lt;p&gt;To which I say: "Why are you looking at me?"&lt;/p&gt;

&lt;p&gt;However, to get started with that little cellular system-on-module known as the &lt;a href="https://blues.com/products/notecard/" rel="noopener noreferrer"&gt;Blues Notecard&lt;/a&gt; and your &lt;a href="https://shop.blues.com/products/carr-pi" rel="noopener noreferrer"&gt;Raspberry Pi&lt;/a&gt;, STM32, ESP32, or whatever host you're using, check out the &lt;a href="https://dev.blues.io/" rel="noopener noreferrer"&gt;Blues Developer Center&lt;/a&gt; for quickstarts, guides, and example apps.&lt;/p&gt;

&lt;p&gt;In the meantime, if the Raspberry Pi 50000 is something you're &lt;em&gt;actually&lt;/em&gt; interested in...well, best of luck to you in all your future endeavors.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yes this is satire...yes I work for Blues...and yes I just wrote this for fun...I mean come on people did I really just need to write that?&lt;/em&gt; 😅&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>cellular</category>
      <category>mouse</category>
      <category>piday</category>
    </item>
    <item>
      <title>Sending and Receiving Binary Files with the Notecard and Notehub</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Tue, 28 Nov 2023 16:13:33 +0000</pubDate>
      <link>https://dev.to/blues/sending-and-receiving-binary-files-with-the-notecard-and-notehub-1ep2</link>
      <guid>https://dev.to/blues/sending-and-receiving-binary-files-with-the-notecard-and-notehub-1ep2</guid>
      <description>&lt;p&gt;The Notecard was originally designed as a &lt;a href="https://dev.blues.io/notecard/notecard-walkthrough/low-bandwidth-design/#low-bandwidth-design" rel="noopener noreferrer"&gt;low-bandwidth&lt;/a&gt; and &lt;a href="https://dev.blues.io/notecard/notecard-walkthrough/low-power-design/#low-power-design" rel="noopener noreferrer"&gt;low-power&lt;/a&gt; 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 &lt;em&gt;also&lt;/em&gt; sync larger payloads of binary data (most often in the form of images).&lt;/p&gt;

&lt;p&gt;With the release of the &lt;a href="https://dev.blues.io/notecard/notecard-firmware-updates/#v5-3-1-september-18th-2023" rel="noopener noreferrer"&gt;Notecard developer firmware release v5.3.1&lt;/a&gt;, this is now possible using the new &lt;a href="https://dev.blues.io/api-reference/notecard-api/card-requests/#card-binary" rel="noopener noreferrer"&gt;card.binary APIs&lt;/a&gt;. Even better, our firmware libraries for &lt;a href="https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library/" rel="noopener noreferrer"&gt;Arduino/C&lt;/a&gt; and &lt;a href="https://dev.blues.io/tools-and-sdks/firmware-libraries/python-library/" rel="noopener noreferrer"&gt;Python&lt;/a&gt; abstract away the complexity of the &lt;code&gt;card.binary&lt;/code&gt; requests for you.&lt;/p&gt;

&lt;p&gt;In this article, I'm going to show an example in Arduino/C of how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Request a JPEG image from a cloud endpoint.&lt;/li&gt;
&lt;li&gt;Write that JPEG image to a Micro SD card.&lt;/li&gt;
&lt;li&gt;Send that same JPEG image back to a different cloud endpoint.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Hardware Setup
&lt;/h2&gt;

&lt;p&gt;For this example, I'm using the following hardware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shop.blues.io/collections/swan/products/swan" rel="noopener noreferrer"&gt;Blues Swan&lt;/a&gt; (STM32L4-based host MCU)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shop.blues.io/collections/swan/products/stlink-v3mini" rel="noopener noreferrer"&gt;STLINK Programmer/Debugger&lt;/a&gt; (optional, but makes working with STM32-based MCUs more pleasant)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shop.blues.io/products/notecard" rel="noopener noreferrer"&gt;Blues Notecard Cellular&lt;/a&gt; (any Notecard will do!)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shop.blues.io/products/carr-al" rel="noopener noreferrer"&gt;Blues Notecarrier A&lt;/a&gt; (a development board for interfacing with the Notecard)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.adafruit.com/product/254" rel="noopener noreferrer"&gt;Adafruit's Micro SD breakout board&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://shop.blues.io/collections/accessories/products/5-000-mah-lipo-battery" rel="noopener noreferrer"&gt;2 x LiPo batteries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Wiring the Micro SD Breakout to the Swan
&lt;/h3&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SD Breakout&lt;/th&gt;
&lt;th&gt;Blues Swan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3v&lt;/td&gt;
&lt;td&gt;3V3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLK&lt;/td&gt;
&lt;td&gt;CK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DO&lt;/td&gt;
&lt;td&gt;MI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DI&lt;/td&gt;
&lt;td&gt;MO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CS&lt;/td&gt;
&lt;td&gt;A3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Attaching the Notecarrier to the Swan
&lt;/h3&gt;

&lt;p&gt;Next, attach the Notecarrier A to the Swan via a Qwiic cable. Note that the Swan and Notecarrier A &lt;em&gt;also&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;Your hardware should look something like the following (with or without the STLINK debugger of course):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5954s20rudz3avygvv4b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5954s20rudz3avygvv4b.jpg" alt="wiring sd breakout to swan and notecarrier" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Format the Micro SD Card
&lt;/h2&gt;

&lt;p&gt;If you haven't already, be sure the Micro SD card you're using is already formatted as either FAT16 (if &amp;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 &lt;a href="https://www.sdcard.org/downloads/formatter/" rel="noopener noreferrer"&gt;macOS/Win&lt;/a&gt; and &lt;a href="https://www.sdcard.org/downloads/sd-memory-card-formatter-for-linux/" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Now it's time to write the sketch!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Looking for a shortcut? &lt;a href="https://gist.github.com/rdlauer/4f50bbb44a1b10100a0bc62b7cc820b2" rel="noopener noreferrer"&gt;Check out this gist&lt;/a&gt; to see the completed sketch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setup the Notecard and SD Card Breakout
&lt;/h2&gt;

&lt;p&gt;Include the necessary libraries in your project, including &lt;a href="https://dev.blues.io/tools-and-sdks/firmware-libraries/arduino-library/" rel="noopener noreferrer"&gt;note-arduino&lt;/a&gt; for the Notecard and the &lt;a href="https://github.com/arduino-libraries/SD" rel="noopener noreferrer"&gt;Arduino SD Library&lt;/a&gt; for the Micro SD breakout board. Also, add the variables needed to interface with the components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Arduino.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Wire.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Notecard.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;SD.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;Notecard&lt;/span&gt; &lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="n"&gt;myImageFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Sd2Card&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;SdVolume&lt;/span&gt; &lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;SdFile&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;chipSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;HardwareSerial&lt;/span&gt; &lt;span class="nf"&gt;stlinkSerial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PIN_VCP_RX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PIN_VCP_TX&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cp"&gt;#define usbSerial Serial
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ledPin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LED_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// references onboard LED on Swan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initialize the Notecard and Micro SD Breakout
&lt;/h2&gt;

&lt;p&gt;Most everything else occurs in the &lt;code&gt;setup()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Set the &lt;code&gt;pinMode&lt;/code&gt; of the Swan's onboard LED to &lt;code&gt;OUTPUT&lt;/code&gt; 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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ledPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Wire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// if using STLINK debugger&lt;/span&gt;
&lt;span class="n"&gt;stlinkSerial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;115200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;usb_timeout_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;start_ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;stlinkSerial&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;usb_timeout_ms&lt;/span&gt;&lt;span class="p"&gt;;)&lt;/span&gt;
  &lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setDebugOutputStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stlinkSerial&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, initialize the Notecard, &lt;strong&gt;link your Notecard to your project in Notehub&lt;/strong&gt; via the &lt;code&gt;product&lt;/code&gt; key, and reset the Notecard's binary storage area to make sure we have the space required to save an image.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you haven't &lt;a href="https://notehub.io/" rel="noopener noreferrer"&gt;set up a Notehub account yet&lt;/a&gt;, you should, because it's free to use (up to 5,000 events routed per month!) and only takes an email address.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;J&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hub.set"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"com.your.project:uid"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"continuous"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddBoolToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"sync"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendRequestWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// wait until we are connected to notehub&lt;/span&gt;
&lt;span class="c1"&gt;// this is REQUIRED when sending binary data!&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;connected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hub.status"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;J&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;requestAndResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;connected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JGetBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"connected"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Reset the Notecard's binary storage area, so we can start fresh&lt;/span&gt;
&lt;span class="n"&gt;NoteBinaryStoreReset&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, initialize the connected Micro SD card:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// init the SD card for usage&lt;/span&gt;
&lt;span class="n"&gt;stlinkSerial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Initializing SD card..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SPI_HALF_SPEED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chipSelect&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;stlinkSerial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SD card initialization failed!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// try to open the 'volume'/'partition' - it should be FAT16 or FAT32&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;stlinkSerial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not find FAT16/FAT32 partition.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Make sure you've formatted the card"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it for our initialization tasks. Next up, it's time to request an image file!&lt;/p&gt;

&lt;h2&gt;
  
  
  Request an Image from the Cloud
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;To request a binary file from a cloud end point, we'll have to set up a &lt;a href="https://dev.blues.io/notehub/notehub-walkthrough/#routing-data-with-notehub" rel="noopener noreferrer"&gt;Notehub Route&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In Notehub, navigate to the &lt;strong&gt;Routes&lt;/strong&gt; menu option, and create a new &lt;strong&gt;Proxy for Notecard Web Requests&lt;/strong&gt; route:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwvwiqfrdad2fs2w106w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwvwiqfrdad2fs2w106w.png" alt="create proxy route" width="800" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supply an arbitrary &lt;strong&gt;Route Name&lt;/strong&gt; and the &lt;strong&gt;URL&lt;/strong&gt; that Notehub should use to fetch the image with a &lt;code&gt;GET&lt;/code&gt;. Also, you'll need to add an arbitrary &lt;strong&gt;Alias&lt;/strong&gt; string, which is how you'll reference this Route in the sketch below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00ri3d4tvyuqtythtwg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00ri3d4tvyuqtythtwg1.png" alt="set up proxy route" width="800" height="423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's it!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Back in your sketch, you'll use the &lt;a href="https://dev.blues.io/api-reference/notecard-api/web-requests/#web-get" rel="noopener noreferrer"&gt;web.get API&lt;/a&gt; to call the newly-created Notehub Route (called &lt;code&gt;GetImage&lt;/code&gt;) and store whatever binary data is returned in flash on the Notecard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;J&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NoteNewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"web.get"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"GetImage"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;JAddBoolToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"binary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;NoteRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logDebug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error receiving image&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;data_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;NoteBinaryStoreDecodedLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;rx_buffer_len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NoteBinaryCodecMaxEncodedLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;rx_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx_buffer_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Receive the data&lt;/span&gt;
&lt;span class="n"&gt;NoteBinaryStoreReceive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx_buffer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;rx_buffer_len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logDebugf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;[INFO] Received %d bytes.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Save the Image to the Micro SD Card
&lt;/h2&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// save the buffer to the specified file name on the SD card&lt;/span&gt;
&lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chipSelect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"logo.jpg"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// remove the file if it already exists&lt;/span&gt;
&lt;span class="n"&gt;myImageFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"logo.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FILE_WRITE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myImageFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;myImageFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx_buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;myImageFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;stlinkSerial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Completed writing the file!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frv7y36bn32mrtlf592ox.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frv7y36bn32mrtlf592ox.jpg" alt="blues logo" width="375" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yes you'll just have to take my word that it worked!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Send the Image Back to a Cloud Endpoint
&lt;/h2&gt;

&lt;p&gt;So far you've downloaded a file and saved it to SD, but what about uploading it back to another cloud endpoint?&lt;/p&gt;

&lt;p&gt;Welcome to the magic of Notehub Routes (yes, they work both ways)!&lt;/p&gt;

&lt;p&gt;Back in Notehub, again navigate to the &lt;strong&gt;Routes&lt;/strong&gt; menu option, and create a new &lt;strong&gt;General HTTP/HTTPS Request/Response&lt;/strong&gt; route (this is different than the &lt;strong&gt;Proxy for Notecard Web Requests&lt;/strong&gt; route type, as this time we will be generating a payload locally using &lt;a href="https://dev.blues.io/api-reference/glossary/#note" rel="noopener noreferrer"&gt;Notes&lt;/a&gt; to send data from the Notecard to Notehub).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyioq8d3df2v4gu0u4upx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyioq8d3df2v4gu0u4upx.png" alt="create https route" width="800" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Provide an arbitrary Route Name and the remote URL that Notehub will use to &lt;code&gt;POST&lt;/code&gt; the binary data. In my case, I used the venerable webhook.site for an easy way to inspect outbound data to the cloud.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdq2wa1pdbmlj9w3j2r0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdq2wa1pdbmlj9w3j2r0.png" alt="configure https route" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Continue to the &lt;strong&gt;Filters&lt;/strong&gt; section and be sure that you are only routing data from the Notecard that appears in the &lt;code&gt;binary.qo&lt;/code&gt; Notefile (this is the name of the Notefile that will contain the binary payload).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8pomzbux56xc7y899le.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv8pomzbux56xc7y899le.png" alt="configure https route" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All set?&lt;/strong&gt; Back to your sketch!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;notecard_binary_area_offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;NoteBinaryStoreTransmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx_buffer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx_buffer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;notecard_binary_area_offset&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logDebugf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;[INFO] Transmitted %d bytes.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data_len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Send the binary data to Notehub&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;J&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;newRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"note.add"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;JAddStringToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"binary.qo"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;JAddBoolToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"binary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;JAddBoolToObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"live"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;notecard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sendRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Free the receive buffer&lt;/span&gt;
&lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rx_buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;stlinkSerial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Completed sending the file!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;a href="https://dev.blues.io/api-reference/notecard-api/note-requests/#note-add" rel="noopener noreferrer"&gt;note.add API&lt;/a&gt;. Note that you're also supplying the &lt;code&gt;binary:true&lt;/code&gt; and &lt;code&gt;live:true&lt;/code&gt; arguments of &lt;code&gt;note.add&lt;/code&gt; in order to pass the binary data from the Notecard to Notehub!&lt;/p&gt;

&lt;h2&gt;
  
  
  Viewing the Binary Payload in the Cloud
&lt;/h2&gt;

&lt;p&gt;Depending on how you set up the Notehub Route that &lt;code&gt;POST&lt;/code&gt;s data, you should be able to see the binary data delivered. As mentioned earlier, I used webhook.site which lets me inspect delivered payloads. Here is a snippet of the binary file I delivered:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjymh4y1ruw55rwc513l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frjymh4y1ruw55rwc513l.png" alt="webhook post results" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up with a Blink and a Loop
&lt;/h2&gt;

&lt;p&gt;Since I like to get some visual indication that my sketch has run to completion, in the &lt;code&gt;loop()&lt;/code&gt; method, I simply flash the Swan's onboard LED on and off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Finally, blink the Swan's LED when done!&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ledPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ledPin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;For a quick view of the full Arduino sketch, &lt;a href="https://gist.github.com/rdlauer/4f50bbb44a1b10100a0bc62b7cc820b2" rel="noopener noreferrer"&gt;consult this gist&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://github.com/blues/note-arduino/tree/master/examples/Example9_BinarySendReceiveChunked" rel="noopener noreferrer"&gt;additional example Arduino sketch&lt;/a&gt; that shows how to accomplish this more advanced scenario.&lt;/p&gt;

&lt;p&gt;If you haven't already, &lt;a href="https://shop.blues.io/collections/blues-starter-kits" rel="noopener noreferrer"&gt;grab your own Blues Starter Kit&lt;/a&gt; and see how easy it really can be to sync data from a physical device to the cloud (over Cellular, Wi-Fi, or LoRa!).&lt;/p&gt;

&lt;p&gt;Happy Hacking! 💙&lt;/p&gt;

</description>
      <category>iot</category>
      <category>embedded</category>
      <category>arduino</category>
    </item>
    <item>
      <title>The Easiest Way to Upgrade Raspberry Pi OS from Bullseye to Bookworm</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Tue, 07 Nov 2023 17:26:45 +0000</pubDate>
      <link>https://dev.to/blues/the-easiest-way-to-upgrade-raspberry-pi-os-from-bullseye-to-bookworm-c3m</link>
      <guid>https://dev.to/blues/the-easiest-way-to-upgrade-raspberry-pi-os-from-bullseye-to-bookworm-c3m</guid>
      <description>&lt;p&gt;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. &lt;strong&gt;I'm also exceptionally lazy&lt;/strong&gt;, so I usually opt for the upgrade path versus a clean install.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixw4ld3u0psovgophz8z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fixw4ld3u0psovgophz8z.gif" alt="lazy gif" width="480" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today I want to take a look at how you can update a Raspberry Pi (or compatible single board computer) to the newly released &lt;a href="https://www.raspberrypi.com/news/bookworm-the-new-version-of-raspberry-pi-os/" rel="noopener noreferrer"&gt;"Bookworm" version of Raspberry Pi OS&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let me be crystal clear: This is an &lt;strong&gt;unsupported upgrade path&lt;/strong&gt; that is not endorsed by the Raspberry Pi Foundation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But I also like to live dangerously.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76ix13qynpsscp8nx3jk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76ix13qynpsscp8nx3jk.gif" alt="austin powers gif" width="480" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before continuing, I'm sure at least some of you are wondering: &lt;em&gt;What exactly is Bookworm?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Bookworm?
&lt;/h2&gt;

&lt;p&gt;Bookworm is the codename for a &lt;a href="https://raspberrytips.com/raspberry-pi-os-versions/" rel="noopener noreferrer"&gt;new version of Raspberry Pi OS&lt;/a&gt;. Based on Debian GNU/Linux 12, Bookworm is a significant new release for Raspberry Pi enthusiasts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faj52s1g111vxsy1qjwc9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faj52s1g111vxsy1qjwc9.png" alt="raspberry pi bookworm desktop" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Arguably the three most important updates in Bookworm are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A switch from the X11 Windowing System to &lt;a href="https://wayland.freedesktop.org/" rel="noopener noreferrer"&gt;Wayland&lt;/a&gt;, which improves security and performance (and includes some pleasing desktop UI updates).&lt;/li&gt;
&lt;li&gt;Adopting &lt;a href="https://pipewire.org/" rel="noopener noreferrer"&gt;PipeWire&lt;/a&gt; as the default audio backend (replacing PulseAudio).&lt;/li&gt;
&lt;li&gt;Use of &lt;a href="https://networkmanager.dev/" rel="noopener noreferrer"&gt;NetworkManager&lt;/a&gt; as the default network suite (replacing dhcpcd).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bullseye to Bookworm Upgrade Options
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;good news&lt;/em&gt; is there are multiple upgrade options from Bullseye to Bookworm.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;bad news&lt;/em&gt; is there are multiple upgrade options from Bullseye to Bookworm.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy Upgrade Path&lt;/li&gt;
&lt;li&gt;Better Upgrade Path&lt;/li&gt;
&lt;li&gt;
Best "Upgrade" Path (the officially recommended path, but that's not why you're here)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING:&lt;/strong&gt; Regardless of the path you take, be sure to &lt;strong&gt;make a full backup&lt;/strong&gt; of the SD card you're using for your current Raspberry Pi installation. Yes, just in case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Easy Upgrade Path from Bullseye to Bookworm
&lt;/h2&gt;

&lt;p&gt;Sorry, one more warning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING:&lt;/strong&gt; These instructions are &lt;strong&gt;only valid for upgrading from Bullseye&lt;/strong&gt;. 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, except for the ending).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ready? Backup finished? Anxious to get started? &lt;strong&gt;Let's begin!&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Perform a &lt;strong&gt;full upgrade&lt;/strong&gt; of your existing Bullseye installation. Open a Terminal and enter these commands, sequentially:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   sudo apt update
   sudo apt full-upgrade
   sudo reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;After rebooting, enter the following command to edit &lt;code&gt;sources.list&lt;/code&gt;, which is how your Pi references the archives for the Debian repositories:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   sudo nano /etc/apt/sources.list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Update the first line, replacing &lt;code&gt;bullseye&lt;/code&gt; with &lt;code&gt;bookworm&lt;/code&gt; and adding &lt;code&gt;non-free-firmware&lt;/code&gt; to the end of the line. Your &lt;code&gt;sources.list&lt;/code&gt; file should end up looking something like this:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   deb http://raspbian.raspberrypi.org/raspbian/ bookworm main contrib non-free rpi non-free-firmware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Type &lt;code&gt;ctrl-x&lt;/code&gt; to exit &lt;code&gt;nano&lt;/code&gt;, making sure you save the file on exit. Next, you need to open &lt;code&gt;raspi.list&lt;/code&gt; (to update the Debian Bookworm base) with this command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   sudo nano /etc/apt/sources.list.d/raspi.list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In this file, you only need to change &lt;code&gt;bullseye&lt;/code&gt; to &lt;code&gt;bookworm&lt;/code&gt;. Again, hit &lt;code&gt;ctrl-x&lt;/code&gt;, save your changes, and exit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Perform &lt;em&gt;another&lt;/em&gt; full upgrade, but this time sourcing from the Bookworm repositories!&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   sudo apt update
   sudo apt full-upgrade
   sudo reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;And now you wait. And wait some more.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxjicvau0gxhayptjzn98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxjicvau0gxhayptjzn98.png" alt="update raspberry pi" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;After this final reboot, confirm that you're now running an &lt;em&gt;almost complete&lt;/em&gt; version of Bookworm with this command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   cat /etc/os-release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success! 🥳&lt;/p&gt;

&lt;p&gt;However, while it &lt;em&gt;looks and feels&lt;/em&gt; 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:&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Way to Upgrade to Bookworm
&lt;/h2&gt;

&lt;p&gt;Yes, there is a better way. It's not for the faint of heart though.&lt;/p&gt;

&lt;p&gt;A brave soul on GitHub is actively updating a series of commands for a &lt;strong&gt;full in-place upgrade&lt;/strong&gt; from Bullseye to Bookworm &lt;a href="https://gist.github.com/jauderho/6b7d42030e264a135450ecc0ba521bd8" rel="noopener noreferrer"&gt;in this gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As of the publishing of this blog post in November 2023, here is the full list of commands. Godspeed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;### 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;&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;&amp;amp; sudo apt -y full-upgrade &amp;amp;&amp;amp; sudo apt -y clean &amp;amp;&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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Best Way to "Upgrade" to Bookworm
&lt;/h2&gt;

&lt;p&gt;After all we just went through, do you really want to know what I think is the &lt;strong&gt;best way to upgrade to Bookworm&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Well, it's quite simple and foolproof:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Backup all of the directories and files you want to keep from your Pi.&lt;/li&gt;
&lt;li&gt;Download and install the &lt;a href="https://www.raspberrypi.com/software/" rel="noopener noreferrer"&gt;Raspberry Pi Imager&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Format an SD card and install a fresh copy of Bookworm.&lt;/li&gt;
&lt;li&gt;Copy your files back to the Pi.&lt;/li&gt;
&lt;li&gt;Success! 🥳&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy Cellular, LoRa, and Wi-Fi on the Pi
&lt;/h2&gt;

&lt;p&gt;Now that you're done, if you're in the market for a new way to add &lt;strong&gt;swappable Cellular, LoRa, and Wi-Fi to your Raspberry Pi&lt;/strong&gt;, take a look at the &lt;a href="https://shop.blues.io/collections/notecard" rel="noopener noreferrer"&gt;Blues Notecard&lt;/a&gt; and the &lt;a href="https://shop.blues.io/products/carr-pi" rel="noopener noreferrer"&gt;Blues Notecarrier Pi HAT&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fblues-wireless%2Fimage%2Ffetch%2Ff_auto%2Cc_limit%2Cw_1920%2Cq_auto%2Fhttps%3A%2F%2Fdev.blues.io%2F_next%2Fstatic%2Fmedia%2Frpi-and-notecarrier.88be48ad.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fblues-wireless%2Fimage%2Ffetch%2Ff_auto%2Cc_limit%2Cw_1920%2Cq_auto%2Fhttps%3A%2F%2Fdev.blues.io%2F_next%2Fstatic%2Fmedia%2Frpi-and-notecarrier.88be48ad.webp" alt="raspberry pi and blues notecard" width="740" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cellular-based Notecards provide &lt;strong&gt;500MB of prepaid data and 10 years of global cellular service&lt;/strong&gt;. These are commonly used in edge deployments of Raspberry Pis that don't have access to reliable Wi-Fi connectivity.&lt;/p&gt;

&lt;p&gt;Learn more about adding low-bandwidth cellular to the Raspberry Pi in &lt;a href="https://www.hackster.io/blues-wireless/projects" rel="noopener noreferrer"&gt;many of these project tutorials on Hackster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Hacking! 💙&lt;/p&gt;

</description>
      <category>bullseye</category>
      <category>raspberrypi</category>
      <category>bookworm</category>
    </item>
    <item>
      <title>Read and Write Images and Text Files with a Micro SD Card and Arduino</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Tue, 24 Oct 2023 17:01:15 +0000</pubDate>
      <link>https://dev.to/blues/read-and-write-images-and-text-files-with-a-micro-sd-card-and-arduino-be6</link>
      <guid>https://dev.to/blues/read-and-write-images-and-text-files-with-a-micro-sd-card-and-arduino-be6</guid>
      <description>&lt;p&gt;We've all been there. Well, at least &lt;em&gt;I've&lt;/em&gt; certainly been there:&lt;/p&gt;

&lt;p&gt;I want to perform what I assume to be a simple task with C (Arduino): &lt;em&gt;"I just want to read and write some data to a Micro SD card! This can't be that difficult can it!?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Time to take a deep breath and channel my internal Bob Ross...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8r748ek2y5c2apq6pdk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8r748ek2y5c2apq6pdk.gif" alt="bob ross" width="336" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In this mini-project, I'm using the following hardware:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://shop.blues.io/collections/swan/products/swan" rel="noopener noreferrer"&gt;STM32-based Blues Swan&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.adafruit.com/product/254" rel="noopener noreferrer"&gt;Adafruit Micro SD breakout board&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/SanDisk-Ultra-microSDXC-Memory-Adapter/dp/B073JWXGNT" rel="noopener noreferrer"&gt;SanDisk 32GB Micro SD card&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Hardware Wiring
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;SD Breakout&lt;/th&gt;
&lt;th&gt;Blues Swan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3v&lt;/td&gt;
&lt;td&gt;3V3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLK&lt;/td&gt;
&lt;td&gt;CK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DO&lt;/td&gt;
&lt;td&gt;MI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DI&lt;/td&gt;
&lt;td&gt;MO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CS&lt;/td&gt;
&lt;td&gt;A3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Your wiring should end up looking like something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7twzz4pdgs9k21ugf6zn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7twzz4pdgs9k21ugf6zn.jpg" alt="wiring blues swan to sd breakout board" width="800" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Prepping the Micro SD Card
&lt;/h2&gt;

&lt;p&gt;You'll want to make sure the Micro SD card is formatted as either FAT16 (if &amp;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 (&lt;a href="https://www.sdcard.org/downloads/formatter/" rel="noopener noreferrer"&gt;macOS/Win&lt;/a&gt; and &lt;a href="https://www.sdcard.org/downloads/sd-memory-card-formatter-for-linux/" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Slide that Micro SD card back in the breakout board and we are ready to write some code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arduino Micro SD Breakout Library
&lt;/h2&gt;

&lt;p&gt;One of the reasons I chose &lt;a href="https://www.adafruit.com/product/254" rel="noopener noreferrer"&gt;Adafruit's Micro SD breakout board&lt;/a&gt; (aside from the generally high quality of their hardware) is because they offer well-supported and well-documented libraries. This one is no exception.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Consult &lt;a href="https://github.com/arduino-libraries/SD" rel="noopener noreferrer"&gt;this GitHub repository&lt;/a&gt; for the latest version of the Arduino SD Library.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's look at how we might perform some typical file-based tasks with an SD card, in particular:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Writing and reading text files.&lt;/li&gt;
&lt;li&gt;Writing and reading images (PNGs).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Writing a Text File
&lt;/h2&gt;

&lt;p&gt;Using a Micro SD card as a data logger is very common in our industry. They are inexpensive and a relatively stable medium.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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 &lt;a href="https://shop.blues.io/collections/notecard" rel="noopener noreferrer"&gt;Cellular, Wi-Fi, or LoRa-based Notecard&lt;/a&gt;, but I digress...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following sketch will initialize the SD card breakout board and write a string to a text file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;SD.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="n"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Sd2Card&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;SdVolume&lt;/span&gt; &lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;chipSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting SD card init..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SPI_HALF_SPEED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chipSelect&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SD card initialization failed!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// try to open the 'volume'/'partition' - it should be FAT16 or FAT32&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not find FAT16/FAT32 partition."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chipSelect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SD initialization complete!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// open a new file for writing&lt;/span&gt;
  &lt;span class="n"&gt;textFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FILE_WRITE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Completed writing a text file!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that there are a few things to be aware of when working with files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You'll want to keep filenames short and use the 8.3 format (e.g. &lt;code&gt;MYFILE.TXT&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Filenames are NOT case sensitive, so in code, &lt;code&gt;myfile.txt&lt;/code&gt; == &lt;code&gt;MYFILE.TXT&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Always &lt;code&gt;close()&lt;/code&gt; your files when done to make sure any pending writes are properly saved.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Reading from a Text File
&lt;/h2&gt;

&lt;p&gt;Now that you've written a simple text file to a Micro SD card, let's see how easy it can be to &lt;strong&gt;read data from an existing file&lt;/strong&gt; (specifically the &lt;code&gt;hello.txt&lt;/code&gt; file we just created).&lt;/p&gt;

&lt;p&gt;Add the following code snippet &lt;strong&gt;after the file is created&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// open the existing file for reading&lt;/span&gt;
&lt;span class="n"&gt;textFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"here's the data in hello.txt:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// read until there's nothing else in it:&lt;/span&gt;
  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// close the file:&lt;/span&gt;
  &lt;span class="n"&gt;textFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because writing to an existing file &lt;em&gt;appends data&lt;/em&gt; by default and doesn't overwrite existing data. In my case, I had clearly tested the write sequence a handful of times &lt;strong&gt;before&lt;/strong&gt; I read the data out.&lt;/p&gt;

&lt;p&gt;To fix this, you can always &lt;code&gt;remove()&lt;/code&gt; the file first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Other Useful Functions When Working with Files on SD
&lt;/h3&gt;

&lt;p&gt;Before writing to a file, you might want to see if it already exists by using the &lt;code&gt;SD.exists("thefile.txt")&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Create directories and subdirectories with the &lt;code&gt;SD.mkdir("/newdir")&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;Use the aforementioned &lt;code&gt;SD.remove("thefile.txt")&lt;/code&gt; function to delete files.&lt;/p&gt;

&lt;p&gt;Be sure to consult &lt;a href="https://learn.adafruit.com/adafruit-micro-sd-breakout-board-card-tutorial" rel="noopener noreferrer"&gt;Adafruit's full tutorial&lt;/a&gt; for additional information on using this breakout board with a Micro SD card.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with Images
&lt;/h2&gt;

&lt;p&gt;Let's switch gears and ramp up the complexity a little bit by writing and reading images to/from the Micro SD card.&lt;/p&gt;

&lt;p&gt;As with every complex Arduino task, we first need to ask: &lt;strong&gt;"Is there library for this?"&lt;/strong&gt;. The answer is almost always "yes", and this time is no exception.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Choosing the &lt;strong&gt;optimal type of compression to use will depend on the image itself&lt;/strong&gt; (e.g. photos usually compress best with JPEG while drawings/line art are best compressed with PNG or WEBP).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Example of thermal camera images saved as BMP, PNG, and JPG:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpiz1yhybeoraum19104k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpiz1yhybeoraum19104k.png" alt="comparison of png to bmp to jpeg" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And yes there are libraries for these options as well! &lt;a href="https://github.com/bitbank2/JPEGenc" rel="noopener noreferrer"&gt;JPGenc&lt;/a&gt; and &lt;a href="https://github.com/bitbank2/JPEGdec" rel="noopener noreferrer"&gt;JPGdec&lt;/a&gt; for encoding/decoding JPG images, and &lt;a href="https://github.com/bitbank2/PNGenc" rel="noopener noreferrer"&gt;PNGenc&lt;/a&gt; and &lt;a href="https://github.com/bitbank2/PNGdec" rel="noopener noreferrer"&gt;PNGdec&lt;/a&gt; for encoding/decoding PNGs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Looking for a functional example of image compression in action? &lt;a href="https://www.hackster.io/rob-lauer/id-chicken-eggs-with-thermal-images-ml-and-cellular-iot-748c88" rel="noopener noreferrer"&gt;Check out this project on Hackster&lt;/a&gt; that uses a thermal camera to capture images, show them on a TFT display, and save them to a Micro SD card.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Writing an Image to a Micro SD Card
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The following sketch creates a 128x128 image with a green border and green "x" going through it, like so, using the &lt;code&gt;PNGenc&lt;/code&gt; library:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18ostp7z2ady52zvrlsl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F18ostp7z2ady52zvrlsl.png" alt="test image" width="128" height="128"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;PNGenc.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;SD.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;PNG&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Sd2Card&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;SdVolume&lt;/span&gt; &lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;chipSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;span class="cp"&gt;#define WIDTH 128
#define HEIGHT 128
&lt;/span&gt;
&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;ucPal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// black, green&lt;/span&gt;
&lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;ucAlphaPal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;        &lt;span class="c1"&gt;// first color (black) is fully transparent&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iDataSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="n"&gt;ucLine&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;WIDTH&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;115200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting SD card init..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SPI_HALF_SPEED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chipSelect&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SD card initialization failed!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// try to open the 'volume'/'partition' - it should be FAT16 or FAT32&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not find FAT16/FAT32 partition."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chipSelect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SD initialization complete!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;micros&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/testimg.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myClose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myRead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myWrite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mySeek&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This is a lightly-edited version of the &lt;a href="https://github.com/bitbank2/PNGenc/blob/master/examples/PNGenc_Test/PNGenc_Test.ino" rel="noopener noreferrer"&gt;example sketch from the PNGenc&lt;/a&gt; repository.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Reading an Image from an SD Card
&lt;/h2&gt;

&lt;p&gt;Now that we can write a simple PNG image, let's see how we might &lt;strong&gt;read PNGs&lt;/strong&gt; using the &lt;code&gt;PNGdec&lt;/code&gt; library.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your mileage may vary, but at the time of this writing, both &lt;code&gt;PNGenc&lt;/code&gt; and &lt;code&gt;PNGdec&lt;/code&gt; cannot be used at the same time in the same project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following sketch will &lt;strong&gt;read all PNG files from the root directory&lt;/strong&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;PNGdec.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;SD.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;PNG&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Sd2Card&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;SdVolume&lt;/span&gt; &lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;chipSelect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;A3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;115200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Starting SD card init..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SPI_HALF_SPEED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chipSelect&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SD card initialization failed!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// try to open the 'volume'/'partition' - it should be FAT16 or FAT32&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not find FAT16/FAT32 partition."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chipSelect&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SD initialization complete!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;myOpen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Attempting to open %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;myPNG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;myClose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="nf"&gt;myRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PNGFILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint8_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="nf"&gt;mySeek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PNGFILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;myPNG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Function to draw pixels to the display&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;PNGDraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PNGDRAW&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pDraw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;usPixels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLineAsRGB565&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pDraw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usPixels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PNG_RGB565_LITTLE_ENDIAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0xffffffff&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Main loop, scan for all .PNG files on the card and display them&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filecount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;File&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;openNextFile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDirectory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;strcmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"PNG"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myOpen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myClose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;myRead&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mySeek&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PNGDraw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;PNG_SUCCESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"image specs: (%d x %d), %d bpp, pixel type: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getWidth&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getHeight&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getBpp&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getPixelType&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
                    &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="c1"&gt;// do something...anything..with the PNG file!&lt;/span&gt;
                    &lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And what does the output look like in the serial monitor?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;File: TESTIMG.PNG
Attempting to open TESTIMG.PNG
image specs: (128 x 128), 8 bpp, pixel type: 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success! 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;As mentioned above, every project scenario is different (whether you're just reading images pre-loaded to the SD card, &lt;a href="https://www.hackster.io/rob-lauer/id-chicken-eggs-with-thermal-images-ml-and-cellular-iot-748c88" rel="noopener noreferrer"&gt;creating PNGs from a thermal camera module&lt;/a&gt;, or most likely taking a picture with an attached camera module and storing them as JPEGs).&lt;/p&gt;

&lt;p&gt;Including a Micro SD card as a data logger is a simple and effective way to store data (which &lt;strong&gt;of course&lt;/strong&gt; you'll later end up syncing to the cloud via &lt;a href="https://shop.blues.io/collections/notecard" rel="noopener noreferrer"&gt;Cellular, Wi-Fi, or LoRa using the Notecard&lt;/a&gt;!).&lt;/p&gt;

&lt;p&gt;Happy Hacking! 💙&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>iot</category>
      <category>c</category>
    </item>
    <item>
      <title>When to Use Tadiran (Li-SOCl2) vs Lithium Polymer (LiPo) Batteries in IoT Applications</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Mon, 10 Jul 2023 17:52:26 +0000</pubDate>
      <link>https://dev.to/blues/when-to-use-tadiran-li-socl2-vs-lithium-polymer-lipo-batteries-in-iot-applications-3da7</link>
      <guid>https://dev.to/blues/when-to-use-tadiran-li-socl2-vs-lithium-polymer-lipo-batteries-in-iot-applications-3da7</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In this article, we will perform a high-level comparison of Tadiran (i.e. Li-SOCl2) batteries and Lithium Polymer (LiPo) batteries, helping you to decide which is best suited for your deployment scenario.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Specifically, we will answer the following questions:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are Tadiran Li-SOCl2 batteries?&lt;/li&gt;
&lt;li&gt;What are LiPo batteries?&lt;/li&gt;
&lt;li&gt;What are the optimal temperature ranges for Tadiran and LiPo batteries?&lt;/li&gt;
&lt;li&gt;What are the self-discharge rates of Tadiran and LiPo batteries?&lt;/li&gt;
&lt;li&gt;What are some valid use cases for Tadiran vs LiPo batteries?&lt;/li&gt;
&lt;li&gt;And why aren't we talking about alkaline batteries!?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a Tadiran Battery?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tadiranbat.com/" rel="noopener noreferrer"&gt;Tadiran&lt;/a&gt; is the brand name of a type of Lithium Thionyl Chloride (Li-SOCl2) battery. Tadiran Li-SOCl2 batteries are known for their &lt;strong&gt;long shelf-life&lt;/strong&gt; (up to 40 years in optimal conditions), relatively &lt;strong&gt;high voltage&lt;/strong&gt; (3.6V), &lt;strong&gt;low self-discharge rates&lt;/strong&gt; (~0.7% per year), and the ability to operate in &lt;strong&gt;extreme temperature ranges&lt;/strong&gt; (-80°C to 125°C).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yes, Tadiran likes to acknowledge these "extreme" capabilities in their marketing materials!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufyd9wrli7ngnn4dtvnn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufyd9wrli7ngnn4dtvnn.png" alt="tadiran extreme conditions" width="800" height="1022"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: While known for their Li-SOCl2 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-SOCl2 batteries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While Li-SOCl2 Tadirans are &lt;strong&gt;not rechargeable&lt;/strong&gt;, 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.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do Li-SOCl2 Tadiran Batteries Work?
&lt;/h3&gt;

&lt;p&gt;Li-SOCl2 battery technology takes advantage of the high reactivity of lithium, the lightest of all metals, and a highly electronegative chlorinated compound, thionyl chloride (SOCl2).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vxn7xcsf9b0r854op0o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5vxn7xcsf9b0r854op0o.jpg" alt="li-socl2 battery structure" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Two types of Li-SOCl2 battery structures. Image credit &lt;a href="http://www.gebc-energy.com/" rel="noopener noreferrer"&gt;gebc-energy.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros and Cons of Li-SOCl2 Tadiran Batteries
&lt;/h3&gt;

&lt;p&gt;Li-SOCl2 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 &lt;strong&gt;fewer batteries to be used in a series&lt;/strong&gt;, potentially saving space and reducing complexity in the design of device enclosures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1d000rf0t2fa63x8aimr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1d000rf0t2fa63x8aimr.jpg" alt="tadiran plusespulse series" width="267" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Tadiran PlusesPulse series of Li-SOCl2 batteries.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, Li-SOCl2 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-SOCl2 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).&lt;/p&gt;

&lt;h3&gt;
  
  
  Common IoT Applications for Li-SOCl2 Tadiran Batteries
&lt;/h3&gt;

&lt;p&gt;Thanks to their unique properties, Li-SOCl2 Tadiran batteries are often found in a variety of IoT deployments, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Utility Metering Systems:&lt;/strong&gt; Gas, water, and electricity metering systems often use Tadirans due to their long operational lifespan and reliability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Emergency Devices:&lt;/strong&gt; Devices like fire/smoke detectors and alarm systems often use these batteries due to their long shelf-life.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Edge Computing Devices:&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When it comes to integrating with the &lt;a href="https://blues.io/products/notecard/" rel="noopener noreferrer"&gt;Blues Notecard&lt;/a&gt;, &lt;a href="https://shop.blues.io/collections/accessories/products/tadiran-lithium-battery" rel="noopener noreferrer"&gt;this specific Tadiran&lt;/a&gt; is ideal for the high current pulses the Notecard requires when used in a region that requires access to a GSM network:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frw7yytit7gx3w67btzi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frw7yytit7gx3w67btzi6.png" alt="tadiran blues" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Blues version of the Tadiran TLP-93111 battery.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; 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 &lt;a href="https://blues.io/products/notecarrier/" rel="noopener noreferrer"&gt;Notecarriers&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is a Lithium Polymer (LiPo) Battery?
&lt;/h2&gt;

&lt;p&gt;Lithium Polymer (LiPo) batteries are a type of rechargeable battery that utilize a &lt;em&gt;polymer electrolyte&lt;/em&gt; instead of a &lt;em&gt;liquid electrolyte&lt;/em&gt; found in other lithium-ion batteries. This unique composition allows LiPo batteries to be &lt;strong&gt;lightweight and flexible&lt;/strong&gt;, making them a popular choice for low-cost powering of embedded/IoT devices, consumer electronics, and devices that require custom battery sizes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkk14q7u3hrqvszzxh6b9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkk14q7u3hrqvszzxh6b9.png" alt="example lipo battery" width="800" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An example LiPo battery.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How do LiPo Batteries Work?
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmjhqkewak4fj0fgzfse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsmjhqkewak4fj0fgzfse.png" alt="lipo battery structure" width="408" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;LiPo battery structure. Image credit &lt;a href="https://www.dnkpower.com/lithium-polymer-battery-guide/" rel="noopener noreferrer"&gt;dnkpower.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros and Cons of LiPo Batteries
&lt;/h3&gt;

&lt;p&gt;Like other lithium-ion batteries, LiPo batteries have a &lt;strong&gt;high energy density&lt;/strong&gt;, meaning they can store a large amount of energy for their size and weight. Unlike Tadiran batteries, LiPo batteries &lt;strong&gt;can be recharged hundreds of times&lt;/strong&gt;, making them a popular choice for many electronics devices. LiPo batteries are also &lt;strong&gt;lightweight&lt;/strong&gt;, can deliver power quickly with high discharge rates, and as mentioned are very &lt;strong&gt;flexible in their shape and size&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sfzve7gjbm6cnsou8s8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sfzve7gjbm6cnsou8s8.jpg" alt="unique lipo battery shapes" width="735" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;LiPo batteries can be manufactured in virtually any shape or size. Image credit &lt;a href="https://www.lipolbattery.com/Novel-LiPo-Battery.html" rel="noopener noreferrer"&gt;lipolbattery.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Finally, while LiPo batteries can be recharged many times, each charge and discharge cycle &lt;em&gt;slightly&lt;/em&gt; reduces their capacity. After numerous cycles, the maximum capacity will be significantly reduced, at which point the battery may need to be replaced.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common IoT Applications for LiPo Batteries
&lt;/h3&gt;

&lt;p&gt;While there is plenty of overlap between LiPo and Li-SOCl2 Tadiran batteries in terms of use cases, lower-power and more cost-sensitive applications tend to use LiPo battery technology:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remote Monitoring Systems:&lt;/strong&gt; 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).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Smart Agriculture:&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backing Up Home Automation Devices:&lt;/strong&gt; 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.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So...Which Should I Choose?
&lt;/h2&gt;

&lt;p&gt;The answer, as you may expect, is: "it depends on your use case!". Which of the following are most important for your deployment scenario?&lt;/p&gt;

&lt;h3&gt;
  
  
  Operating Temperature Range
&lt;/h3&gt;

&lt;p&gt;When comparing the operating temperature ranges of Tadiran and LiPo batteries, &lt;strong&gt;Tadiran batteries have a clear advantage&lt;/strong&gt; of -80°C to 125°C. Their Li-SOCl2 chemistry enables them to perform well in extreme temperatures, making them suitable for applications in harsh environments.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;✅ Tadiran Li-SOCl2 ⛔️ LiPo&lt;/p&gt;

&lt;h3&gt;
  
  
  Longevity (Self-Discharge Rate)
&lt;/h3&gt;

&lt;p&gt;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. &lt;strong&gt;Tadiran batteries offer an extremely low self-discharge rate&lt;/strong&gt; (~0.7% per year) while LiPo batteries self-discharge at a rate of up to 5% per month when not in use.&lt;/p&gt;

&lt;p&gt;✅ Tadiran Li-SOCl2 ⛔️ LiPo&lt;/p&gt;

&lt;h3&gt;
  
  
  Flexibility
&lt;/h3&gt;

&lt;p&gt;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).&lt;/p&gt;

&lt;p&gt;⛔️ Tadiran Li-SOCl2 ✅ LiPo&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost
&lt;/h3&gt;

&lt;p&gt;Comparing the cost of Tadiran Li-SOCl2 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!).&lt;/p&gt;

&lt;p&gt;In general, Tadiran Li-SOCl2 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, &lt;strong&gt;LiPo tends to come out ahead when it comes to total cost of ownership&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING:&lt;/strong&gt; 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.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;⛔️ Tadiran Li-SOCl2 ✅ LiPo&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Alkaline Batteries?
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjd4clhiywyp5e1dx526.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnjd4clhiywyp5e1dx526.png" alt="duracell aa batteries" width="800" height="685"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The venerable Duracell AA alkaline battery. Image credit &lt;a href="https://www.duracell.com/" rel="noopener noreferrer"&gt;Duracell&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When choosing between Tadiran Li-SOCl2 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;If you need help determining the best battery tech for your individual solution, feel free to reach out via the &lt;a href="https://discuss.blues.io/" rel="noopener noreferrer"&gt;Blues community forum&lt;/a&gt;. 🔋&lt;/p&gt;

</description>
      <category>battery</category>
      <category>lipo</category>
      <category>power</category>
      <category>iot</category>
    </item>
    <item>
      <title>Use Cell Tower and Wi-Fi Triangulation to Achieve Pin-Point Locations, without GPS</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Thu, 04 Aug 2022 19:00:14 +0000</pubDate>
      <link>https://dev.to/blues/use-cell-tower-and-wi-fi-triangulation-to-achieve-pin-point-locations-without-gps-4ko5</link>
      <guid>https://dev.to/blues/use-cell-tower-and-wi-fi-triangulation-to-achieve-pin-point-locations-without-gps-4ko5</guid>
      <description>&lt;p&gt;Many IoT solutions include one basic requirement: to know precisely &lt;em&gt;where&lt;/em&gt; they are in the world.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzq0z4r3ztyje6u08wmtj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzq0z4r3ztyje6u08wmtj.gif" alt="where in the world am i" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While ascertaining location is usually handled with a GNSS/GPS module, there are scenarios where GPS is not reliable or practical. In fact, I'll give you three exact scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Indoor deployments where getting a GPS satellite fix is impossible.&lt;/li&gt;
&lt;li&gt;Use of a physical enclosure that prevents a GPS fix.&lt;/li&gt;
&lt;li&gt;Low-power solutions that can't afford the power drain of GPS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here at Blues Wireless, when we talk about gathering lat/lon coordinates, we usually refer people to the onboard GPS capabilities of the &lt;a href="https://blues.io/products/notecard/" rel="noopener noreferrer"&gt;Cellular Notecard&lt;/a&gt;, which we highly recommend as the first (and often only) way of ascertaining location.&lt;/p&gt;

&lt;p&gt;However, today I want to look more closely at a lesser-known capability of the Notecard: &lt;a href="https://dev.blues.io/notecard/notecard-walkthrough/time-and-location-requests/#using-cell-tower-and-wi-fi-triangulation" rel="noopener noreferrer"&gt;Cell Tower and Wi-Fi Triangulation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triangulation
&lt;/h2&gt;

&lt;p&gt;If you've taken basic trigonometry or geometry courses, you already know that triangulation is the process of determining a location by forming triangles to the point of interest, from a series of other known points.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16xm3pk7exw2ho5oy369.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16xm3pk7exw2ho5oy369.png" alt="cell tower triangulation example" width="300" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Notecard provides built-in capabilities of utilizing triangulation in a few different ways: cell tower triangulation, Wi-Fi triangulation, or both!&lt;/p&gt;

&lt;h2&gt;
  
  
  Cell Tower Location
&lt;/h2&gt;

&lt;p&gt;If you're experienced with the Cellular Notecard, you've likely noticed the &lt;code&gt;tower_&lt;/code&gt; parameters that show up in Notehub events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"tower_when": 1656000634,
"tower_lat": 43.074087500000005,
"tower_lon": -89.44282812499999,
"tower_country": "US",
"tower_location": "Shorewood Hills WI",
"tower_timezone": "America/Chicago",
"tower_id": "310,410,17169,77315594",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;tower_lat&lt;/code&gt; and &lt;code&gt;tower_lon&lt;/code&gt; values are an approximate location of the &lt;strong&gt;single cell tower&lt;/strong&gt; this Notecard was connected to at the given &lt;code&gt;tower_when&lt;/code&gt; timestamp. While this is a nice (rough) location, it's not useful on its own when trying to determine the precise location of a device.&lt;/p&gt;

&lt;p&gt;For example, here is the approximate position of the cell tower used in a recent request, relative to my location:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnznr85vp8gn0lcfgznog.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnznr85vp8gn0lcfgznog.png" alt="single cell tower location with notecard" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cell Tower Triangulation
&lt;/h2&gt;

&lt;p&gt;In scenarios where GPS is unusable, performing &lt;strong&gt;cell tower triangulation&lt;/strong&gt; with the Notecard can provide a massive improvement, thanks to the magic of the &lt;a href="https://dev.blues.io/reference/notecard-api/card-requests/#card-triangulate" rel="noopener noreferrer"&gt;card.triangulate&lt;/a&gt; API.&lt;/p&gt;

&lt;p&gt;To enable cell tower triangulation on a Cellular Notecard, simply issue this request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "req": "card.triangulate",
  "mode": "cell"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next time the Notecard establishes a &lt;em&gt;new session&lt;/em&gt; with Notehub, information about every &lt;em&gt;visible&lt;/em&gt; cell tower will be uploaded and processed. A new set of location data will appear in every Notehub event, prepended with &lt;code&gt;tri_&lt;/code&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"tri_when": 1656011112,
"tri_lat": 43.07113895,
"tri_lon": -89.43272533,
"tri_location": "Shorewood Hills WI",
"tri_country": "US",
"tri_timezone": "America/Chicago",
"tri_points": 16,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the updated location estimate, based on cell tower triangulation only:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bd42x75x3nc8kmv4qcj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bd42x75x3nc8kmv4qcj.png" alt="cell tower triangulation with notecard" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the actual location has improved significantly, and will only improve as more cell towers are visible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; It's important to know that cell tower triangulation comes with a non-trivial power penalty because the modem scan for nearby cell towers can take almost as long as an entire sync. Triangulation also only occurs during a connection to Notehub and there is no way to "re-triangulate" during a &lt;code&gt;continuous&lt;/code&gt; Notehub session.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Wi-Fi Triangulation
&lt;/h2&gt;

&lt;p&gt;If your host MCU has an onboard Wi-Fi module (like the ESP32), you can perform a Wi-Fi access point (AP) scan and send this data to the Notecard to perform Wi-Fi triangulation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Wi-Fi triangulation is an experimental, technical preview feature from Blues Wireless that is free &lt;br&gt;
for use today, but may use &lt;a href="https://blues.io/pricing/" rel="noopener noreferrer"&gt;Consumption Credits&lt;/a&gt; in &lt;br&gt;
the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The format of the &lt;code&gt;card.triangulate&lt;/code&gt; call is a little different when using Wi-Fi triangulation. Note that you can use &lt;code&gt;cell&lt;/code&gt;, &lt;code&gt;wifi&lt;/code&gt;, or both with &lt;code&gt;wifi,cell&lt;/code&gt; when enabling triangulation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "req": "card.triangulate",
  "mode": "wifi",
  "text": "+CWLAP:(4,\"Blues\",-51,\"74:ac:b9:12:12:f8\",1)\n+CWLAP:(3,\"AAAA-62DD\",-70,\"6c:55:e8:91:62:e1\",11)\n+CWLAP:(4,\"Blues\",-81,\"74:ac:b9:11:12:23\",1)\n+CWLAP:(4,\"Blues\",-82,\"74:ac:a9:12:19:48\",11)\n+CWLAP:(4,\"Free Parking\",-83,\"02:18:4a:11:60:31\",6)\n+CWLAP:(5,\"GO\",-84,\"01:13:6a:13:90:30\",6)\n+CWLAP:(4,\"AAAA-5C62-2.4\",-85,\"d8:97:ba:7b:fd:60\",1)\n+CWLAP:(3,\"DIRECT-a5-HP MLP50\",-86,\"fa:da:0c:1b:16:a5\",6)\n+CWLAP:(3,\"DIRECT-c6-HP M182 LaserJet\",-88,\"da:12:65:44:31:c6\",6)\n\n"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's that scary-looking &lt;code&gt;text&lt;/code&gt; field? It's a newline-terminated list of Wi-Fi access points that follows a pattern similar to the &lt;a href="https://docs.espressif.com/projects/esp-at/en/latest/esp32/AT_Command_Set/Wi-Fi_AT_Commands.html#id17" rel="noopener noreferrer"&gt;ESP32's AT+CWLAP command output&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deriving Access Point Data with Arduino
&lt;/h3&gt;

&lt;p&gt;If you're comfortable issuing AT commands to your host MCU, you can send &lt;code&gt;AT+CWLAP&lt;/code&gt; and format the response per the above requirements. However, not many of us are so cozy with AT syntax, which is why we provide the &lt;br&gt;
&lt;a href="https://github.com/blues/notecard-aux-wifi" rel="noopener noreferrer"&gt;Notecard Auxiliary Wi-Fi Arduino library&lt;/a&gt; &lt;br&gt;
for an easier way of programmatically pulling a list of Wi-Fi access points with &lt;br&gt;
your Wi-Fi enabled host MCU, and sending them to the Notecard in one command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Update Notecard Triangulation Data
aux_wifi.updateTriangulationData();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deriving Access Point Data with CircuitPython
&lt;/h3&gt;

&lt;p&gt;Developing with &lt;a href="https://circuitpython.org/" rel="noopener noreferrer"&gt;CircuitPython&lt;/a&gt;? Here is an example function that uses the built-in &lt;code&gt;wifi&lt;/code&gt; library to gather the same WAP data when used with an ESP32 MCU:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import wifi
import binascii

def get_wifi_access_points():
    """ returns a set of visible access points, corresponds to esp32 AT+CWLAP format """
    # https://docs.espressif.com/projects/esp-at/en/latest/esp32/AT_Command_Set/Wi-Fi_AT_Commands.html#cmd-lap

    all_wifi_aps = ""

    for network in wifi.radio.start_scanning_networks():

        wifi_ap = "+CWLAP:("

        if wifi.AuthMode.ENTERPRISE in network.authmode:
            wifi_ap += "5"
        elif wifi.AuthMode.PSK in network.authmode:
            wifi_ap += "6"
        elif wifi.AuthMode.WPA3 in network.authmode:
            wifi_ap += "6"
        elif wifi.AuthMode.WPA2 in network.authmode:
            wifi_ap += "3"
        elif wifi.AuthMode.WPA in network.authmode:
            wifi_ap += "2"
        elif wifi.AuthMode.WEP in network.authmode:
            wifi_ap += "1"
        else:
            wifi_ap += "0"

        bssid = binascii.hexlify(network.bssid).decode("ascii")
        bssid = ':'.join(bssid[i:i+2] for i in range(0,12,2))

        wifi_ap = wifi_ap + ",\"" + str(network.ssid) + "\"," + str(network.rssi) + ",\"" + bssid + "\"," + str(network.channel) + ")\n"

        all_wifi_aps += wifi_ap

    wifi.radio.stop_scanning_networks()

    return all_wifi_aps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wi-Fi Triangulation in Action
&lt;/h3&gt;

&lt;p&gt;How different are my results when using Wi-Fi triangulation? Well the only access point my ESP32 was able to find was my own router. However, even with just that one AP, here are the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"tri_when": 1656011112,
"tri_lat": 43.07113895,
"tri_lon": -89.43272533,
"tri_location": "Shorewood Hills WI",
"tri_country": "US",
"tri_timezone": "America/Chicago",
"tri_points": 16,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which becomes amazingly accurate when displayed on a map!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqluf3c551pr9f5k4pkct.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqluf3c551pr9f5k4pkct.png" alt="wi-fi triangulation with notecard" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wi-Fi triangulation becomes &lt;strong&gt;even more valuable&lt;/strong&gt; when you realize the power requirements are minimal, and it adds only 1-2 seconds to the sync time with Notehub.&lt;/p&gt;

&lt;p&gt;Be sure to consult the developer documentation on &lt;a href="https://dev.blues.io/notecard/notecard-walkthrough/time-and-location-requests/#using-cell-tower-and-wi-fi-triangulation" rel="noopener noreferrer"&gt;Cell Tower and Wi-Fi Triangulation&lt;/a&gt; as there are additional settings in Notehub that you may want to change, depending on how you use triangulation in your solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triangulation with Wi-Fi Notecard
&lt;/h2&gt;

&lt;p&gt;Up until now, all of these instructions have revolved around using the Cellular Notecard. What about if you're using the &lt;a href="https://blues.io/products/wifi-notecard/" rel="noopener noreferrer"&gt;Wi-Fi Notecard&lt;/a&gt; (which doesn't have an onboard GNSS/GPS module)?&lt;/p&gt;

&lt;p&gt;Luckily enough, Wi-Fi triangulation is enabled on the Wi-Fi Notecard by default, no configuration changes required!&lt;/p&gt;

&lt;p&gt;After setting up a Wi-Fi Notecard, here were the results in a given event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"tri_when": 47,
"tri_lat": 43.07121864,
"tri_lon": -89.43263674,
"tri_location": "Shorewood Hills WI",
"tri_country": "US",
"tri_timezone": "America/Chicago",
"tri_points": 3,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which, again, when plotted on a map, comes shockingly close to landing on my location:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau4jgrekg2v10fwhvlw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fau4jgrekg2v10fwhvlw4.png" alt="triangulation with wi-fi notecard" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;While GPS is the de facto standard for identifying the precise location of an IoT device, and should always be the first option when gathering location data, the Notecard's triangulation capabilities provide options when GPS isn't available.&lt;/p&gt;

&lt;p&gt;Happy Mapping with the Notecard and Notehub! 📍🗺&lt;/p&gt;

</description>
      <category>gps</category>
      <category>cellular</category>
      <category>iot</category>
    </item>
    <item>
      <title>Easiest Way to Add Cellular to an ESP32 IoT Project</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Mon, 02 May 2022 18:45:55 +0000</pubDate>
      <link>https://dev.to/blues/easiest-way-to-add-cellular-to-an-esp32-iot-project-10nm</link>
      <guid>https://dev.to/blues/easiest-way-to-add-cellular-to-an-esp32-iot-project-10nm</guid>
      <description>&lt;p&gt;I've come to appreciate the &lt;a href="https://en.wikipedia.org/wiki/ESP32" rel="noopener noreferrer"&gt;ESP32&lt;/a&gt; line of microcontrollers from &lt;a href="https://www.espressif.com/" rel="noopener noreferrer"&gt;Espressif Systems&lt;/a&gt;, mostly due to a combination of low-cost, low-power, yet solid MCU performance. In particular, the ESP32-based &lt;a href="https://www.adafruit.com/product/3405" rel="noopener noreferrer"&gt;HUZZAH32 Feather from Adafruit&lt;/a&gt; is a board you'll routinely see in some &lt;a href="https://www.hackster.io/blues-wireless" rel="noopener noreferrer"&gt;tutorials we post on Hackster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyiirqff1a6pxe6yfbh0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhyiirqff1a6pxe6yfbh0.jpg" alt="adafruit huzzah 32 feather" width="800" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adafruit HUZZAH32&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While Wi-Fi and Bluetooth are integrated into some ESP32 MCUs, adding cellular to the mix broadens the deployment options of any IoT project. Having a &lt;a href="https://blues.io/blog/network-connectivity/?utm_source=devto" rel="noopener noreferrer"&gt;combination of connectivity options&lt;/a&gt; also enables you to use Wi-Fi when available, but fall back on cellular should Wi-Fi be unavailable or the physical location require it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developer-Friendly Cellular with the Notecard
&lt;/h2&gt;

&lt;p&gt;The key to success with cellular IoT connectivity on the ESP32 is a secure and reliable System-on-Module, the &lt;a href="https://blues.io/products/notecard/?utm_source=devto" rel="noopener noreferrer"&gt;Blues Wireless Notecard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwizm8cy85hxggoaer8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuwizm8cy85hxggoaer8b.png" alt="blues wireless cellular notecard" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Notecard is a 30mm x 35mm device-to-cloud data pump. With the included cellular and GPS capabilities (and a &lt;a href="https://blues.io/products/wifi-notecard/?utm_source=devto" rel="noopener noreferrer"&gt;Wi-Fi option&lt;/a&gt;), the Notecard is an easy choice for securely syncing data with the cloud over a variety of global cellular protocols (specifically Cat-1, LTE-M, and NB-IoT).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the cost?&lt;/strong&gt; The Notecard comes &lt;em&gt;prepaid&lt;/em&gt; with 10 years of &lt;a href="https://dev.blues.io/hardware/notecard-datasheet/note-nbgl-500/?utm_source=devto#cellular-service" rel="noopener noreferrer"&gt;global service&lt;/a&gt; and 500MB of data, starting at just $49.&lt;/p&gt;

&lt;p&gt;But...what about the M.2 edge connector on the Notecard? How does that connect to an ESP32 microcontroller?&lt;/p&gt;

&lt;p&gt;Probably not like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52du2h1xex87c9iga6o6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52du2h1xex87c9iga6o6.jpg" alt="not the way to connect notecard and esp32 huzzah feather" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy Integration Options
&lt;/h2&gt;

&lt;p&gt;Let me introduce you to the &lt;a href="https://blues.io/products/notecarrier/?utm_source=devto" rel="noopener noreferrer"&gt;Blues Wireless Notecarriers&lt;/a&gt;. These are development boards that make it dead simple to &lt;strong&gt;connect virtually any MCU or SBC to a Notecard&lt;/strong&gt;. You place the Notecard into the provided M.2 slot and the Notecarrier exposes all of the pins on the Notecard to the MCU.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcalgvt9fswef5vc3jrt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcalgvt9fswef5vc3jrt.jpg" alt="notecard and notecarrier-a" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it gets even easier if you're using a Feather-based ESP32 (like I often do). The &lt;a href="https://blues.io/products/notecarrier/notecarrier-af/?utm_source=devto" rel="noopener noreferrer"&gt;Notecarrier-AF&lt;/a&gt; (part of the &lt;a href="https://shop.blues.io/products/feather-starter-kit?utm_source=devto" rel="noopener noreferrer"&gt;Feather Starter kit&lt;/a&gt;) has a Feather-compatible socket so you can insert your MCU directly onto the board, for wire-free connectivity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7ls6vswibfxg57cpkr0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa7ls6vswibfxg57cpkr0.png" alt="notecarrier-af" width="762" height="886"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Notecarrier-AF also includes onboard antennas, two Grove I2C ports, and JST connectors for solar and/or LiPo batteries.&lt;/p&gt;

&lt;p&gt;Notecard + Notecarrier-AF + ESP32 = ❤️&lt;/p&gt;

&lt;h2&gt;
  
  
  From Device To Cloud, Securely
&lt;/h2&gt;

&lt;p&gt;A key component of the Notecard's security architecture is that it doesn't live on the public Internet. &lt;strong&gt;The Notecard doesn't have a publicly-accessible IP address.&lt;/strong&gt; You must use a proxy, accessed via private VPN tunnels, to communicate between the Notecard and your cloud application.&lt;/p&gt;

&lt;p&gt;This is where the Blues Wireless cloud service &lt;a href="https://blues.io/services/?utm_source=devto" rel="noopener noreferrer"&gt;Notehub&lt;/a&gt; comes into play.&lt;/p&gt;

&lt;p&gt;Notehub is a thin cloud service that securely (over TLS) receives, processes, and &lt;strong&gt;routes data to your cloud endpoint of choice&lt;/strong&gt;. This could be a big cloud like AWS, Azure, or Google Cloud - or any IoT-optimized platform like Losant, Datacake, or Ubidots. Your own self-hosted custom RESTful API is fine as well!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcr6gw1zi1rd9w2868r18.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcr6gw1zi1rd9w2868r18.jpg" alt="blues wireless notehub and notecard data flow" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an added bonus, Notehub provides OTA firmware update capabilities along with full device fleet management and &lt;a href="https://dev.blues.io/notecard/notecard-guides/understanding-environment-variables/?utm_source=devto" rel="noopener noreferrer"&gt;cloud-based environment variables&lt;/a&gt; for easily sharing data across fleets of Notecards.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Notecard, and When Not
&lt;/h2&gt;

&lt;p&gt;It's important to note that the cellular Notecard is a &lt;strong&gt;low-power, low-bandwidth, and high-latency&lt;/strong&gt; device. Its design center is focused on sending small packets of data (e.g. accumulated sensor readings, generated ML inferences, and the like) on an occasional cadence to the cloud.&lt;/p&gt;

&lt;p&gt;👍 Scenarios when the Notecard makes sense:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Any low-bandwidth transfer of data over Cat-1, LTE-M, or NB-IoT cellular&lt;/li&gt;
&lt;li&gt;Low-power edge computing deployments (agriculture, smart meters, environmental monitoring, and so on)&lt;/li&gt;
&lt;li&gt;High-latency remote control solutions&lt;/li&gt;
&lt;li&gt;When secure and encrypted communications are critical&lt;/li&gt;
&lt;li&gt;When turnkey cloud integrations are desired&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;👎 Scenarios when the Notecard doesn't quite work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;As an "always connected" drop-in replacement for Wi-Fi&lt;/li&gt;
&lt;li&gt;When you need sub-millisecond latency&lt;/li&gt;
&lt;li&gt;Streaming HD video or high res images&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Ready to See How it All Works?
&lt;/h2&gt;

&lt;p&gt;With a &lt;a href="https://shop.blues.io/products/feather-starter-kit?utm_source=devto" rel="noopener noreferrer"&gt;Blues Wireless Feather Starter Kit for ESP32&lt;/a&gt; you can follow the remainder of this blog post for a quick and easy getting started experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Be sure to &lt;a href="https://dev.blues.io/?utm_source=devto" rel="noopener noreferrer"&gt;browse our full developer site&lt;/a&gt; for Notecard datasheets and additional technical information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After creating a free account at &lt;a href="https://notehub.io/" rel="noopener noreferrer"&gt;Notehub.io&lt;/a&gt; and initializing your first project, copy the provided unique &lt;a href="https://dev.blues.io/reference/glossary/?utm_source=devto#productuid" rel="noopener noreferrer"&gt;ProductUID&lt;/a&gt; from the project and save it for the next step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qf81ivuby7zwcuav6ba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qf81ivuby7zwcuav6ba.png" alt="getting your productuid from notehub" width="736" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming ESP32 Firmware
&lt;/h2&gt;

&lt;p&gt;The ESP32 supports commonly used languages like Arduino/C and MicroPython. While I'm an avid Python lover, most ESP32 developers are likely writing Arduino or C/C++, so we'll stick with that path for now.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.blues.io/reference/notecard-api/introduction/?utm_source=devto" rel="noopener noreferrer"&gt;API that powers the Notecard&lt;/a&gt; is completely JSON-based. So technically speaking, it doesn't matter what programming language you use. We provide SDKs for &lt;a href="https://dev.blues.io/tools-and-sdks/?utm_source=devto" rel="noopener noreferrer"&gt;Arduino, Go, C/C++, and Python&lt;/a&gt; and our community has built SDKs for &lt;a href="https://blues.io/blog/dot-net-developer-iot-too/?utm_source=devto" rel="noopener noreferrer"&gt;.NET&lt;/a&gt; and &lt;a href="https://github.com/gauteh/notecard-rs" rel="noopener noreferrer"&gt;Rust&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Whichever route you take, open up your preferred IDE and paste in this basic sketch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Include the Arduino library for the Notecard
#include &amp;lt;Notecard.h&amp;gt;
#define productUID "com.your-company.your-name:your_project"

Notecard notecard;

void setup() {
  delay(2500);
  Serial.begin(115200);
  notecard.begin();

  J *req = notecard.newRequest("hub.set");
  JAddStringToObject(req, "product", productUID);
  JAddStringToObject(req, "mode", "continuous");
  notecard.sendRequest(req);
}

void loop() {
  J *req = notecard.newRequest("note.add");
  if (req != NULL) {
    JAddStringToObject(req, "file", "sensors.qo");
    JAddBoolToObject(req, "sync", true);

    J *body = JCreateObject();
    if (body != NULL) {
      JAddNumberToObject(body, "temp", 25.6);
      JAddNumberToObject(body, "humidity", 48.2);
      JAddItemToObject(req, "body", body);
    }

    notecard.sendRequest(req);
  }

  delay(15000);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What exactly is happening in this sketch?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are using a &lt;a href="https://dev.blues.io/reference/notecard-api/hub-requests/?utm_source=devto#hub-set" rel="noopener noreferrer"&gt;hub.set&lt;/a&gt; request to associate the Notecard with the Notehub project.&lt;/li&gt;
&lt;li&gt;We are sending a JSON object (a &lt;a href="https://dev.blues.io/reference/glossary/?utm_source=devto#note" rel="noopener noreferrer"&gt;Note&lt;/a&gt;) with mock temperature and humidity data to the cloud, over cellular.&lt;/li&gt;
&lt;li&gt;Wait 15 seconds and do it all again!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can build and upload this sketch to your ESP32 now, then check your Notehub project to see it in the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cellular Data in the Cloud
&lt;/h2&gt;

&lt;p&gt;Once that &lt;code&gt;note.add&lt;/code&gt; request is initiated, your Note is queued on the Notecard for syncing with the cloud. Since we are keeping this example simple, we are also using the &lt;code&gt;sync:true&lt;/code&gt; option in the &lt;code&gt;note.add&lt;/code&gt; request to &lt;em&gt;immediately&lt;/em&gt; initiate a cellular connection and send the data to Notehub ASAP.&lt;/p&gt;

&lt;p&gt;Go ahead and head back to Notehub.io and take a look at the recently uploaded events in the &lt;strong&gt;Event&lt;/strong&gt; panel for your project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftgpildqv0h49n2xq1sis.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftgpildqv0h49n2xq1sis.png" alt="mock sensor data in notehub" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But we don't want your data to live on Notehub! The most important part of the story is &lt;strong&gt;securely routing your data to any cloud endpoint&lt;/strong&gt;. Take a look at our &lt;a href="https://dev.blues.io/guides-and-tutorials/routing-data-to-cloud/?utm_source=devto" rel="noopener noreferrer"&gt;extensive Routing Tutorials&lt;/a&gt; and pick your favorite cloud, such as &lt;a href="https://ubidots.com/" rel="noopener noreferrer"&gt;Ubidots&lt;/a&gt;, and create an engaging cloud dashboard to interpret your data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9n9gr588xznsbms5si1x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9n9gr588xznsbms5si1x.gif" alt="example ubidots dashboard" width="600" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; Communication with the Notecard is &lt;em&gt;bi-directional&lt;/em&gt; in nature. This means the Notecard can also &lt;em&gt;receive&lt;/em&gt; data from the cloud. See &lt;a href="https://dev.blues.io/notecard/notecard-walkthrough/inbound-requests-and-shared-data/?utm_source=devto" rel="noopener noreferrer"&gt;Inbound Requests &amp;amp; Shared Data Guide&lt;/a&gt; for more details.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;We've barely even started our journey with cellular IoT and the ESP32! Hopefully you've seen that with minimal hardware and minimal firmware, you can easily add cellular connectivity to a new or existing IoT project.&lt;/p&gt;

&lt;p&gt;Looking for some good next steps?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you haven't already, pick up your own &lt;a href="https://shop.blues.io/products/feather-starter-kit?utm_source=devto" rel="noopener noreferrer"&gt;Feather Starter Kit for ESP32&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Next, the &lt;em&gt;real&lt;/em&gt; &lt;a href="https://dev.blues.io/quickstart/notecard-quickstart/?utm_source=devto" rel="noopener noreferrer"&gt;Notecard Quickstart&lt;/a&gt; does a MUCH better job getting you started.&lt;/li&gt;
&lt;li&gt;Consult the &lt;a href="https://dev.blues.io/reference/notecard-api/introduction/?utm_source=devto" rel="noopener noreferrer"&gt;Notecard API reference&lt;/a&gt; to get a full grasp of the capabilities of the Notecard.&lt;/li&gt;
&lt;li&gt;Check out all the awesome &lt;a href="https://www.hackster.io/blues-wireless" rel="noopener noreferrer"&gt;Blues Wireless projects on Hackster&lt;/a&gt; for inspiration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy hacking on the ESP32! 👩‍💻&lt;/p&gt;

</description>
      <category>iot</category>
      <category>cloud</category>
      <category>esp32</category>
    </item>
    <item>
      <title>Thermal Image Anomaly Detection with TinyML</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Wed, 16 Feb 2022 20:55:45 +0000</pubDate>
      <link>https://dev.to/blues/thermal-image-anomaly-detection-with-tinyml-48n0</link>
      <guid>https://dev.to/blues/thermal-image-anomaly-detection-with-tinyml-48n0</guid>
      <description>&lt;p&gt;When I say "anomaly detection" you may imagine an overly complicated process, something exclusive to deep learning algorithms and indecipherable coding. In reality, the concept of uncovering anomalous behavior in a system is really just the act of &lt;em&gt;identifying an unclassified or uncertain state&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://www.hackster.io/rob-lauer/thermal-image-anomaly-detection-with-tinyml-36831c" rel="noopener noreferrer"&gt;this Hackster project&lt;/a&gt;, I created a basic &lt;strong&gt;anomaly detection ML model&lt;/strong&gt; with &lt;a href="https://edgeimpulse.com/" rel="noopener noreferrer"&gt;Edge Impulse&lt;/a&gt; that processes thermal images to detect unknown states of thermal readings and relays collected data to the cloud with the &lt;a href="https://blues.io/products/notecard/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_campaign=featured-project&amp;amp;utm_content=thermal" rel="noopener noreferrer"&gt;Blues Wireless Notecard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/hYVXLxFa8aY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;By using the "edge ML" capabilities provided by Edge Impulse, the cellular Notecard device-to-cloud data pump from Blues Wireless, a &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/" rel="noopener noreferrer"&gt;Raspberry Pi Zero 2 W&lt;/a&gt;, and an &lt;a href="https://ubidots.com/" rel="noopener noreferrer"&gt;Ubidots&lt;/a&gt; dashboard, I was able to create a simple, low-power, thermal monitoring station with only a small bit of Python coding required 🐍.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdt7jvi92vwuaji7of2x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqdt7jvi92vwuaji7of2x.gif" alt="Animation of all the recorded thermal images" width="320" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Animation of all the recorded thermal images.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Be sure to &lt;a href="https://www.hackster.io/rob-lauer/thermal-image-anomaly-detection-with-tinyml-36831c" rel="noopener noreferrer"&gt;view the full tutorial on Hackster&lt;/a&gt; to see how this project was built from scratch!&lt;/p&gt;

</description>
      <category>iot</category>
      <category>machinelearning</category>
      <category>cloud</category>
    </item>
    <item>
      <title>.NET Developer? You're an IoT Developer Too!</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Mon, 10 Jan 2022 18:41:08 +0000</pubDate>
      <link>https://dev.to/blues/net-developer-youre-an-iot-developer-too-3mgn</link>
      <guid>https://dev.to/blues/net-developer-youre-an-iot-developer-too-3mgn</guid>
      <description>&lt;p&gt;Like many software engineers, I cut my proverbial teeth on the web. Showing off cutting edge ASP.NET web apps with the occasional AJAX request and JavaScript &lt;code&gt;mouseover&lt;/code&gt; effect really got heads spinning back in the early `00's!&lt;/p&gt;

&lt;p&gt;Eventually my career took a new path as I found myself in the embedded space, writing firmware with Arduino and CircuitPython to control tiny devices with microcontrollers. While I'm still a complete noob, I've had the pleasure of building everything from a &lt;a href="https://www.hackster.io/rob-lauer/solar-powered-crypto-mining-with-raspberry-pi-64adee" rel="noopener noreferrer"&gt;solar-powered crypto miner&lt;/a&gt;, to a &lt;a href="https://www.hackster.io/rob-lauer/debugging-a-hot-tub-time-series-machine-92e44f" rel="noopener noreferrer"&gt;hot tub water quality monitor&lt;/a&gt;, to a &lt;a href="https://www.hackster.io/rob-lauer/busted-create-an-ml-powered-speed-trap-b1e5d1" rel="noopener noreferrer"&gt;speed trap powered by machine learning&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, I've always lusted after combining my love for C# with the embedded world.&lt;/p&gt;

&lt;p&gt;Lo and behold, I learned about a couple of frameworks geared towards this scenario: &lt;a href="https://www.nanoframework.net/" rel="noopener noreferrer"&gt;.NET nanoFramework&lt;/a&gt; and &lt;a href="https://www.ghielectronics.com/tinyclr/" rel="noopener noreferrer"&gt;TinyCLR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qy0tvkilub7fzg8p32o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qy0tvkilub7fzg8p32o.png" alt="logos for nanoframework and tinyclr os" width="800" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before we look more closely at these tiny .NET frameworks, let's take a short step back and better define the concepts of "embedded development" and "IoT".&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is Embedded Development and the IoT?&lt;/li&gt;
&lt;li&gt;The Tiniest .NET Frameworks&lt;/li&gt;
&lt;li&gt;The "Tiny" Hardware&lt;/li&gt;
&lt;li&gt;Getting Started with TinyCLR OS&lt;/li&gt;
&lt;li&gt;Adding IoT to the Mix&lt;/li&gt;
&lt;li&gt;Next Steps&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Embedded Development and the IoT?
&lt;/h2&gt;

&lt;p&gt;Embedded programming is the concept of writing code for incredibly small computers (a.k.a. microcontrollers) that communicate with sensors (e.g. temperature, gas, humidity) or power servo motors that in turn control other devices. In the embedded space, we often talk about programming "firmware", which is essentially semi-permanent software loaded into non-volatile memory on a device (it's not &lt;strong&gt;soft&lt;/strong&gt;ware or &lt;strong&gt;hard&lt;/strong&gt;ware, it's &lt;strong&gt;firm&lt;/strong&gt;ware!).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;An example programmable microcontroller from Blues Wireless, the Feather-compatible &lt;a href="https://blues.io/products/swan/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Swan&lt;/a&gt;:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpd508xkklclbjalsu3n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpd508xkklclbjalsu3n.jpg" alt="blues wireless feather-compatible swan mcu" width="800" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most common languages used to write firmware for embedded devices are C/C++, Arduino (a simpler form of C), and Python derivatives like &lt;a href="https://micropython.org/" rel="noopener noreferrer"&gt;MicroPython&lt;/a&gt; and &lt;a href="https://circuitpython.org/" rel="noopener noreferrer"&gt;CircuitPython&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Embedded development is directly related to the Internet of Things (IoT) as these "things" we are building often need an Internet connection to upload the data they are gathering or the calculations they are creating.&lt;/p&gt;

&lt;p&gt;For instance a smart...belt?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6kg0vapnxi36phjqezs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6kg0vapnxi36phjqezs.png" alt="smart belt" width="700" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Getting slightly more practical, think about the following common devices, as they are all IoT projects at their core:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Irrigation systems&lt;/strong&gt; that measure rainfall and only turn on when needed;&lt;/li&gt;
&lt;li&gt;Gas/electric &lt;strong&gt;utility meters&lt;/strong&gt; that can relay home energy usage remotely;&lt;/li&gt;
&lt;li&gt;Home health &lt;strong&gt;monitoring devices&lt;/strong&gt; that provide insights previously only available in a hospital setting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what makes the IoT so fascinating: there are countless applications for the IoT to impact our lives in positive ways.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now let's learn how to build something fun with C#!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tiniest .NET Frameworks
&lt;/h2&gt;

&lt;p&gt;Both nanoFramework and TinyCLR are free frameworks for building .NET applications meant for deployment on resource-constrained embedded devices. They make it easy (or at least &lt;em&gt;easier&lt;/em&gt;) to use C# instead of Arduino, C/C++, or a Python derivative when programming firmware on microcontrollers.&lt;/p&gt;

&lt;p&gt;For .NET developers, this opens up an entire new world of embedded development. We can use the tools (Visual Studio) and languages (C#) that we've been using for years, all without worrying about the low-level hardware issues that can easily confound even the most dedicated embedded engineer.&lt;/p&gt;

&lt;p&gt;It does come with a catch though. Neither nanoFramework nor TinyCLR provide access to the full .NET Common Language Runtime (CLR), and they only provide a subset of the .NET base class libraries and APIs. This is primarily because of the memory constraints on these microcontrollers. Specifically for TinyCLR, you can find a &lt;a href="https://docs.ghielectronics.com/software/tinyclr/limitations.html" rel="noopener noreferrer"&gt;list of limitations in their docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What about the differences between nanoFramework and TinyCLR? At a high level they boil down down to the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.NET nanoFramework provides:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Support for a variety of popular ESP32- and STM32-based microcontrollers.&lt;/li&gt;
&lt;li&gt;A fully open source offering.&lt;/li&gt;
&lt;li&gt;Support from the .NET foundation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TinyCLR provides:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tight integration with specific proprietary microcontrollers.&lt;/li&gt;
&lt;li&gt;A superior getting started experience.&lt;/li&gt;
&lt;li&gt;An easier integrated debugging solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I did some experimentation with both platforms, as an IoT and embedded engineer noob, I did find the TinyCLR experience preferable (though I know of plenty of folks who are nanoFramework fans!). Therefore, the rest of this article is going to walk through the experience of getting started with .NET on the TinyCLR OS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Tiny" Hardware
&lt;/h2&gt;

&lt;p&gt;As just mentioned, TinyCLR OS only runs on microcontrollers (MCUs) provided by GHI Electronics. My MCU of choice is for this exercise is the &lt;a href="https://www.ghielectronics.com/sitcore/sbc/" rel="noopener noreferrer"&gt;FEZ Feather&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9webp8rxf2ecglrydabz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9webp8rxf2ecglrydabz.png" alt="fez feather mcu" width="650" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The &lt;em&gt;Feather&lt;/em&gt; aspect comes from the fact that it is compatible with the &lt;a href="https://learn.adafruit.com/adafruit-feather" rel="noopener noreferrer"&gt;Adafruit Feather specification&lt;/a&gt;. This means you can connect it to virtually any carrier board with header pins meant to accept a Feather-compatible MCU.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Getting Started with TinyCLR OS
&lt;/h2&gt;

&lt;p&gt;Let's walk through some (relatively) simple steps to get the TinyCLR OS loaded onto your FEZ Feather microcontroller and write an embedded program using C#.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the TinyCLR OS
&lt;/h3&gt;

&lt;p&gt;Our first step is to install the latest version of the TinyCLR OS on the device. TinyCLR OS includes the .NET CLR which converts your compiled C# into instructions for the microcontroller. It's also used for interacting with Visual Studio (critical for loading the program onto the device and debugging).&lt;/p&gt;

&lt;p&gt;Connect your FEZ Feather to your Windows PC via a USB-C cable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmv64tqhtztajv9mijj1u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmv64tqhtztajv9mijj1u.jpg" alt="connect fez feather" width="700" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Download the latest version of &lt;strong&gt;TinyCLR Config&lt;/strong&gt; and &lt;strong&gt;SITCore SC20xxx&lt;/strong&gt; firmware from the &lt;a href="https://docs.ghielectronics.com/software/tinyclr/downloads.html" rel="noopener noreferrer"&gt;GHI electronics download page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open &lt;strong&gt;TinyCLR Config&lt;/strong&gt;, choose the connected device in the &lt;strong&gt;Port&lt;/strong&gt; dropdown, and click &lt;strong&gt;Connect&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5op2bljyhyvjursrfspc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5op2bljyhyvjursrfspc.png" alt="tinyclr config port" width="496" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To update the device firmware, click on the &lt;code&gt;...&lt;/code&gt; button next to the &lt;strong&gt;Update Firmware&lt;/strong&gt; button. A dialog will open allowing you to select the firmware file you previously downloaded and click on &lt;strong&gt;Update Firmware&lt;/strong&gt; to flash your device:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famgakpwinvtulqgb4wpd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famgakpwinvtulqgb4wpd.png" alt="tinyclr os firmware" width="494" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once it's done, disconnect and reconnect the device and confirm that your firmware was updated:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskw3qacua58vtf9fixff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskw3qacua58vtf9fixff.png" alt="tinyclr confirm firmware" width="497" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Visual Studio
&lt;/h3&gt;

&lt;p&gt;You also need to perform a one-time configuration of Visual Studio (these instructions are compatible with both VS 2019 and 2022 community editions).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; Make sure you've already installed the ".NET Desktop Development" requirements in Visual Studio.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Within Visual Studio, navigate to &lt;strong&gt;Extensions --&amp;gt; Manage Extensions&lt;/strong&gt;. Search for "tinyclr" to find and install the &lt;strong&gt;TinyCLR OS Project System&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhga0swxs58zhv7w41cid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhga0swxs58zhv7w41cid.png" alt="tinyclr visual studio extension" width="661" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Restart Visual Studio to enable the installer to complete the installation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Your First "Tiny" .NET Project
&lt;/h3&gt;

&lt;p&gt;Re-open Visual Studio and create a new project, choosing &lt;strong&gt;TinyCLROS&lt;/strong&gt; from the platform dropdown list and selecting the &lt;strong&gt;C# TinyCLR Application&lt;/strong&gt; template:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2s2lwwy2q0ueu84ls600.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2s2lwwy2q0ueu84ls600.png" alt="tinyclr new visual studio project" width="591" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your project will be pre-loaded with some boilerplate code and a &lt;code&gt;Program.cs&lt;/code&gt; file. This will be the starting point of your application.&lt;/p&gt;

&lt;p&gt;Next, we're going to perform the "Hello World!" of embedded programming: blinking an LED!&lt;/p&gt;

&lt;p&gt;Start by replacing &lt;em&gt;everything&lt;/em&gt; in &lt;code&gt;Program.cs&lt;/code&gt; with the following code. Have no fear, we'll walk through what's happening in this code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Pins;
using System.Threading;

namespace TinyCLRApplication
{
    class Program
    {
        static void Main()
        {
            var LED = GpioController.GetDefault().OpenPin(SC20100.GpioPin.PE11);
            LED.SetDriveMode(GpioPinDriveMode.Output);

            while (true)
            {
                LED.Write(GpioPinValue.High);
                Thread.Sleep(100);

                LED.Write(GpioPinValue.Low);
                Thread.Sleep(100);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;So what exactly is happening in this code?&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the &lt;code&gt;Main()&lt;/code&gt; method we are creating a reference to the onboard LED.&lt;/li&gt;
&lt;li&gt;In &lt;code&gt;LED.SetDriveMode(GpioPinDriveMode.Output)&lt;/code&gt; we are configuring the LED's pin as an &lt;em&gt;output&lt;/em&gt;, meaning it'll be in a state to manage the voltage (i.e. lighting) the LED.&lt;/li&gt;
&lt;li&gt;Next, we get in an infinite loop (don't worry, this is very common in embedded land!) with a &lt;code&gt;while (true)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We set the LED pin to &lt;code&gt;High&lt;/code&gt; (lighting it), wait 100 ms, then set the pin to &lt;code&gt;Low&lt;/code&gt; (turning it off), and waiting another 100ms, forever. This blinks the LED. 💡&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before you deploy the program to your device, you might notice that there are two NuGet packages to be installed first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GHIElectronics.TinyCLR.Devices.Gpio&lt;/li&gt;
&lt;li&gt;GHIElectronics.TinyCLR.Pins&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make sure the device is still plugged into your PC's USB port. Click the &lt;strong&gt;Start&lt;/strong&gt; or &lt;strong&gt;F5&lt;/strong&gt; button in Visual Studio to compile and deploy the program. If everything works (should take 10-20 seconds) an LED on the board will blink until you unplug it or flash a new program:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8yswtb31fefdykp065lx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8yswtb31fefdykp065lx.gif" alt="tinyclr blink" width="216" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Congratulations!&lt;/strong&gt; You've literally just used C# code to control a microcontroller. This is huge! 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding IoT to the Mix
&lt;/h2&gt;

&lt;p&gt;Now that you're (relatively) comfortable with building a simple C# app for deployment on a tiny microcontroller, it's time to take the next step by adding the "I" to your IoT project.&lt;/p&gt;

&lt;p&gt;While some embedded systems can get away with storing data on an SD card for later retrieval, it's far more useful to actively transmit data as it comes in. Wi-Fi, LoRa, and Bluetooth are common technologies used in the IoT, but there is no more ubiquitous global solution for remotely transmitting data than cellular.&lt;/p&gt;

&lt;p&gt;It's also important to consider not only the raw technical ability of communicating over cellular, but also how you then get this data to a cloud endpoint, such as Microsoft Azure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing the Blues Wireless Notecard
&lt;/h3&gt;

&lt;p&gt;Cellular has traditionally scared developers due to its archaic AT command syntax (it's an awful developer experience) and likewise businesses have been afraid of the price (per-device monthly plans add up fast). So the key to cellular success lies in a tiny pre-paid device-to-cloud data pump called a &lt;a href="https://blues.io/products/notecard/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Notecard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg151zpkr5pqs387k2mg7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg151zpkr5pqs387k2mg7.png" alt="blues wireless cellular notecard" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With both cellular and GPS capabilities (and 10 years + 500 MB of data included) the Notecard is a no-brainer for when you want to start pushing data to the cloud over LTE-M, NB-IoT, or Cat-1 cellular.&lt;/p&gt;

&lt;p&gt;But what about that strange M.2 edge connector at the bottom of the Notecard? How do you use this with the microcontroller we just programmed? The answer comes in the form of the &lt;a href="https://blues.io/products/notecarrier/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Notecarrier&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3c6eg8tvv3fvvmw8vak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3c6eg8tvv3fvvmw8vak.png" alt="blues wireless notecarrier-al" width="600" height="548"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Notecarrier acts as a &lt;em&gt;bridge&lt;/em&gt; between your MCU and the Notecard. It includes an embedded antenna, header connectors, and battery ports to allow you to easily connect it to your prototype.&lt;/p&gt;

&lt;p&gt;Here is the FEZ Feather wired up to a Notecard + Notecarrier, ready to send data to the cloud over cellular!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qw41casvgort1fw5ni9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qw41casvgort1fw5ni9.jpg" alt="fez feather and notecard" width="800" height="745"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The white board you see is called a breadboard, and is used to quickly create connections between the MCU and the Notecarrier.&lt;/p&gt;

&lt;p&gt;If you're following along at home, here are the connections being made with those jumper wires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Supplying power to the Notecarrier from the FEZ via the &lt;code&gt;V+&lt;/code&gt; and &lt;code&gt;GND&lt;/code&gt; pins (the black/blue and orange/red wires).&lt;/li&gt;
&lt;li&gt;Communicating over I2C by connecting the &lt;code&gt;SCL&lt;/code&gt; and &lt;code&gt;SDA&lt;/code&gt; pins (the longer orange and green wires).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  All JSON, All the Time
&lt;/h3&gt;

&lt;p&gt;To me, the real beauty of the Notecard comes from its developer experience. Every command to (and every request from) the Notecard is JSON.&lt;/p&gt;

&lt;p&gt;For example, here is an API command that will tell you the current GPS location of a Notecard:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"req": "card.location"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And an example response from the Notecard:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "status": "GPS updated (58 sec, 41dB SNR, 9 sats) {gps-active}
            {gps-signal} {gps-sats} {gps}",
  "mode":   "periodic",
  "lat":    42.577600,
  "lon":    -70.871340,
  "time":   1598554399,
  "max":    25
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Adding Wireless Capabilities
&lt;/h3&gt;

&lt;p&gt;Knowing all of this, let's see how easy it can be to send a small packet of data, over cellular, to the cloud.&lt;/p&gt;

&lt;p&gt;Once again, you can remove everything in your &lt;code&gt;Program.cs&lt;/code&gt; file and replace it with the following:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Bytewizer.TinyCLR.Drivers.Blues.Notecard;
using GHIElectronics.TinyCLR.Devices.I2c;
using GHIElectronics.TinyCLR.Pins;
using System.Diagnostics;

namespace TinyCLRApplication
{
    class Program
    {
        static void Main()
        {
            // setup I2C bus for Fez Feather
            var controller = I2cController.FromName(SC20100.I2cBus.I2c1);
            var notecard = new NotecardController(controller);

            // associate this Notecard with a Notehub.io project
            var request1 = new JsonRequest("hub.set");
            request1.Add("product", "com.blues.me:some_project"); // replace this with your product uid!

            var results1 = notecard.Request(request1);
            if (results1.IsSuccess)
                Debug.WriteLine(results1.Response);

            // create a mock JSON body object
            var body = new JsonObject();
            body.Add("temp", 35.5);
            body.Add("humid", 56.23);

            // create a "note" with the JSON body from above
            var request2 = new JsonRequest("note.add");
            request2.Add("body", body);
            request2.Add("sync", true);

            var results2 = notecard.Request(request2);

            if (results2.IsSuccess)
                Debug.WriteLine(results2.Response);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you look carefully, you can see that to program the Notecard, we are simply building JSON objects that correlate to commands available in the &lt;a href="https://dev.blues.io/reference/notecard-api/introduction/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Notecard API&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://dev.blues.io/reference/notecard-api/hub-requests/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot#hub-set" rel="noopener noreferrer"&gt;hub.set&lt;/a&gt; request associates the Notecard with a project on Notehub.io (more on &lt;a href="https://blues.io/services/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Notehub&lt;/a&gt; in the next section!).&lt;/li&gt;
&lt;li&gt;We are creating a JSON object that stores some mock temperature and humidity data.&lt;/li&gt;
&lt;li&gt;We are sending this data (a &lt;a href="https://dev.blues.io/reference/glossary/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot#note" rel="noopener noreferrer"&gt;Note&lt;/a&gt; in Blues Wireless speak) to the cloud with a &lt;a href="https://dev.blues.io/reference/notecard-api/note-requests/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot#note-add" rel="noopener noreferrer"&gt;note.add&lt;/a&gt; request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before running this program, there is one more NuGet package to include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bytewizer.TinyCLR.Drivers.Blues.Notecard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Go ahead and click &lt;strong&gt;Start&lt;/strong&gt; or hit &lt;strong&gt;F5&lt;/strong&gt; in VS to build and deploy your cellular-connected IoT project!&lt;/p&gt;

&lt;p&gt;In the debug output, you should see &lt;code&gt;{"total":1}&lt;/code&gt; which is the Notecard telling you that there is a single event being sent to the cloud.&lt;/p&gt;

&lt;h3&gt;
  
  
  To the Cloud
&lt;/h3&gt;

&lt;p&gt;Yes, I got ahead of myself. We started pushing data to the cloud without fully understanding &lt;em&gt;where&lt;/em&gt; in the cloud the data was going!&lt;/p&gt;

&lt;p&gt;We first need to understand that a &lt;strong&gt;key benefit of the Notecard is security&lt;/strong&gt;. The device itself lives off of the public Internet. It's a cellular data pump, meaning it needs to connect to &lt;em&gt;secure proxy&lt;/em&gt; to deliver data over the Internet.&lt;/p&gt;

&lt;p&gt;This is where &lt;a href="https://blues.io/services/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Notehub&lt;/a&gt; comes into play. Notehub is a thin cloud service that securely receives and syncs data with virtually any cloud (think AWS, Azure, Google Cloud, or even your own custom MQTT or RESTful endpoint).&lt;/p&gt;

&lt;p&gt;To start, we can simply see this mock data appear in Notehub, ready to be routed to its final home on a big cloud ☁️:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6inym981ws4gmxoslug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6inym981ws4gmxoslug.png" alt="our mock data in notehub" width="800" height="849"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing Data to Azure
&lt;/h3&gt;

&lt;p&gt;Since Notehub is not the final resting place of our data, naturally we will want to automatically sync this data with a big cloud such as &lt;a href="https://azure.microsoft.com/" rel="noopener noreferrer"&gt;Azure&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbf47vxkhka29vr27z8wz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbf47vxkhka29vr27z8wz.png" alt="notecard data in microsoft azure" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do so, you can follow the &lt;a href="https://dev.blues.io/guides-and-tutorials/routing-data-to-cloud/azure/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;in-depth routing tutorial for Azure&lt;/a&gt; that walks through building your own cloud-based dashboard using data delivered with the Notecard.&lt;/p&gt;

&lt;p&gt;Finally, while it's outside the scope of this article, as data is being passed to Azure you can &lt;a href="https://dev.blues.io/notecard/notecard-guides/jsonata-1-2-3/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;alter and optimize your JSON payloads with JSONata&lt;/a&gt; on-the-fly. Using JSONata you can write server-side functions that can alter data to conform to whatever structures your endpoint is expecting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;If this has piqued your curiosity, I highly recommend taking the following steps in your C# IoT journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read more about what the &lt;a href="https://www.nanoframework.net/" rel="noopener noreferrer"&gt;.NET nanoFramework&lt;/a&gt; and &lt;a href="https://www.ghielectronics.com/tinyclr/" rel="noopener noreferrer"&gt;TinyCLR&lt;/a&gt; have to offer to .NET developers.&lt;/li&gt;
&lt;li&gt;If you want to follow this tutorial, grab your own &lt;a href="https://www.ghielectronics.com/sitcore/sbc/" rel="noopener noreferrer"&gt;FEZ Feather&lt;/a&gt;, &lt;a href="https://shop.blues.io/collections/notecard/products/note-nbgl-500?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Notecard&lt;/a&gt;, and &lt;a href="https://shop.blues.io/products/carr-al?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Notecarrier-AL&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you want to tinker with cloud connectivity with Python on a Raspberry Pi or by writing some Arduino, check out the &lt;a href="https://shop.blues.io/collections/development-kits?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_content=dotnetiot" rel="noopener noreferrer"&gt;Blues Wireless starter kits&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy hacking with C# and the IoT! 👩‍💻&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>iot</category>
      <category>azure</category>
    </item>
    <item>
      <title>Easiest Way to Add Cellular to the Raspberry Pi Zero 2</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Mon, 20 Dec 2021 15:08:47 +0000</pubDate>
      <link>https://dev.to/blues/easiest-way-to-add-cellular-to-the-raspberry-pi-zero-2-ld2</link>
      <guid>https://dev.to/blues/easiest-way-to-add-cellular-to-the-raspberry-pi-zero-2-ld2</guid>
      <description>&lt;p&gt;This holiday season I'm thankful for many things, not the least of which is the fact that I got my hands on the new &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-zero-2-w/" rel="noopener noreferrer"&gt;Raspberry Pi Zero 2 W&lt;/a&gt;! And I have to say, it's been an absolute dream to work with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faf39u4hyhqw8s9mz9c5f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faf39u4hyhqw8s9mz9c5f.jpg" alt="raspberry pi zero 2" width="600" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building on the foundation of the original Raspberry Pi Zero, the 2 W includes a significantly improved CPU (a quad core Cortex-A53 @ 1 GHz to be precise) and improved wireless performance over its Wi-Fi enabled predecessor.&lt;/p&gt;

&lt;p&gt;Now while Wi-Fi is a great connectivity method for the IoT, adding cellular access to the Zero can turn it into a truly "off-grid" Raspberry Pi. You may also want to enable cellular as a fallback connectivity method in case Wi-Fi goes down. Either way, read on for a quick tutorial on the easiest way to add cellular connectivity to the Raspberry Pi Zero (1 or 2!).&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Notecard
&lt;/h2&gt;

&lt;p&gt;The key to your success with adding cellular communication capabilities to the Raspberry Pi Zero is the &lt;a href="https://blues.io/products/notecard/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Notecard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34n5p6xui3vc6q1qo1dq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F34n5p6xui3vc6q1qo1dq.png" alt="blues wireless cellular notecard" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Notecard is a tiny 30mm x 35mm device-to-cloud data pump. With both cellular and GPS capabilities (and predictable pricing that starts at $49 for 10 years and 500 MB of data) the Notecard is a no-brainer for when you want to start pushing data to the cloud over LTE-M, NB-IoT, or Cat-1 cellular.&lt;/p&gt;

&lt;p&gt;But what about that M.2 edge connector at the bottom of the Notecard? How on earth do you use this with a Raspberry Pi single-board computer (SBC)? The answer comes in the form of a HAT (the Notecarrier-Pi HAT that is).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr30uoekx6iyslh1k2a79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr30uoekx6iyslh1k2a79.png" alt="blues wireless raspberry pi notecarrier-pi hat" width="600" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://blues.io/products/notecarrier/notecarrier-pi/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Notecarrier-Pi&lt;/a&gt; is a HAT (Hardware Attached on Top) that's compatible with every Raspberry Pi SBC. It includes pass-through headers for stacking multiple HATs, a Grove I2C port, and an external antenna.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing the Notecard and Notecarrier-Pi HAT
&lt;/h2&gt;

&lt;p&gt;I'm not here to insult your intelligence, nor do I want to waste your time. I state this because the physical installation steps are about as simple as you might expect when working in the Raspberry Pi ecosystem!&lt;/p&gt;

&lt;p&gt;1) Slot the Notecard into the provided M.2 slot and screw the Notecard onto the HAT:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuk7kcpk4pdwsd6oudzpp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuk7kcpk4pdwsd6oudzpp.gif" alt="placing notecard in notecarrier-pi hat" width="240" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2) Plug the Pi HAT into your Raspberry Pi Zero's 40-pin header:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxh4fcgwrsv2aakn07qj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxh4fcgwrsv2aakn07qj.jpg" alt="notecarrier pi hat on raspberry pi zero" width="600" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3) Plug the provided molex antenna into the &lt;code&gt;MAIN&lt;/code&gt; u.FL connector on the Notecard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2537nsslgoszrf91tlr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2537nsslgoszrf91tlr.jpg" alt="molex cellular antenna raspberry pi" width="600" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And yes, that's the end of the installation tutorial!&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the Raspberry Pi Zero Ready
&lt;/h2&gt;

&lt;p&gt;There are a few one-time configuration steps to make the development experience as seamless as possible.&lt;/p&gt;

&lt;p&gt;First, open up the "Raspberry Pi Configuration Tool" with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo raspi-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and enable the I2C interface if it isn't already enabled.&lt;/p&gt;

&lt;p&gt;Verify that the Pi HAT is correctly installed by using &lt;code&gt;i2c-tools&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install -y i2c-tools
sudo i2cdetect -y 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second command should show the Notecard on the correct I2C address (&lt;code&gt;0x17&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Install the &lt;code&gt;note-python&lt;/code&gt; and &lt;code&gt;python-periphery&lt;/code&gt; libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo pip3 install note-python python-periphery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you are good to go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Send Some Data to the Cloud Over Cellular
&lt;/h2&gt;

&lt;p&gt;Since the &lt;a href="https://dev.blues.io/reference/notecard-api/introduction/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Notecard API&lt;/a&gt; is 100% JSON-based (literally, everything is JSON in and JSON out), it doesn't matter what programming language you use. But since we are on a Raspberry Pi, we are going to stick with Python.&lt;/p&gt;

&lt;p&gt;Open up your preferred text editor (&lt;a href="https://thonny.org/" rel="noopener noreferrer"&gt;Thonny&lt;/a&gt; is a nice one for Python development on the Pi) and paste in this entire code block. We'll go through a few important bits afterwards:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import notecard
from notecard import hub, card, note
from periphery import I2C

productUID = "com.gmail.something:pizero"
port = I2C("/dev/i2c-1")
nCard = notecard.OpenI2C(port, 0, 0)

# associate Notecard with a project on Notehub.io
rsp = hub.set(nCard, product=productUID, mode="continuous")

# send a single "note" to the cloud over cellular!
rsp = note.add(nCard, file="pi.qo", body={"Hello":"World!"})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Now what's all going on here?&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are opening a communication port on the I2C bus (&lt;code&gt;/dev/i2c-1&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;We are using the &lt;a href="https://dev.blues.io/reference/notecard-api/hub-requests/?utm_source=devto&amp;amp;utm_medium=web#hub-set" rel="noopener noreferrer"&gt;hub.set&lt;/a&gt; API to associate the Notecard with a Notehub project (more on Notehub below).&lt;/li&gt;
&lt;li&gt;We are sending the JSON "Hello World!" object to the cloud, over cellular.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Viewing Your Cellular Data in the Cloud
&lt;/h2&gt;

&lt;p&gt;Yes, we did put the cart before the horse by going through the Python code &lt;em&gt;prior&lt;/em&gt; to setting up the cloud service that receives data. So now is a good time to run through what &lt;a href="https://blues.io/services/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Notehub&lt;/a&gt; is all about.&lt;/p&gt;

&lt;p&gt;Notehub is a cloud service from Blues Wireless that provides the ability to securely receive, process, and &lt;strong&gt;route data to your cloud endpoint of choice&lt;/strong&gt;. This could be AWS, Azure, Google Cloud, or any number of IoT platforms like Datacake or Ubidots (or even your own custom RESTful API).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0cbrwk42x6egzpixpx3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq0cbrwk42x6egzpixpx3.jpg" alt="blues wireless notehub and notecard data flow" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notehub also provides OTA firmware update features, fleet management, and &lt;a href="https://dev.blues.io/notecard/notecard-guides/understanding-environment-variables/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;cloud-based environment variables&lt;/a&gt; for easily sharing data across fleets of Notecards.&lt;/p&gt;

&lt;p&gt;After creating a free account at &lt;a href="https://notehub.io/" rel="noopener noreferrer"&gt;Notehub.io&lt;/a&gt; and setting up your first project, you'll want to copy the provided &lt;a href="https://dev.blues.io/reference/glossary/?utm_source=devto&amp;amp;utm_medium=web#productuid" rel="noopener noreferrer"&gt;ProductUID&lt;/a&gt; and paste that into the &lt;code&gt;product&lt;/code&gt; parameter in the &lt;code&gt;hub.set&lt;/code&gt; call in the Python script above.&lt;/p&gt;

&lt;p&gt;Upon running the Python script, the &lt;code&gt;note.add&lt;/code&gt; function will be executed. You can then log into the Notehub project and view related events in the &lt;strong&gt;Event&lt;/strong&gt; panel:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6dv7e60qwbwptzswavn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6dv7e60qwbwptzswavn.png" alt="raspberry pi data in notehub" width="800" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here you can &lt;a href="https://dev.blues.io/start/tutorials/route-tutorial/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;set up a Notehub route&lt;/a&gt; to start relaying data to any cloud endpoint.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP:&lt;/strong&gt; The Notecard can also &lt;em&gt;receive&lt;/em&gt; data as documented in the &lt;a href="https://dev.blues.io/notecard/notecard-walkthrough/inbound-requests-and-shared-data/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Inbound Requests &amp;amp; Shared Data Guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Next Steps with the Pi
&lt;/h2&gt;

&lt;p&gt;Clearly we've only just scratched the surface with the Notecard and Raspberry Pi Zero. Hopefully you've witnessed how ridiculously easy it is to add cellular connectivity and send data to the cloud in a matter of minutes.&lt;/p&gt;

&lt;p&gt;If you're looking for some good next steps, I can recommend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grabbing your own &lt;a href="https://shop.blues.io/collections/development-kits/products/raspberry-pi-starter-kit?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Raspberry Pi Starter Kit&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Following the complete &lt;a href="https://dev.blues.io/start/quickstart/notecarrier-pi/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Notecard Quickstart Tutorial&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Learn how to &lt;a href="https://dev.blues.io/start/tutorials/route-tutorial/?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;route data to your cloud of choice&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy hacking on the Pi Zero! 👩‍💻🥧0️⃣&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>cellular</category>
      <category>iot</category>
    </item>
    <item>
      <title>The Idiot-Proof Guide to Upgrading Raspberry Pi from Buster to Bullseye</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Tue, 07 Dec 2021 17:20:54 +0000</pubDate>
      <link>https://dev.to/blues/the-idiot-proof-guide-to-upgrading-raspberry-pi-from-buster-to-bullseye-42c3</link>
      <guid>https://dev.to/blues/the-idiot-proof-guide-to-upgrading-raspberry-pi-from-buster-to-bullseye-42c3</guid>
      <description>&lt;p&gt;&lt;em&gt;Title photo by [Silvan Arnet (&lt;a href="https://unsplash.com/@silvanarnet" rel="noopener noreferrer"&gt;https://unsplash.com/@silvanarnet&lt;/a&gt;) on &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A couple of weeks ago I enjoyed a very traditional Thanksgiving at home: I spent the day avoiding my extended family and proceeded to upgrade my home computers to their latest OS versions!&lt;/p&gt;

&lt;p&gt;While migrating to &lt;a href="https://www.apple.com/macos/monterey/" rel="noopener noreferrer"&gt;macOS 12 Monterey&lt;/a&gt; and &lt;a href="https://www.microsoft.com/en-us/windows" rel="noopener noreferrer"&gt;Windows 11&lt;/a&gt; were relatively painless point-and-click procedures, upgrading my Raspberry Pi 4 model B single-board computer from the Buster release to Bullseye involved a few extra steps. I'll admit the difficulty of this process was exacerbated by the fact that I wanted to perform an &lt;em&gt;in-place upgrade&lt;/em&gt; to maintain all previously installed packages and stored files.&lt;/p&gt;

&lt;h2&gt;
  
  
  A New Raspberry Pi OS?
&lt;/h2&gt;

&lt;p&gt;You heard that right, a new version of Raspberry Pi OS (codenamed Bullseye 🎯) was released a few weeks ago. It's based upon &lt;a href="https://www.debian.org/News/2021/20210814" rel="noopener noreferrer"&gt;Debian 11&lt;/a&gt; and on the right hardware can provide a significant speed boost with certain tasks. There are numerous updates and enhancements to be found in Bullseye, not the least of which include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An updated PiCamera driver (which will require re-writing your code BTW);&lt;/li&gt;
&lt;li&gt;Replacing GTK+2 with GTK+3 to provide an improved GUI;&lt;/li&gt;
&lt;li&gt;Using Mutter instead of Openbox for window management (if your Pi has &amp;gt;= 2GB of RAM);&lt;/li&gt;
&lt;li&gt;A fresh new notification manager in the taskbar;&lt;/li&gt;
&lt;li&gt;A better updating experience for installed packages;&lt;/li&gt;
&lt;li&gt;An update to the Chromium browser (v92), which supports hardware-accelerated video playback.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a complete list of Raspberry Pi Bullseye updates, &lt;a href="https://www.raspberrypi.com/news/raspberry-pi-os-debian-bullseye/" rel="noopener noreferrer"&gt;consult this article from the folks at Raspberry Pi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This new release of Raspberry Pi OS does technically work with any Raspberry Pi SBC (yes, including the Raspberry Pi Zero). However, unless you &lt;em&gt;need&lt;/em&gt; features from Bullseye specifically, you might want to stay on your current release. Owners of a "4.x" Raspberry Pi (e.g. the &lt;br&gt;
&lt;a href="https://www.raspberrypi.com/products/raspberry-pi-4-model-b/" rel="noopener noreferrer"&gt;Raspberry Pi 4&lt;/a&gt;, &lt;a href="https://www.raspberrypi.com/products/compute-module-4/?variant=raspberry-pi-cm4001000" rel="noopener noreferrer"&gt;Compute Module 4&lt;/a&gt;, or the &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-400/" rel="noopener noreferrer"&gt;Raspberry Pi 400&lt;/a&gt;) &lt;br&gt;
will likely choose to upgrade regardless.&lt;/p&gt;
&lt;h2&gt;
  
  
  Upgrading Caveats
&lt;/h2&gt;

&lt;p&gt;Before you begin, I can't stress enough that the best (and frankly, supported) way to upgrade to Bullseye is to backup all of your critical files and perform a clean install using the &lt;a href="https://www.raspberrypi.com/software/" rel="noopener noreferrer"&gt;Raspberry Pi Imager&lt;/a&gt;. However, if you're like me and choose to live dangerously, be sure to backup your files anyway and buckle up for the ride.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please be aware that the following process is not officially supported AND may temporarily disable Wi-Fi (see the note below) or lead to other unintended issues. Backup any critical files on your RPi before proceeding!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Step-by-Step Installation of Bullseye
&lt;/h2&gt;

&lt;p&gt;Disclaimers aside (did you read the warning above?), here is a step-by-step set of steps for you to upgrade from Buster to Bullseye via the Raspberry Pi terminal.&lt;/p&gt;

&lt;p&gt;1) Open your Raspberry Pi terminal and update the repository lists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2) Install all of the latest packages (and their dependencies):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt full-upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3) Reboot your Pi to activate any packages that require a reboot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4) &lt;strong&gt;Optional, if you like to live on the edge!&lt;/strong&gt; Update to the latest version of the &lt;a href="https://www.raspberrypi.com/documentation/computers/os.html#using-rpi-update" rel="noopener noreferrer"&gt;Raspberry Pi firmware&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo rpi-update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another reboot may be necessary after updating your firmware.&lt;/p&gt;

&lt;p&gt;5) Next, edit your &lt;code&gt;sources.list&lt;/code&gt; file to switch your repository list from Buster to Bullseye:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nano /etc/apt/sources.list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Locate the following line and change &lt;code&gt;buster&lt;/code&gt; to &lt;code&gt;bullseye&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deb http://raspbian.raspberrypi.org/raspbian/ buster main contrib non-free rpi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save your changes and proceed to the next step.&lt;/p&gt;

&lt;p&gt;6) Update your repository lists again (this time it will be using the Bullseye-specific repositories):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;7) Install the latest version of Node.js (this helps avoid an error you may receive when running &lt;code&gt;apt full-upgrade&lt;/code&gt; in step 9). This step may take several minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note that you may receive a prompt to "Restart services during package upgrades without asking". If so, choose "Yes".&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;8) Update to the latest version of GCC 8 (this &lt;em&gt;also&lt;/em&gt; helps avoid an error in the next step).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install gcc-8-base
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;9) Run another full upgrade to install any additional Bullseye requirements and downstream dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt full-upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; It's at this point your Wi-Fi may go out! We'll fix that in a bit.&lt;/p&gt;

&lt;p&gt;10) Clean up your packages to remove any that are obsolete or no longer used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt autoclean
$ sudo apt autoremove
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;11) Next, you need to alter your &lt;code&gt;/boot/config.txt&lt;/code&gt; file to enable KMS (the new standard video driver). This is done by default with a clean install, but our &lt;br&gt;
upgrade process requires this change to be performed manually. Thanks to &lt;a href="https://www.linuxuprising.com/2021/11/how-to-upgrade-raspberry-pi-os-10.html" rel="noopener noreferrer"&gt;Linux Uprising&lt;/a&gt; for this tip!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo sed -i 's/dtoverlay=vc4-fkms-v3d/#dtoverlay=vc4-fkms-v3d/g' /boot/config.txt
$ sudo sed -i 's/\[all\]/\[all\]\ndtoverlay=vc4-kms-v3d/' /boot/config.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;12) When you're all done, reboot your Pi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;13) After a reboot, check to see if everything was updated properly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat /etc/os-release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should return the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRETTY_NAME="Raspbian GNU/Linux 11 (bullseye)"
NAME="Raspbian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wi-Fi Not Working?
&lt;/h2&gt;

&lt;p&gt;To reclaim your Wi-Fi, go to "Preferences" --&amp;gt; "Connman Settings" --&amp;gt; "Wireless" and then click on the SSID to which you want to connect. You may also want to click the ⚙️ icon next to the SSID and enable "autoconnect".&lt;/p&gt;

&lt;p&gt;If any wireless or network panels are missing from the taskbar, you can re-add those by right-clicking on the taskbar, choosing "Add/Remove Panel Items", then adding the appropriate panels.&lt;/p&gt;

&lt;p&gt;If those suggestions didn't work, consult some forum threads &lt;br&gt;
&lt;a href="https://forums.tomshardware.com/threads/how-to-upgrade-raspberry-pi-os-to-bullseye-from-buster.3733298/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://forums.raspberrypi.com/viewtopic.php?f=66&amp;amp;t=317986" rel="noopener noreferrer"&gt;here&lt;/a&gt; that may help.&lt;/p&gt;

&lt;h2&gt;
  
  
  Great Success...?
&lt;/h2&gt;

&lt;p&gt;It should be stated again here (at the end, when you've already made the mistake of performing an in-place upgrade 😅) that this process is not officially supported by Raspberry Pi. Rather the supported upgrade path is a clean installation of Bullseye using the &lt;a href="https://www.raspberrypi.com/software/" rel="noopener noreferrer"&gt;Raspberry Pi Imager&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While you're here, if you're curious about &lt;strong&gt;adding wireless cellular capabilities to your Raspberry Pi&lt;/strong&gt;, check out the &lt;a href="https://blues.io/products/notecard?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Notecard&lt;/a&gt; and get 10 years and 500 MB of cellular data using the &lt;a href="https://shop.blues.io/products/raspberry-pi-starter-kit?utm_source=devto&amp;amp;utm_medium=web" rel="noopener noreferrer"&gt;Raspberry Pi starter kit&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Happy Hacking on the Pi! 👩‍💻&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>linux</category>
    </item>
    <item>
      <title>Debugging a Hot Tub Time (Series) Machine</title>
      <dc:creator>Rob Lauer</dc:creator>
      <pubDate>Thu, 11 Nov 2021 15:21:38 +0000</pubDate>
      <link>https://dev.to/blues/debugging-a-hot-tub-time-series-machine-4e6a</link>
      <guid>https://dev.to/blues/debugging-a-hot-tub-time-series-machine-4e6a</guid>
      <description>&lt;p&gt;I don't mean to sound like a prude or anything, but when a neighbor invites me over for a hot tub party, I'm gonna hope the hot tub itself looks a little more like the one on the left, than the one on the right:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt9qmmxzd7clyybafmbi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt9qmmxzd7clyybafmbi.jpg" alt="hot tub comparison" width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might not know it, but even a clean-looking hot tub or pool can still harbor nasty bacteria! So how can we make hot tubs and pools around the world safer, and frankly, more fun? &lt;a href="https://www.hackster.io/rob-lauer/debugging-a-hot-tub-time-series-machine-92e44f" rel="noopener noreferrer"&gt;In this Hackster project&lt;/a&gt;, I walk through how I built an IoT project with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A variety of &lt;strong&gt;water quality sensors&lt;/strong&gt; from DFRobot;&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;new Feather-compatible microcontroller&lt;/strong&gt; (spoiler: &lt;a href="https://blues.io/products/swan/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_campaign=featured-project&amp;amp;utm_content=hottub" rel="noopener noreferrer"&gt;Swan&lt;/a&gt;);&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;cellular&lt;/strong&gt; device-to-cloud data pump (another spoiler: &lt;a href="https://blues.io/products/notecard/?utm_source=devto&amp;amp;utm_medium=web&amp;amp;utm_campaign=featured-project&amp;amp;utm_content=hottub" rel="noopener noreferrer"&gt;Notecard&lt;/a&gt;);&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;cloud-based dashboard&lt;/strong&gt; from &lt;a href="https://www.losant.com/" rel="noopener noreferrer"&gt;Losant&lt;/a&gt; populated by time series data;&lt;/li&gt;
&lt;li&gt;PLUS an &lt;strong&gt;SMS notification&lt;/strong&gt; from &lt;a href="https://www.twilio.com/sms" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; for chlorine emergencies!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Interested in a preview of this project? Check out the project trailer on YouTube:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/pYxxY7sP5MI"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;As an owner (or even user) of a hot tub, &lt;strong&gt;it's a pain to keep your water clean&lt;/strong&gt;. You have to maintain the proper chlorine and pH levels by regularly sampling the water, running tests, waiting for the results, adding chlorine, sampling again, and repeating until your levels stabilize appropriately.&lt;/p&gt;

&lt;p&gt;This whole process of sampling the water, running tests, waiting for the results, adding chlorine, sampling again, and repeating until your levels stabilize appropriately is dying for automation. The beauty of the IoT too, is that we can take a semi-traditional sensor project and build it out into a robust cloud-based dashboard with proactive SMS text messages for alerting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav3nleoyj7m9ncolq7i2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fav3nleoyj7m9ncolq7i2.png" alt="hot tub dashboard and sms" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check out the &lt;a href="https://www.hackster.io/rob-lauer/debugging-a-hot-tub-time-series-machine-92e44f" rel="noopener noreferrer"&gt;completed project on Hackster&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>iot</category>
      <category>cellular</category>
      <category>sms</category>
    </item>
  </channel>
</rss>
