<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ embedded systems - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ embedded systems - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 16 May 2026 22:22:31 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/embedded-systems/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ ITCM vs DTCM vs DDR: Embedded Memory Types Explained [Full Handbook] ]]>
                </title>
                <description>
                    <![CDATA[ Most embedded engineers hit this problem early on: the same code on the same processor runs fast in one scenario and surprisingly slow in another. The culprit is almost always where the code and data  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/itcm-vs-dtcm-vs-ddr-embedded-memory-types-explained-handbook/</link>
                <guid isPermaLink="false">69fb8bbc50ecad4533638e41</guid>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ memory-management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nikheel Vishwas Savant ]]>
                </dc:creator>
                <pubDate>Wed, 06 May 2026 18:43:08 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/66013473-45d1-4f6f-87f4-727bf75e0c5e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Most embedded engineers hit this problem early on: the same code on the same processor runs fast in one scenario and surprisingly slow in another. The culprit is almost always <em>where</em> the code and data are stored in memory.</p>
<p>Desktop and server processors hide memory latency behind multi-level caches. Many embedded processors, especially ARM Cortex-M and Cortex-R based chips, take a different approach. They give you direct control over multiple memory regions, each with very different performance characteristics.</p>
<p>This handbook covers what ITCM, DTCM, and DDR memory are, how they differ, how to place code and data in the right region, and how to profile and monitor firmware memory usage over time.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-why-embedded-memory-architecture-matters">Why Embedded Memory Architecture Matters</a></p>
</li>
<li><p><a href="#heading-what-is-itcm-instruction-tightly-coupled-memory">What is ITCM (Instruction Tightly-Coupled Memory)?</a></p>
</li>
<li><p><a href="#heading-what-is-dtcm-data-tightly-coupled-memory">What is DTCM (Data Tightly-Coupled Memory)?</a></p>
</li>
<li><p><a href="#heading-what-is-ddr-double-data-rate-memory">What is DDR (Double Data Rate) Memory?</a></p>
</li>
<li><p><a href="#heading-how-they-compare-a-side-by-side-overview">How They Compare: A Side-by-Side Overview</a></p>
</li>
<li><p><a href="#heading-how-to-decide-where-to-place-code-and-data">How to Decide Where to Place Code and Data</a></p>
</li>
<li><p><a href="#heading-how-the-linker-script-controls-memory-placement">How the Linker Script Controls Memory Placement</a></p>
</li>
<li><p><a href="#heading-common-mistakes-to-avoid">Common Mistakes to Avoid</a></p>
</li>
<li><p><a href="#heading-performance-comparison-with-real-numbers">Performance Comparison With Real Numbers</a></p>
</li>
<li><p><a href="#heading-how-tcm-affects-power-consumption">How TCM Affects Power Consumption</a></p>
</li>
<li><p><a href="#heading-how-to-profile-memory-usage">How to Profile Memory Usage</a></p>
</li>
<li><p><a href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To get the most from this guide, you should have a basic understanding of C programming, including pointers, structs, and the difference between static and local variables.</p>
<p>Some familiarity with embedded development concepts like compiling, linking, and flashing firmware to a target board will also help.</p>
<p>Finally, a general sense of how a CPU fetches and executes instructions will make the performance discussions easier to follow.</p>
<p>You don't need to be an expert in any of these. The article explains each concept as it comes up.</p>
<h2 id="heading-why-embedded-memory-architecture-matters">Why Embedded Memory Architecture Matters</h2>
<p>A modern embedded processor might be clocked at 400 MHz or higher. It can execute an instruction every few nanoseconds.</p>
<p>But when it needs to fetch that instruction from memory, or read a variable, the memory might not keep up. The processor ends up stalling, waiting for the memory subsystem to deliver the data it asked for. Those stall cycles add up fast.</p>
<p>On a desktop computer, hardware caches (L1, L2, L3) sit between the CPU and main memory, automatically keeping recently-used data nearby. The cache hardware decides what to keep and what to evict, and it does this transparently. The programmer rarely needs to think about it, and performance is generally good enough without manual intervention.</p>
<p>On many embedded processors, the situation is different. Instead of hardware caches, you get <strong>three distinct memory regions</strong>, each attached to the CPU in a different way.</p>
<table>
<thead>
<tr>
<th>Memory Type</th>
<th>What It Stores</th>
<th>Access Speed</th>
<th>Typical Size</th>
</tr>
</thead>
<tbody><tr>
<td><strong>ITCM</strong></td>
<td>Instructions (executable code)</td>
<td>Single-cycle (deterministic)</td>
<td>512 KB to 2 MB</td>
</tr>
<tr>
<td><strong>DTCM</strong></td>
<td>Data (variables, stacks, buffers)</td>
<td>Single-cycle (deterministic)</td>
<td>512 KB to 1.5 MB</td>
</tr>
<tr>
<td><strong>DDR</strong></td>
<td>Everything else</td>
<td>Multi-cycle (variable)</td>
<td>4 MB to several GB</td>
</tr>
</tbody></table>
<p>The table above shows the three memory types you'll encounter on a typical ARM Cortex-M or Cortex-R-based embedded system. ITCM and DTCM are fast but small. DDR is slow but large.</p>
<p>The "deterministic" label on TCM means that the access time is always the same, every single time, regardless of what accessed that memory before or what else is happening on the chip. The "variable" label on DDR means the access time can change depending on the internal state of the DDR chip and its controller.</p>
<p>You, the developer, control which region each piece of your firmware lives in. The compiler and linker don't make these decisions automatically. You specify them through section attributes in your source code and placement rules in your linker script. Getting this right is often the difference between firmware that meets its real-time deadlines and firmware that misses them.</p>
<h2 id="heading-what-is-itcm-instruction-tightly-coupled-memory">What is ITCM (Instruction Tightly-Coupled Memory)?</h2>
<p>ITCM stands for <strong>Instruction Tightly-Coupled Memory</strong>.</p>
<p>The "Instruction" part means this memory is used for storing executable machine code, the compiled instructions your CPU fetches and runs.</p>
<p>The "Tightly-Coupled" part means the memory is physically located on the same silicon die as the CPU core, connected through a dedicated bus with no arbitration or contention. There's no shared bus to compete with. There's no cache hierarchy to traverse. The CPU asks for an instruction, and ITCM delivers it directly, through a private path that nothing else on the chip can interfere with.</p>
<p>The CPU can fetch an instruction from ITCM in a <strong>single clock cycle, every time</strong>. This access time is both fast and deterministic. It doesn't vary based on access patterns, recent history, or what else is happening on the bus.</p>
<p>This determinism is just as important as the raw speed, because it makes worst-case execution time analysis possible. In safety-critical systems, you need to be able to <em>prove</em> that a function will always complete within a certain number of cycles. ITCM makes that proof much simpler.</p>
<h3 id="heading-why-single-cycle-fetch-matters">Why Single-Cycle Fetch Matters</h3>
<p>Every line of C code compiles down to one or more machine instructions. Each of those instructions must be fetched from memory before the CPU can decode and execute it. This fetch step happens for every single instruction, so even small per-instruction delays compound rapidly in loops and frequently-called functions.</p>
<p>Consider a loop that runs 1,000,000 iterations, where each iteration involves 10 instruction fetches. That's 10 million fetches total.</p>
<pre><code class="language-shell">ITCM:  10,000,000 fetches x 1 cycle  = 10,000,000 cycles
DDR:   10,000,000 fetches x 8 cycles = 80,000,000 cycles

Difference: 70,000,000 cycles
At 400 MHz: 70,000,000 / 400,000,000 = 0.175 seconds = 175 ms
</code></pre>
<p>This calculation compares the total cycle count when the same loop runs from ITCM versus DDR. With ITCM, each fetch takes 1 cycle, so 10 million fetches cost 10 million cycles.</p>
<p>With DDR, each fetch takes 8 cycles (a conservative average), so the same 10 million fetches cost 80 million cycles. The difference is 70 million cycles, which at 400 MHz translates to 175 milliseconds.</p>
<p>In a real-time system running a control loop at 1 kHz (one iteration every 1 ms), 175 ms of extra latency spread across your processing isn't a minor inconvenience. It can cause the system to miss deadlines, drop sensor readings, or produce incorrect outputs. In motor control applications, a missed deadline can mean physical damage to the hardware. In audio processing, it means audible glitches. The cost of slow instruction fetch isn't abstract.</p>
<h3 id="heading-what-should-go-in-itcm">What Should Go in ITCM?</h3>
<p>Because ITCM is small (typically 512 KB to 2 MB), you can't fit your entire firmware in it. You need to be selective about what earns a spot.</p>
<p><strong>Interrupt Service Routines (ISRs)</strong> are the highest-priority candidates. ISRs run in response to hardware events like a timer tick, an ADC conversion completing, or a communication peripheral receiving data. They need to execute and return as quickly as possible.</p>
<p>A slow ISR delays all lower-priority interrupts and can cause missed events. If your ISR fetches its instructions from DDR, each fetch takes multiple cycles, and the total ISR execution time increases by a factor that could push it past its deadline.</p>
<p>Placing ISRs in ITCM ensures they run at maximum speed with completely predictable timing.</p>
<p><strong>Real-time processing functions</strong> are the next priority. These include signal processing routines, motor control loops, audio processing pipelines, and any function that runs at a fixed rate and must complete within a strict time budget.</p>
<p>If your audio codec callback needs to process a buffer of samples every 5 ms, every instruction fetch cycle counts. Placing these functions in ITCM gives you the maximum amount of CPU time for actual computation rather than waiting on memory.</p>
<p><strong>Inner loops of your main processing pipeline</strong> also benefit significantly from ITCM placement. If your firmware spends 80% of its time in a handful of functions, those functions should be in ITCM. Profiling tools and the linker map file (covered later in this article) can help you identify which functions are the hottest.</p>
<p><strong>Functions that require deterministic timing</strong> belong in ITCM even if they aren't the fastest path. ITCM access time doesn't vary, which makes timing analysis predictable. This matters for safety-critical systems (automotive, medical, aerospace) where you need to prove worst-case execution times to a certification authority.</p>
<h3 id="heading-how-to-place-a-function-in-itcm">How to Place a Function in ITCM</h3>
<p>You use a GCC section attribute to tell the compiler that a function belongs in a specific memory section. Then, in your linker script, you map that section to the ITCM memory region.</p>
<pre><code class="language-c">__attribute__((section(".itcm_text")))
void my_critical_isr(void) {
    volatile uint32_t *sensor_reg = (volatile uint32_t *)0x40001000;
    uint32_t reading = *sensor_reg;
    process_sample(reading);
}
</code></pre>
<p>In this code, the <code>__attribute__((section(".itcm_text")))</code> directive tells the compiler to emit this function's compiled machine code into a section called <code>.itcm_text</code> instead of the default <code>.text</code> section. The function itself reads a sensor register at the memory-mapped address <code>0x40001000</code>, stores the result in a local variable, and passes it to <code>process_sample()</code> for further processing. The <code>volatile</code> keyword tells the compiler that this memory address can change at any time (because it is a hardware register), so the compiler must not optimize away the read.</p>
<p>On its own, the section attribute doesn't determine where the function ends up in physical memory. It just tells the compiler to label the function's code with a specific section name.</p>
<p>The actual memory placement is the linker script's job, which maps <code>.itcm_text</code> to the ITCM address range. We'll cover the linker script in detail in a later section.</p>
<h3 id="heading-how-much-itcm-is-typical">How Much ITCM is Typical?</h3>
<p>A real-world memory profile from an embedded project, to give you a sense of scale:</p>
<pre><code class="language-shell">Memory region         Used Size  Region Size  %age Used
            ITCM:      570936 B         2 MB     27.22%
            DTCM:      727240 B    1572608 B     46.24%
             DDR:      622915 B         4 MB     14.85%
</code></pre>
<p>This output comes from the linker map file's summary section. It shows three memory regions and how much of each one is used by the compiled firmware.</p>
<p>ITCM has 2 MB available and the firmware is using about 557 KB (27.22%). DTCM has about 1.5 MB available and is using 727 KB (46.24%). DDR has 4 MB available and is using about 609 KB (14.85%).</p>
<p>This project uses about 557 KB of the available 2 MB of ITCM, roughly 27%. That leaves good headroom for growth.</p>
<p>In practice, you want to keep ITCM utilization below 80-85% to leave room for future features and library updates. If utilization climbs above 90%, you're one feature addition away from a build failure, and you should proactively move less-critical code to DDR.</p>
<h2 id="heading-what-is-dtcm-data-tightly-coupled-memory">What is DTCM (Data Tightly-Coupled Memory)?</h2>
<p>DTCM stands for <strong>Data Tightly-Coupled Memory</strong>. It works on the same principle as ITCM (physically close to the CPU core, connected via a dedicated bus, single-cycle access) but it stores <strong>data</strong> instead of instructions.</p>
<p>If ITCM is where your code lives, DTCM is where your code <em>works</em>. It's the fast scratch space that the CPU reads from and writes to while executing your performance-critical functions. Every variable read, every array access, every stack push and pop in your hot code paths goes through data memory. Making that data memory as fast as possible eliminates one of the biggest sources of stall cycles.</p>
<h3 id="heading-what-kind-of-data-belongs-in-dtcm">What Kind of Data Belongs in DTCM?</h3>
<p><strong>Stack frames</strong> are the most important thing in DTCM. Every function call pushes a stack frame containing local variables, the return address, and saved registers. Every function return pops that frame. I</p>
<p>f your stack is in DTCM, the memory-access portion of function calls and returns happens in a single cycle. If your stack were in DDR, every function call and return would incur multiple cycles of memory latency just for the stack operations alone, before the function even begins doing useful work.</p>
<p>On most Cortex-M and Cortex-R configurations, the startup code initializes the stack pointer to point into DTCM by default, so you get this benefit without any extra configuration.</p>
<p><strong>Frequently accessed global variables</strong> are another strong candidate. State machine variables, control flags, sensor readings that are updated and read in every loop iteration, counters that are incremented in ISRs and read in the main loop: all of these benefit from single-cycle access.</p>
<p>If a variable is read or written thousands of times per second, the cumulative latency difference between DTCM and DDR adds up.</p>
<p><strong>Small lookup tables used in hot paths</strong> belong in DTCM when they're small enough to fit. Sine/cosine tables for motor control, filter coefficients for audio processing, and CRC tables for communication protocols are common examples.</p>
<p>These tables are typically a few hundred bytes to a few kilobytes, and they get accessed on every iteration of a processing loop. The key word is "small." A 512-byte sine table is a good fit for DTCM. A 64 KB calibration table is not, and should go in DDR instead.</p>
<p><strong>DMA buffers</strong> can sometimes go in DTCM, but this depends on your chip's bus architecture. On some chips, the DMA controller has a direct path to DTCM through the bus matrix. On others, the DMA controller can only reach DDR and possibly other SRAM regions. If you place a DMA buffer in DTCM on a chip where the DMA controller can't reach it, the transfer will silently fail or write to a completely wrong address.</p>
<p>Always check your chip's bus matrix diagram in the reference manual before putting DMA buffers in DTCM.</p>
<h3 id="heading-how-to-place-data-in-dtcm">How to Place Data in DTCM</h3>
<p>Placing data in DTCM uses the same section attribute mechanism as ITCM, but with a section name that your linker script maps to the DTCM address range.</p>
<pre><code class="language-c">__attribute__((section(".dtcm_data")))
static int16_t audio_buffer[256];

__attribute__((section(".dtcm_data")))
static volatile uint32_t sensor_state = 0;
</code></pre>
<p>In this code, <code>audio_buffer</code> is an array of 256 signed 16-bit integers (512 bytes total) that will be placed in DTCM. This could be a buffer for audio samples that gets filled by a DMA transfer and processed by an ISR. The <code>static</code> keyword means the buffer has file scope and persists for the lifetime of the program (it's not allocated on the stack).</p>
<p>The <code>sensor_state</code> variable is a 32-bit unsigned integer marked as <code>volatile</code>, meaning the compiler must read it from memory every time it's accessed rather than caching it in a register.</p>
<p>This is important for variables that are written in an ISR and read in the main loop, since the compiler needs to know the value can change at any time. Placing it in DTCM ensures that both the ISR write and the main loop read happen in a single cycle.</p>
<h3 id="heading-dtcm-fills-up-faster-than-itcm">DTCM Fills Up Faster Than ITCM</h3>
<p>Looking at the memory profile again:</p>
<pre><code class="language-shell">            DTCM:      727240 B    1572608 B     46.24%
</code></pre>
<p>This single line from the linker map file summary shows that DTCM has 1,572,608 bytes (about 1.5 MB) available, and the firmware is using 727,240 bytes (about 710 KB), which is 46.24% of the total capacity.</p>
<p>DTCM fills up faster than ITCM because many things compete for it: your stack, your heap (if you have one), your global variables, and data sections from every library you link against. Every C library function that uses static data, every RTOS data structure, every middleware component brings its own data footprint. This creates a constant sizing exercise.</p>
<p>For every data structure, you need to ask: does this really need single-cycle access, or can it work from DDR?</p>
<h3 id="heading-a-concrete-example-of-the-performance-impact">A Concrete Example of the Performance Impact</h3>
<p>Say your processor runs at 400 MHz. DTCM gives you 1-cycle access. DDR gives you 8-cycle access. You have a lookup table that gets accessed 100,000 times per second.</p>
<pre><code class="language-shell">DTCM: 100,000 accesses x 1 cycle  = 100,000 cycles/sec
DDR:  100,000 accesses x 8 cycles = 800,000 cycles/sec

Difference: 700,000 cycles/sec
At 400 MHz: 700,000 / 400,000,000 = 0.00175 seconds = 1.75 ms
</code></pre>
<p>This calculation shows the cycle cost of 100,000 memory accesses per second in both memory types. In DTCM, each access is 1 cycle, totaling 100,000 cycles. In DDR, each access is 8 cycles, totaling 800,000 cycles. The difference of 700,000 cycles per second, at a 400 MHz clock rate, translates to 1.75 milliseconds of additional CPU time spent waiting on memory.</p>
<p>If you're running a real-time control loop at 1 kHz (1 ms period), 1.75 ms of additional memory latency per second means that some individual iterations are running longer than their 1 ms budget. Whether this causes actual deadline misses depends on how the accesses are distributed across iterations and how much slack you have in your time budget, but it shows why memory placement decisions have real consequences in embedded systems.</p>
<h2 id="heading-what-is-ddr-double-data-rate-memory">What is DDR (Double Data Rate) Memory?</h2>
<p>DDR is external memory. It sits on the circuit board outside the processor die, connected through a memory controller. It's much larger than TCM (typically 4 MB to several GB), but significantly slower to access.</p>
<p>The name "Double Data Rate" refers to how data is transferred between the DDR chip and the memory controller: data is sent on both the rising edge and the falling edge of the clock signal, effectively doubling the transfer rate compared to a single-data-rate design. But this doesn't eliminate the latency of activating rows and columns inside the DDR chip, which is where the slowness comes from.</p>
<h3 id="heading-how-ddr-access-works">How DDR Access Works</h3>
<p>When your CPU reads from DDR, a multi-step process occurs inside the memory controller and DDR chip.</p>
<p>First, the CPU sends an address request to the memory controller. The memory controller is a hardware block inside the processor that translates CPU addresses into the specific row and column addresses that the DDR chip understands.</p>
<p>Second, the memory controller activates the correct row inside the DDR chip. This step is called the RAS (Row Address Strobe) phase. The DDR chip is organized as a grid of tiny capacitors, and "activating a row" means reading all the capacitors in that row into a row buffer inside the DDR chip. This takes several clock cycles.</p>
<p>Third, the memory controller selects the correct column within the activated row. This is called the CAS (Column Address Strobe) phase. The DDR chip uses the column address to pick the right bits out of the row buffer. This also takes several clock cycles.</p>
<p>Fourth, the data is transferred back to the memory controller, and from there to the CPU. The data transfer happens on both clock edges (the "double data rate" part), which helps with throughput but doesn't reduce the initial latency of the RAS and CAS phases.</p>
<p>The total latency depends on what state the memory is in when the request arrives. If the correct row is already activated from a previous access (a "row hit"), the RAS phase can be skipped, and the access is faster. If a different row is active and needs to be closed (precharged) before the new row can be opened (a "row miss"), the access takes longer. If the DDR chip happens to be performing a refresh cycle at that moment, the access is delayed further.</p>
<p>In practice, DDR access latency ranges from about 5 to 20+ CPU clock cycles, depending on the access pattern and timing.</p>
<h3 id="heading-why-ddr-is-necessary">Why DDR is Necessary</h3>
<p>Because firmware often doesn't fit in TCM alone. Real embedded projects include protocol stacks, connectivity libraries, file system drivers, debug interfaces, and more. TCM is typically 2 to 3.5 MB total (ITCM + DTCM combined), and a full-featured firmware image can easily exceed that.</p>
<p>A real example showing memory usage before and after adding a wireless connectivity stack:</p>
<pre><code class="language-shell">Without connectivity stack:
    ITCM:      506,996 B     (24.18%)
    DTCM:      628,408 B     (39.96%)
    DDR:       558,779 B     (13.32%)

With connectivity stack:
    ITCM:      570,936 B     (27.22%)
    DTCM:      727,240 B     (46.24%)
    DDR:       622,915 B     (14.85%)

Delta:
    ITCM: +63,940 B   (~62 KB of additional code)
    DTCM: +98,832 B   (~96 KB of additional data)
    DDR:  +64,136 B   (~62 KB of additional data/code)
</code></pre>
<p>This comparison shows memory usage from the same project built with and without a wireless connectivity stack.</p>
<p>The "Without" rows show the baseline. The "With" rows show the usage after adding the connectivity feature. The "Delta" rows show the difference.</p>
<p>Adding this single feature consumed an extra ~220 KB across all three memory regions. The time-critical parts of the stack (interrupt handlers, buffer management) went into ITCM and DTCM. The rest (packet parsers, connection management, configuration logic) went into DDR where it doesn't need single-cycle performance.</p>
<h3 id="heading-what-belongs-in-ddr">What Belongs in DDR?</h3>
<p><strong>Initialization and configuration code</strong> is the easiest category. Functions that run once at boot, like parsing a configuration file, initializing peripherals, or setting up data structures, don't need fast execution. They run once, take a few extra milliseconds because of DDR latency, and then never run again. Nobody notices. Put them in DDR and save TCM space for the code that runs a million times per second.</p>
<p><strong>Large buffers</strong> must go in DDR because they simply can't fit in TCM. An image framebuffer for a 320x240 display at 16 bits per pixel is 150 KB. A network packet pool might be 32 KB or more. A file system cache might be 64 KB. These buffers would consume a significant fraction of DTCM's total capacity, leaving no room for the stack and variables that actually need single-cycle access.</p>
<p><strong>Infrequently accessed data</strong> belongs in DDR as well. Calibration tables that are loaded once at boot and then read occasionally during operation, string tables for debug messages that are only printed during development or error conditions, and error description tables are all fine in DDR. The extra latency per access is irrelevant when the access count is low.</p>
<p><strong>Non-time-critical code</strong> rounds out the DDR category. Protocol stacks (Bluetooth, Wi-Fi, TCP/IP), file system drivers, OTA update handlers, and shell/debug command interpreters all do important work, but none of them need to execute in a single clock cycle per instruction. They can tolerate the higher latency of DDR without affecting system behavior.</p>
<h3 id="heading-how-to-place-code-and-data-in-ddr">How to Place Code and Data in DDR</h3>
<pre><code class="language-c">__attribute__((section(".ddr_text")))
void parse_config_file(const char *path) {
    // Runs from DDR, slower instruction fetch,
    // but config parsing happens once at boot,
    // so the latency does not affect runtime performance.
}

__attribute__((section(".ddr_bss")))
static uint8_t network_packet_pool[32768];

__attribute__((section(".ddr_bss")))
static uint8_t framebuffer[320 * 240 * 2];  // 150 KB, far too large for TCM
</code></pre>
<p>In this code, <code>parse_config_file</code> is placed in the <code>.ddr_text</code> section, which the linker script maps to DDR. Every instruction in this function will be fetched from DDR at multi-cycle latency, but since config parsing happens once at boot, the extra time is negligible.</p>
<p>The <code>network_packet_pool</code> is a 32 KB buffer placed in <code>.ddr_bss</code>. The <code>.bss</code> suffix is a convention indicating that this is zero-initialized data (the linker will ensure the memory is zeroed at startup rather than storing 32 KB of zeros in the firmware image). This buffer is used for network packet storage, which is not time-critical enough to justify DTCM space.</p>
<p>The <code>framebuffer</code> is a 150 KB buffer (320 pixels wide, 240 pixels tall, 2 bytes per pixel) also placed in <code>.ddr_bss</code>. At 150 KB, this single buffer would consume about 10% of DTCM's total capacity, which is far too expensive when the display update isn't a hard real-time operation.</p>
<h2 id="heading-how-they-compare-a-side-by-side-overview">How They Compare: A Side-by-Side Overview</h2>
<table>
<thead>
<tr>
<th>Property</th>
<th>ITCM</th>
<th>DTCM</th>
<th>DDR</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Purpose</strong></td>
<td>Instruction storage</td>
<td>Data storage</td>
<td>General-purpose storage</td>
</tr>
<tr>
<td><strong>Location</strong></td>
<td>On-die, dedicated bus</td>
<td>On-die, dedicated bus</td>
<td>Off-chip, through memory controller</td>
</tr>
<tr>
<td><strong>Access latency</strong></td>
<td>1 cycle (deterministic)</td>
<td>1 cycle (deterministic)</td>
<td>5 to 20+ cycles (variable)</td>
</tr>
<tr>
<td><strong>Typical size</strong></td>
<td>512 KB to 2 MB</td>
<td>512 KB to 1.5 MB</td>
<td>4 MB to several GB</td>
</tr>
<tr>
<td><strong>Technology</strong></td>
<td>SRAM</td>
<td>SRAM</td>
<td>DRAM (requires refresh)</td>
</tr>
<tr>
<td><strong>Power</strong></td>
<td>Low (no refresh needed)</td>
<td>Low (no refresh needed)</td>
<td>Higher (constant refresh)</td>
</tr>
<tr>
<td><strong>Best for</strong></td>
<td>ISRs, real-time loops, DSP</td>
<td>Stack, hot variables, lookup tables</td>
<td>Large buffers, init code, protocol stacks</td>
</tr>
</tbody></table>
<p>This table summarizes the key differences between the three memory types. The most important columns are "Access latency" and "Typical size," because they represent the fundamental tradeoff: TCM is fast but small, DDR is slow but large.</p>
<p>The "Technology" column explains why: TCM uses SRAM (static RAM), which stores each bit using a flip-flop circuit that holds its state as long as power is applied. DDR uses DRAM (dynamic RAM), which stores each bit as charge in a tiny capacitor. Because capacitors leak charge, DRAM must be periodically refreshed, which adds power consumption and introduces occasional access delays when a refresh cycle coincides with a read request.</p>
<h3 id="heading-the-memory-map">The Memory Map</h3>
<pre><code class="language-markdown">Address Space:
  +------------------------------+  0x00000000
  |                              |
  |         ITCM (2 MB)          |  Single-cycle Inst Fetch
  |    ISRs, real-time loops,    |
  |    DSP, critical code        |
  |                              |
  +------------------------------+  0x00200000
  |       (reserved/gap)         |
  +------------------------------+  0x20000000
  |                              |
  |       DTCM (~1.5 MB)         |  Single-cycle Data Access
  |    Stack, hot variables,     |
  |    lookup tables, DMA bufs   |
  |                              |
  +------------------------------+  0x20180000
  |       (reserved/gap)         |
  +------------------------------+  0x80000000
  |                              |
  |         DDR (4 MB)           |  Multi-cycle Access
  |    Large buffers, init code, |
  |    protocol stacks, config   |
  |                              |
  +------------------------------+  0x80400000
</code></pre>
<p>This diagram shows the CPU's address space laid out from low addresses at the top to high addresses at the bottom. ITCM occupies the lowest 2 MB starting at address 0x00000000. After a gap of reserved/unused address space, DTCM sits at 0x20000000 and spans about 1.5 MB. Another gap of reserved space follows, and then DDR starts at 0x80000000 with 4 MB of space.</p>
<p>The gaps between regions are important. They're reserved address ranges that don't map to any physical memory. If your code accidentally reads from or writes to an address in one of these gaps, the result depends on the chip's bus fault configuration: it might trigger a HardFault exception, or it might silently return garbage data.</p>
<p>These addresses are illustrative. Every chip has its own memory map, documented in its Technical Reference Manual (TRM). Always consult your chip's TRM for the exact addresses and sizes.</p>
<h2 id="heading-how-to-decide-where-to-place-code-and-data">How to Decide Where to Place Code and Data</h2>
<pre><code class="language-plaintext">Is it code or data?
|
+-- CODE (instructions):
|   +-- Called from an ISR or runs in a real-time loop?
|   |   +-- YES -&gt; ITCM (deterministic timing is critical)
|   +-- Called frequently in the main processing pipeline?
|   |   +-- YES -&gt; ITCM (if space is available)
|   +-- Called rarely (init, config, debug)?
|       +-- DDR (save ITCM space for critical code)
|
+-- DATA (variables, buffers, tables):
    +-- Accessed in an ISR or real-time context?
    |   +-- YES -&gt; DTCM (single-cycle, deterministic)
    +-- Small and frequently accessed?
    |   +-- YES -&gt; DTCM (if space is available)
    +-- Large buffer (&gt;16 KB)?
    |   +-- Probably DDR (DTCM cannot afford the space)
    +-- Accessed only once at boot or very rarely?
        +-- DDR (do not use DTCM for this)
</code></pre>
<p>This decision tree captures the thought process for placing each piece of firmware into the right memory region.</p>
<p>Start by asking whether you're placing code (instructions) or data (variables, buffers, tables). For code, the primary question is how often it runs and whether it has timing constraints. ISR code and real-time loop code goes in ITCM. Everything else goes in DDR. For data, the primary question is how often it's accessed and how large it is. Small, frequently accessed data goes in DTCM. Large buffers and rarely-accessed data go in DDR.</p>
<p>The general principle: <strong>put the hottest code and data in TCM, and everything else in DDR</strong>. "Hot" means frequently accessed, latency-sensitive, or requiring deterministic timing. When in doubt, start with DDR placement and move things to TCM only when profiling shows it's necessary. It's much easier to promote a function from DDR to ITCM after discovering it's a bottleneck than to cram everything into ITCM from the start and run out of space.</p>
<h2 id="heading-how-the-linker-script-controls-memory-placement">How the Linker Script Controls Memory Placement</h2>
<p>Everything we've discussed so far (section attributes, memory placement, address assignments) comes together in the <strong>linker script</strong>. This is a file (usually with a <code>.ld</code> extension) that tells the linker exactly which sections go into which memory regions. The linker script is the single source of truth for your firmware's memory layout.</p>
<pre><code class="language-plaintext">MEMORY
{
    ITCM    (rx)  : ORIGIN = 0x00000000, LENGTH = 2M
    DTCM    (rw)  : ORIGIN = 0x20000000, LENGTH = 1536K
    DDR     (rwx) : ORIGIN = 0x80000000, LENGTH = 4M
}

SECTIONS
{
    /* === ITCM: Critical code === */
    .itcm_text :
    {
        KEEP(*(.isr_vector))          /* Interrupt vector table */
        *(.itcm_text)                 /* Functions with __attribute__((section(".itcm_text"))) */
        *audio_processing.o(.text)    /* All code from audio_processing.c */
        *motor_control.o(.text)       /* All code from motor_control.c */
    } &gt; ITCM

    /* === DDR: Non-critical code === */
    .ddr_text :
    {
        *(.text)                      /* Default catch-all for remaining code */
        *(.text*)
        *(.rodata)                    /* Read-only data (string literals, constants) */
        *(.rodata*)
    } &gt; DDR

    /* === DTCM: Critical data === */
    .dtcm_data :
    {
        *(.dtcm_data)                 /* Data with __attribute__((section(".dtcm_data"))) */
        *audio_processing.o(.data)    /* All initialized data from audio_processing.c */
        *audio_processing.o(.bss)     /* All zero-initialized data from audio_processing.c */
    } &gt; DTCM

    /* === DTCM: Stack === */
    .stack (NOLOAD) :
    {
        . = ALIGN(8);
        __stack_start = .;
        . = . + 8K;                  /* 8 KB stack */
        __stack_end = .;
    } &gt; DTCM

    /* === DDR: Everything else === */
    .ddr_data :
    {
        *(.data)                      /* Default catch-all for remaining initialized data */
        *(.bss)                       /* Default catch-all for remaining zero-initialized data */
        *(COMMON)
    } &gt; DDR
}
</code></pre>
<p>This linker script has two main blocks: <code>MEMORY</code> and <code>SECTIONS</code>.</p>
<p>The <code>MEMORY</code> block defines the physical memory regions available on the chip. Each line declares a region name, its permissions (<code>rx</code> for read-execute, <code>rw</code> for read-write, <code>rwx</code> for read-write-execute), its starting address (<code>ORIGIN</code>), and its size (<code>LENGTH</code>). These values must match your chip's actual memory map as documented in its reference manual.</p>
<p>The <code>SECTIONS</code> block defines how the linker should distribute compiled code and data across those memory regions. Each section rule consists of a section name (like <code>.itcm_text</code>), a list of input patterns that specify which object file sections to include, and a <code>&gt; REGION</code> directive that tells the linker which memory region to place the output section in.</p>
<p>The <code>.itcm_text</code> section collects the interrupt vector table (<code>KEEP(*(.isr_vector))</code>), any functions explicitly marked with <code>__attribute__((section(".itcm_text")))</code>, and all code from <code>audio_processing.o</code> and <code>motor_control.o</code>. The <code>KEEP</code> directive prevents the linker from discarding the interrupt vector table during garbage collection, even if no code appears to reference it directly. All of this goes into ITCM.</p>
<p>The <code>.ddr_text</code> section uses catch-all patterns <code>*(.text)</code> and <code>*(.text*)</code> to collect all remaining code that wasn't claimed by the ITCM section above. It also collects read-only data (<code>.rodata</code>), which includes string literals and <code>const</code> variables. All of this goes into DDR.</p>
<p>The <code>.dtcm_data</code> section collects explicitly-placed data and all data from <code>audio_processing.o</code>. The <code>.stack</code> section reserves 8 KB for the stack with 8-byte alignment, and exports the <code>__stack_start</code> and <code>__stack_end</code> symbols that your startup code and stack profiling code can reference. Both go into DTCM.</p>
<p>The <code>.ddr_data</code> section collects all remaining data with catch-all patterns, and goes into DDR.</p>
<h3 id="heading-how-section-matching-works">How Section Matching Works</h3>
<p>The linker processes sections from top to bottom. When it encounters a wildcard pattern like <code>*(.text)</code>, it matches all <code>.text</code> sections that haven't already been claimed by a more specific rule earlier in the script.</p>
<p>So in the example above, <code>*audio_processing.o(.text)</code> in the ITCM section claims all code from <code>audio_processing.c</code> first. Then, when the linker reaches <code>*(.text)</code> in the DDR section, <code>audio_processing.o</code>'s <code>.text</code> section has already been placed, so it's skipped. Only unclaimed <code>.text</code> sections from other object files match the DDR catch-all.</p>
<p>This means the <strong>order of sections in your linker script matters</strong>. Place your specific rules (individual object files, named sections) before the generic catch-all rules. If you put the <code>*(.text)</code> catch-all before the <code>*audio_processing.o(.text)</code> rule, the catch-all would claim everything first, and the specific rule would match nothing.</p>
<h2 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h2>
<h3 id="heading-1-stack-overflow-in-dtcm">1. Stack Overflow in DTCM</h3>
<p>Your stack lives in DTCM. DTCM is small. If you declare a large local array inside a function, it goes on the stack:</p>
<pre><code class="language-c">void problematic_function(void) {
    uint8_t huge_local_buffer[65536];  // 64 KB allocated on the stack
    // This consumes 64 KB of DTCM immediately
}
</code></pre>
<p>This code declares a 64 KB local array. Because it's a local variable (not <code>static</code>), it is allocated on the stack when the function is called. If your total stack size is 8 KB (as in the linker script example above), this single declaration overflows the stack by 56 KB, writing into whatever memory is adjacent to the stack in DTCM.</p>
<p>On a desktop OS, a stack overflow triggers a segmentation fault because the OS uses virtual memory and guard pages to detect it.</p>
<p>In an embedded system without memory protection, the stack silently grows into adjacent memory regions, corrupting whatever data is stored there. The resulting bugs are extremely difficult to diagnose because the symptoms (corrupted variables, erratic behavior, intermittent crashes) appear unrelated to the actual cause. You might spend days debugging a seemingly random data corruption issue before realizing the root cause is a stack overflow from a function three call levels deep.</p>
<p><strong>The fix</strong>: Use <code>static</code> allocation or heap allocation for large buffers, and place them in DDR:</p>
<pre><code class="language-c">void fixed_function(void) {
    __attribute__((section(".ddr_bss")))
    static uint8_t huge_buffer[65536];  // In DDR, not on the stack

    // Stack is safe, DTCM is not wasted
}
</code></pre>
<p>By making the buffer <code>static</code>, it's no longer allocated on the stack. Instead, the linker allocates it once in the <code>.ddr_bss</code> section, which maps to DDR. The buffer persists for the entire lifetime of the program (like a global variable), but its name is scoped to this function. The stack only holds a pointer to the buffer, which is a few bytes instead of 64 KB.</p>
<h3 id="heading-2-overfilling-itcm">2. Overfilling ITCM</h3>
<p>If you exceed ITCM's capacity, the linker will produce an error along the lines of "region ITCM overflowed by N bytes." But if you're <em>close</em> to the limit, you're one library update or feature addition away from a build failure. A minor version bump of your RTOS or connectivity stack could add enough code to push ITCM over the edge.</p>
<p>Keep headroom. The 27% utilization shown earlier is healthy. If you're above 85%, you should actively work on moving less-critical code to DDR. If you're above 95%, you have no room for growth and need to make immediate changes. Setting up automated memory budget checks in your CI pipeline (covered later in this article) prevents surprises.</p>
<h3 id="heading-3-ignoring-alignment-requirements">3. Ignoring Alignment Requirements</h3>
<p>TCM memories often have alignment requirements. On Cortex-M processors with strict alignment enforcement, accessing a 32-bit value at an unaligned address causes a HardFault exception.</p>
<pre><code class="language-c">/* Problematic: packed struct can create unaligned fields */
__attribute__((section(".dtcm_data"), packed))
struct badly_aligned {
    uint8_t  flag;
    uint32_t counter;  // May be at byte offset 1, unaligned
};

/* Correct: natural alignment, with minor padding */
__attribute__((section(".dtcm_data")))
struct properly_aligned {
    uint32_t counter;  // At offset 0, 4-byte aligned
    uint8_t  flag;     // At offset 4
    // 3 bytes of padding follow, a small cost for correctness
};
</code></pre>
<p>In the first struct, the <code>packed</code> attribute tells the compiler to use no padding between fields. This means <code>counter</code> starts at byte offset 1 (right after the 1-byte <code>flag</code>), which isn't a multiple of 4. When the CPU tries to read a 32-bit value from a non-4-byte-aligned address in TCM, it triggers a HardFault on processors with strict alignment (which includes most Cortex-M cores).</p>
<p>In the second struct, the fields are ordered so that <code>counter</code> (4 bytes) comes first at offset 0, which is naturally 4-byte aligned. The <code>flag</code> (1 byte) follows at offset 4. The compiler inserts 3 bytes of padding after <code>flag</code> to bring the struct size to 8 bytes (a multiple of 4), but this is a small price for correct, crash-free operation.</p>
<h3 id="heading-4-dma-transfers-to-tcm-on-incompatible-bus-architectures">4. DMA Transfers to TCM on Incompatible Bus Architectures</h3>
<p>Some DMA controllers can't access TCM memory. Whether DMA can reach TCM depends entirely on your chip's internal bus architecture (the bus matrix).</p>
<p>If you set up a DMA transfer from a peripheral to a DTCM buffer, but the DMA controller doesn't have a bus path to DTCM, the transfer will either silently fail or write to an incorrect address.</p>
<p>Neither produces an obvious error. The DMA controller thinks it completed successfully, your code reads the buffer expecting fresh data, and you get stale or garbage values instead. This is one of the most confusing bugs in embedded development because everything <em>looks</em> correct in the code.</p>
<p><strong>Always check your chip's bus matrix diagram</strong> in the reference manual before using DMA with TCM buffers. The bus matrix diagram shows which masters (CPU, DMA, USB, and so on) can access which slaves (ITCM, DTCM, SRAM, DDR, peripherals). Look for whether the DMA controller's master port has a connection line to the TCM slave port. If it doesn't, your DMA transfers to TCM will not work.</p>
<h2 id="heading-performance-comparison-with-real-numbers">Performance Comparison With Real Numbers</h2>
<p>The following table compares access latencies across memory types, assuming a Cortex-R class processor at 400 MHz:</p>
<pre><code class="language-markdown">+---------------------+----------+----------+----------+
| Operation           | ITCM/    |   DDR    | Slowdown |
|                     | DTCM     |          | Factor   |
+---------------------+----------+----------+----------+
| Instruction fetch   | 1 cycle  | 5-20 cyc |   5-20x  |
| Data read (32-bit)  | 1 cycle  | 5-20 cyc |   5-20x  |
| Data write (32-bit) | 1 cycle  | 5-20 cyc |   5-20x  |
| Sequential burst    | 1 cyc/wd | 2-4 cy/wd|    2-4x  |
| Random access       | 1 cycle  | 10-20 cyc|  10-20x  |
+---------------------+----------+----------+----------+
</code></pre>
<p>This table shows the latency for five different types of memory operations. The first three rows (instruction fetch, data read, data write) show that individual accesses to TCM are always 1 cycle, while individual accesses to DDR range from 5 to 20 cycles depending on the memory's internal state. The slowdown factor is the ratio between the two.</p>
<p>The "Sequential burst" row shows what happens when you read or write consecutive addresses. DDR performs much better in burst mode (2-4 cycles per word instead of 5-20) because once a row is activated, subsequent reads from the same row skip the RAS phase. TCM is still 1 cycle per word because it doesn't have the row/column structure of DDR.</p>
<p>The "Random access" row shows the worst case for DDR. When each access hits a different row, the memory controller must precharge the old row and activate the new one every time. This is the 10-20 cycle range, and it's common in workloads that jump around in memory (traversing linked lists, hash table lookups, and indirect function calls through function pointer arrays).</p>
<p>The practical takeaway: if your code accesses DDR data, try to access it sequentially. Iterating through an array in order is much faster than jumping to random positions. Your memory controller and the DDR chip's internal prefetch logic work in your favor during sequential access patterns.</p>
<h2 id="heading-how-tcm-affects-power-consumption">How TCM Affects Power Consumption</h2>
<p>Memory placement has a direct impact on power consumption, something that becomes critical for battery-powered products.</p>
<p><strong>DDR requires constant refresh cycles.</strong> DRAM stores each bit as charge in a tiny capacitor, and that charge leaks over time.</p>
<p>To prevent data loss, the memory controller must read and rewrite every row in the DDR chip approximately every 64 ms. This refresh process consumes power even when the processor is sleeping and no code is running. On some systems, DDR refresh can account for a significant portion of the total sleep-mode power budget.</p>
<p><strong>TCM is SRAM-based and doesn't require refresh.</strong> SRAM stores data using flip-flop circuits that hold their state as long as power is applied. There is some leakage current (no transistor is perfect), but it is orders of magnitude lower than DDR refresh power.</p>
<p>For battery-powered devices (wearables, IoT sensors, medical devices), this means you should keep data that must survive sleep modes in DTCM when possible.</p>
<p>If your hardware supports it, power-gate the DDR chip during deep sleep to eliminate its refresh power entirely. The less DDR your firmware uses at runtime, the more aggressively you can manage DDR power states, which directly extends battery life.</p>
<h2 id="heading-how-to-profile-memory-usage">How to Profile Memory Usage</h2>
<p>After placing code and data into ITCM, DTCM, and DDR, you need to verify that everything fits, monitor usage over time, and catch regressions before they become build failures. There are several techniques for this, ranging from simple command-line tools to automated CI checks.</p>
<h3 id="heading-method-1-the-linker-map-file">Method 1: The Linker Map File</h3>
<p>Every time you build your firmware, the linker can produce a <strong>map file</strong>, a detailed text file that records where every symbol (function, variable, constant) ended up and how large it is. This is the most useful single artifact in embedded development for understanding memory usage.</p>
<p>To generate one, add <code>-Wl,-Map=output.map</code> to your linker flags:</p>
<pre><code class="language-shell">arm-none-eabi-gcc \
    -T linker_script.ld \
    -Wl,-Map=firmware.map \
    -o firmware.elf \
    main.o audio.o bluetooth.o
</code></pre>
<p>This command invokes the ARM GCC toolchain to link three object files (<code>main.o</code>, <code>audio.o</code>, <code>bluetooth.o</code>) using the linker script <code>linker_script.ld</code>. The <code>-Wl,-Map=firmware.map</code> flag tells GCC to pass the <code>-Map=firmware.map</code> option to the linker, which causes it to write a detailed map file alongside the output ELF binary. The map file can be thousands of lines long, but the most useful part is the summary at the end.</p>
<p>The summary at the end of the map file shows overall utilization per memory region:</p>
<pre><code class="language-shell">Memory region         Used Size  Region Size  %age Used
            ITCM:      570936 B         2 MB     27.22%
            DTCM:      727240 B    1572608 B     46.24%
             DDR:      622915 B         4 MB     14.85%
</code></pre>
<p>This summary shows three columns: how many bytes are used, the total size of the region, and the percentage used. It gives you the health of your firmware at a glance. As a rule of thumb, below 80% is healthy with room for growth. Between 80% and 90% is getting tight, and you should plan for how you will accommodate the next feature. Above 90% requires action: start moving things to a cheaper memory region or optimizing existing placement.</p>
<h3 id="heading-method-2-parsing-the-map-file-for-per-module-breakdown">Method 2: Parsing the Map File for Per-Module Breakdown</h3>
<p>The summary tells you <em>how much</em> memory is used, but not <em>who</em> is using it. The map file contains per-symbol details, but they're difficult to read manually because the file can be thousands of lines long with a format that isn't designed for human consumption.</p>
<p>The following Python script parses the map file and produces a per-module report showing which object files are consuming memory in which regions.</p>
<pre><code class="language-python">#!/usr/bin/env python3
"""Parse a linker map file and report memory usage per object file."""

import re
import sys
from collections import defaultdict

def parse_map_file(map_path):
    """Extract symbol placements from a GCC linker map file."""
    usage = defaultdict(lambda: defaultdict(int))

    regions = {
        'ITCM': (0x00000000, 0x00200000),
        'DTCM': (0x20000000, 0x20180000),
        'DDR':  (0x80000000, 0x80400000),
    }

    def addr_to_region(addr):
        for name, (start, end) in regions.items():
            if start &lt;= addr &lt; end:
                return name
        return 'UNKNOWN'

    symbol_re = re.compile(
        r'^\s+\S+\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s+(\S+\.o)'
    )

    with open(map_path) as f:
        for line in f:
            m = symbol_re.match(line)
            if m:
                addr = int(m.group(1), 16)
                size = int(m.group(2), 16)
                obj = m.group(3).split('/')[-1]
                region = addr_to_region(addr)
                usage[obj][region] += size

    return usage

def print_report(usage):
    """Print a sorted memory usage report."""
    print(f"{'Object File':&lt;35} {'ITCM':&gt;10} {'DTCM':&gt;10} {'DDR':&gt;10} {'Total':&gt;10}")
    print("-" * 80)

    totals = defaultdict(int)
    rows = []

    for obj, regions in usage.items():
        total = sum(regions.values())
        rows.append((obj, regions, total))
        for r, s in regions.items():
            totals[r] += s

    rows.sort(key=lambda x: x[2], reverse=True)

    for obj, regions, total in rows[:20]:
        print(f"{obj:&lt;35} "
              f"{regions.get('ITCM', 0):&gt;10,} "
              f"{regions.get('DTCM', 0):&gt;10,} "
              f"{regions.get('DDR', 0):&gt;10,} "
              f"{total:&gt;10,}")

    print("-" * 80)
    grand = sum(totals.values())
    print(f"{'TOTAL':&lt;35} "
          f"{totals.get('ITCM', 0):&gt;10,} "
          f"{totals.get('DTCM', 0):&gt;10,} "
          f"{totals.get('DDR', 0):&gt;10,} "
          f"{grand:&gt;10,}")

if __name__ == '__main__':
    usage = parse_map_file(sys.argv[1])
    print_report(usage)
</code></pre>
<p>This script does three things. First, <code>parse_map_file</code> reads the map file line by line, looking for lines that match the format of a symbol placement entry (a section name, an address, a size, and an object file name). For each match, it converts the hex address to an integer, determines which memory region it falls in using the <code>addr_to_region</code> helper, and accumulates the size into a nested dictionary keyed by object file and region.</p>
<p>Second, <code>print_report</code> sorts the object files by total memory usage (largest first), prints the top 20, and shows how much each one uses in each region.</p>
<p>Third, the <code>if __name__ == '__main__'</code> block makes the script runnable from the command line.</p>
<p>You'll need to adjust the address ranges in the <code>regions</code> dictionary to match your chip's memory map.</p>
<p>Run it with:</p>
<pre><code class="language-shell">python3 parse_map.py firmware.map
</code></pre>
<p>Sample output:</p>
<pre><code class="language-shell">Object File                              ITCM       DTCM        DDR      Total
--------------------------------------------------------------------------------
bluetooth_stack.o                      42,380     65,200     38,400    146,080
audio_processing.o                     89,200     32,000          0    121,200
wifi_driver.o                          21,560     33,632     25,736     80,928
sensor_hub.o                           45,000     18,400          0     63,400
libc.a(memcpy.o)                       12,340          0          0     12,340
...
--------------------------------------------------------------------------------
TOTAL                                 570,936    727,240    622,915  1,921,091
</code></pre>
<p>This output shows the top memory consumers in the firmware, sorted by total usage. Each row shows an object file and how many bytes it contributes to each memory region.</p>
<p>The <code>bluetooth_stack.o</code> file is the largest consumer at 146 KB total, spread across all three regions. The <code>audio_processing.o</code> file uses 121 KB, all in ITCM and DTCM (0 bytes in DDR), which makes sense because audio processing is time-critical and was placed entirely in TCM. The <code>libc.a(memcpy.o)</code> entry shows a C library function that was placed in ITCM, likely because it is called from performance-critical code paths.</p>
<h3 id="heading-method-3-the-size-command">Method 3: The <code>size</code> Command</h3>
<p>For a quick check without parsing the map file, use <code>arm-none-eabi-size</code>:</p>
<pre><code class="language-shell">arm-none-eabi-size -A firmware.elf
</code></pre>
<p>Output:</p>
<pre><code class="language-shell">firmware.elf  :
section               size        addr
.itcm_text          570936           0
.dtcm_data          530240   536870912
.dtcm_bss           196000   537401152
.stack                8192   537600000
.ddr_text           422915  2147483648
.ddr_data           120000  2147906563
.ddr_bss             80000  2148026563
Total              1928283
</code></pre>
<p>This output lists every section in the ELF binary, its size in bytes, and its starting address (shown in decimal).</p>
<p>You can map sections to memory regions by looking at the address: addresses near 0 are ITCM, addresses near 536 million (0x20000000) are DTCM, and addresses near 2.1 billion (0x80000000) are DDR.</p>
<p>Alternatively, the section names themselves indicate the region (<code>.itcm_text</code> is in ITCM, <code>.dtcm_data</code> and <code>.dtcm_bss</code> are in DTCM, <code>.ddr_text</code> and <code>.ddr_data</code> and <code>.ddr_bss</code> are in DDR).</p>
<p>The <code>-A</code> flag gives per-section sizes instead of the default BSD-format output. It's less detailed than the map file approach, but it runs instantly and gives you the big picture.</p>
<h3 id="heading-method-4-runtime-stack-profiling">Method 4: Runtime Stack Profiling</h3>
<p>Static analysis (map files, <code>size</code> output) tells you about compile-time placement. But some memory usage is dynamic, particularly the stack, which grows and shrinks at runtime based on call depth and local variable sizes. A function that allocates a 2 KB local buffer only uses that stack space while it is executing, so static analysis can't tell you the peak stack usage.</p>
<p>A common technique is <strong>stack watermarking</strong>: fill the entire stack region with a known pattern at boot, then periodically check how much of the pattern has been overwritten.</p>
<pre><code class="language-c">#define STACK_FILL_PATTERN 0xDEADBEEF

void stack_watermark_init(void) {
    extern uint32_t __stack_start;
    extern uint32_t __stack_end;
    uint32_t *p = &amp;__stack_start;

    register uint32_t sp asm("sp");
    while (p &lt; (uint32_t *)(sp - 64)) {
        *p++ = STACK_FILL_PATTERN;
    }
}

uint32_t stack_usage_bytes(void) {
    extern uint32_t __stack_start;
    extern uint32_t __stack_end;
    uint32_t *p = &amp;__stack_start;

    while (p &lt; &amp;__stack_end &amp;&amp; *p == STACK_FILL_PATTERN) {
        p++;
    }

    return (uint32_t)(&amp;__stack_end) - (uint32_t)p;
}

void check_stack_health(void) {
    uint32_t used = stack_usage_bytes();
    uint32_t total = 8192;
    uint32_t percent = (used * 100) / total;

    if (percent &gt; 80) {
        log_warning("Stack usage: %lu / %lu bytes (%lu%%)",
                    used, total, percent);
    }
}
</code></pre>
<p>The <code>stack_watermark_init</code> function fills the stack memory (from <code>__stack_start</code> to just below the current stack pointer) with the pattern <code>0xDEADBEEF</code>. The <code>extern</code> declarations reference the linker symbols defined in the linker script's <code>.stack</code> section. The <code>register uint32_t sp asm("sp")</code> line reads the current stack pointer value so the function knows where to stop filling (you do not want to overwrite your own stack frame). The 64-byte safety margin ensures the fill loop doesn't get too close to the active stack.</p>
<p>The <code>stack_usage_bytes</code> function scans from the bottom of the stack upward, counting how many words still contain the fill pattern. The first word that does <em>not</em> match the pattern indicates the deepest point the stack has reached (the high-water mark). The function returns the number of bytes from that point to the top of the stack.</p>
<p>The <code>check_stack_health</code> function computes the percentage of stack used and logs a warning if it exceeds 80%. Call this function periodically during normal operation to monitor stack usage.</p>
<p>Call <code>stack_watermark_init()</code> as early as possible in your startup code (before <code>main()</code> if you can), then call <code>check_stack_health()</code> periodically during normal operation. This tells you the high-water mark, the maximum stack depth your firmware has reached so far.</p>
<h3 id="heading-method-5-tracking-memory-across-builds">Method 5: Tracking Memory Across Builds</h3>
<p>Every time you add a feature or merge a change, run the memory profile before and after:</p>
<pre><code class="language-shell">arm-none-eabi-size -A firmware_before.elf &gt; mem_before.txt
arm-none-eabi-size -A firmware_after.elf &gt; mem_after.txt
diff mem_before.txt mem_after.txt
</code></pre>
<p>These three commands capture the section sizes of two firmware builds (before and after a change) into text files, then diff them to see what changed. This is useful but the raw diff output can be hard to read. The following script provides a cleaner view by computing the delta per memory region:</p>
<pre><code class="language-shell">#!/bin/bash
# memory_diff.sh - Compare memory usage between two builds

echo "Memory Impact of Change:"
echo "========================"

parse_size() {
    arm-none-eabi-size -A "$1" | awk '
    /\.itcm/  { itcm += $2 }
    /\.dtcm/  { dtcm += $2 }
    /\.ddr/   { ddr += $2 }
    /\.stack/ { dtcm += $2 }
    END { printf "%d %d %d", itcm, dtcm, ddr }
    '
}

read itcm_before dtcm_before ddr_before &lt;&lt;&lt; \((parse_size "\)1")
read itcm_after  dtcm_after  ddr_after  &lt;&lt;&lt; \((parse_size "\)2")

printf "ITCM: %+d bytes (%d -&gt; %d)\n" \
    \(((itcm_after - itcm_before)) \)itcm_before $itcm_after
printf "DTCM: %+d bytes (%d -&gt; %d)\n" \
    \(((dtcm_after - dtcm_before)) \)dtcm_before $dtcm_after
printf "DDR:  %+d bytes (%d -&gt; %d)\n" \
    \(((ddr_after - ddr_before)) \)ddr_before $ddr_after
</code></pre>
<p>This script takes two ELF files as arguments (the "before" and "after" builds). The <code>parse_size</code> function runs <code>arm-none-eabi-size -A</code> on the given ELF file and uses <code>awk</code> to sum up section sizes by memory region. Sections whose names contain <code>.itcm</code> are counted toward ITCM, sections containing <code>.dtcm</code> or <code>.stack</code> toward DTCM, and sections containing <code>.ddr</code> toward DDR. The main body reads the before and after values, then prints the delta for each region with a <code>+</code> or <code>-</code> sign.</p>
<p>Usage and output:</p>
<pre><code class="language-shell">$ ./memory_diff.sh firmware_without_bt.elf firmware_with_bt.elf

Memory Impact of Change:
========================
ITCM: +63940 bytes (506996 -&gt; 570936)
DTCM: +98832 bytes (628408 -&gt; 727240)
DDR:  +64136 bytes (558779 -&gt; 622915)
</code></pre>
<p>This output shows that adding the Bluetooth feature increased ITCM by about 62 KB, DTCM by about 96 KB, and DDR by about 62 KB. You can put this in your CI/CD pipeline so that every pull request shows exactly how much memory it costs.</p>
<h3 id="heading-method-6-automated-memory-budget-checks-in-ci">Method 6: Automated Memory Budget Checks in CI</h3>
<p>You can integrate memory profiling into your CI/CD pipeline to catch overflows before they land in your main branch.</p>
<pre><code class="language-shell">#!/bin/bash
# memory_check.sh - Fail CI if memory usage exceeds thresholds

ITCM_LIMIT=85   # percent
DTCM_LIMIT=80
DDR_LIMIT=90

check_region() {
    local name=\(1 used=\)2 total=\(3 limit=\)4
    local percent=$((used * 100 / total))

    if [ \(percent -ge \)limit ]; then
        echo "FAIL: \(name usage is \){percent}% (limit: ${limit}%)"
        echo "      Used: \(used / \)total bytes"
        return 1
    else
        echo "OK:   \(name usage is \){percent}% (limit: ${limit}%)"
        return 0
    fi
}

ITCM_USED=\((grep "ITCM:" firmware.map | awk '{print \)2}')
ITCM_TOTAL=$((2 * 1024 * 1024))

DTCM_USED=\((grep "DTCM:" firmware.map | awk '{print \)2}')
DTCM_TOTAL=1572608

DDR_USED=\((grep "DDR:" firmware.map | awk '{print \)2}')
DDR_TOTAL=$((4 * 1024 * 1024))

FAILED=0
check_region "ITCM" \(ITCM_USED \)ITCM_TOTAL $ITCM_LIMIT || FAILED=1
check_region "DTCM" \(DTCM_USED \)DTCM_TOTAL $DTCM_LIMIT || FAILED=1
check_region "DDR"  \(DDR_USED  \)DDR_TOTAL  $DDR_LIMIT  || FAILED=1

exit $FAILED
</code></pre>
<p>This script reads memory usage numbers from the linker map file and compares them against configurable percentage thresholds. The <code>check_region</code> function takes a region name, the number of bytes used, the total bytes available, and the percentage limit. It computes the actual percentage and prints either "OK" or "FAIL" along with the numbers. If any region exceeds its limit, the script exits with a non-zero status, which causes the CI build to fail.</p>
<p>The thresholds at the top (85% for ITCM, 80% for DTCM, 90% for DDR) should be adjusted based on your project's growth rate and how much headroom you want to maintain. DTCM has a lower limit because it fills up faster and is harder to free up.</p>
<p>Add this script to your build pipeline so every pull request shows its memory cost. If a change pushes any region past its threshold, the build fails and the developer knows immediately.</p>
<h3 id="heading-method-7-heap-tracking-at-runtime">Method 7: Heap Tracking at Runtime</h3>
<p>If your embedded project uses dynamic memory allocation (<code>malloc</code>/<code>free</code>), you can wrap the allocator to track usage.</p>
<pre><code class="language-c">static size_t heap_used = 0;
static size_t heap_peak = 0;

void *tracked_malloc(size_t size) {
    size_t *block = (size_t *)malloc(size + sizeof(size_t));
    if (!block) return NULL;

    *block = size;
    heap_used += size;
    if (heap_used &gt; heap_peak) {
        heap_peak = heap_used;
    }

    return (void *)(block + 1);
}

void tracked_free(void *ptr) {
    if (!ptr) return;
    size_t *block = ((size_t *)ptr) - 1;
    heap_used -= *block;
    free(block);
}

void print_heap_stats(void) {
    printf("Heap: current=%zu bytes, peak=%zu bytes\n",
           heap_used, heap_peak);
}
</code></pre>
<p>This code wraps <code>malloc</code> and <code>free</code> with tracking logic. The <code>tracked_malloc</code> function allocates slightly more memory than requested (an extra <code>sizeof(size_t)</code> bytes) and stores the requested size in the first word of the allocation. It then updates the <code>heap_used</code> counter and, if the new total exceeds the previous peak, updates <code>heap_peak</code>. It returns a pointer that's offset past the size header, so the caller sees a normal pointer to their data.</p>
<p>The <code>tracked_free</code> function reverses the process: it subtracts one <code>size_t</code> from the pointer to find the hidden size header, subtracts that size from <code>heap_used</code>, and calls the real <code>free</code> on the original block.</p>
<p>The <code>print_heap_stats</code> function prints the current and peak heap usage. Call it periodically or on demand through a debug interface (UART console, debug CLI) to monitor how much heap your firmware is using.</p>
<p>This approach has a small overhead (one extra word per allocation), but it gives you visibility into dynamic memory usage that's otherwise completely invisible. It's especially useful for tracking down memory leaks: if <code>heap_used</code> keeps growing over time without ever decreasing, something is allocating without freeing.</p>
<h2 id="heading-summary">Summary</h2>
<p>Embedded processors based on ARM Cortex-M and Cortex-R architectures give you direct control over three memory regions with very different performance characteristics.</p>
<p><strong>ITCM (Instruction Tightly-Coupled Memory)</strong> stores your most performance-critical code. It provides single-cycle, deterministic instruction fetch. It's small (typically 512 KB to 2 MB), so reserve it for ISRs, real-time processing functions, and hot loops.</p>
<p><strong>DTCM (Data Tightly-Coupled Memory)</strong> stores your most performance-critical data. It also provides single-cycle, deterministic access. Your stack lives here by default. It's even smaller than ITCM and fills up quickly, so be deliberate about what you place in it.</p>
<p><strong>DDR (Double Data Rate) memory</strong> stores everything else. It's much larger but slower (5 to 20+ cycles per access, with variable latency). Use it for initialization code, large buffers, protocol stacks, and anything that doesn't need deterministic timing.</p>
<p>You control placement through <code>__attribute__((section(...)))</code> in your C code and section-to-region mappings in your linker script. You verify placement through map files, the <code>size</code> command, and runtime profiling techniques like stack watermarking. The core skill is knowing which region each piece of your firmware belongs in, and having the tooling to catch mistakes early.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Scale Bluetooth Across Android, iOS, and Embedded Devices ]]>
                </title>
                <description>
                    <![CDATA[ Bluetooth is one of those inventions that seems magical the first time you use it. You turn on a gadget, pair it with your phone, and suddenly they are talking to each other without a single wire in sight. Music plays through your headphones, your sm... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-scale-bluetooth-across-devices/</link>
                <guid isPermaLink="false">691742dfb6a85c7f18a5fc15</guid>
                
                    <category>
                        <![CDATA[ bluetooth ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iOS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Android ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nikheel Vishwas Savant ]]>
                </dc:creator>
                <pubDate>Thu, 13 Nov 2025 23:00:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763131642774/dd2366f8-f491-4313-901e-acd4c1d937e2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Bluetooth is one of those inventions that seems magical the first time you use it. You turn on a gadget, pair it with your phone, and suddenly they are talking to each other without a single wire in sight. Music plays through your headphones, your smartwatch shows messages from your friends, and for a brief moment it feels like technology finally has its act together. Everything works and life is good.</p>
<p>Then you try to connect one more thing. Maybe a fitness band, a smart lock, or that tiny temperature sensor you ordered online because it was on sale. That is when the charm fades and reality walks in. Suddenly the connection drops, your phone cannot find the device anymore, and the once-friendly Bluetooth logo on your screen starts to feel like a taunt. You restart, you unpair, you try again, and somehow it only gets worse. What was once effortless turns into a puzzle with no clear solution.</p>
<p>Here is the secret that few people know: Bluetooth was never meant to handle the chaos we put it through today. When engineers designed it in the late 1990s, they imagined a world of simple one-to-one connections. A laptop talking to a mouse. A phone connecting to a headset. That was the whole idea. Fast-forward to the present and we are using the same technology to run entire networks of wearables, sensors, and smart appliances. We ask it to connect not just one or two devices but sometimes dozens of them at the same time, each running on different hardware and software. It is a miracle that it works at all.</p>
<p>To make things even more interesting, these devices live in very different worlds. Android devices are like an open playground where every manufacturer adds its own slide and swing set. iPhones live inside Apple’s carefully fenced garden where everything is polished but also tightly controlled. Embedded devices, like the ones built on tiny chips inside sensors or IoT boards, are the quiet introverts of the group. They have little memory, tiny batteries, and a strong preference for naps to save power. Getting all three to cooperate is a bit like trying to organize a band where one member only plays jazz, another insists on classical, and the third speaks in Morse code.</p>
<p>That is what engineers mean when they talk about scaling Bluetooth. It is not just about adding more devices. It is about making sure completely different systems can talk to each other reliably and continuously without draining their batteries or losing their minds. It requires design decisions that consider timing, power management, data formats, and even how the operating system schedules background tasks.</p>
<p>This article will guide you through that strange world. We will peel back the layers of how Bluetooth actually works and what happens when Android, iOS, and embedded devices try to share the same airwaves. We will explore why each one behaves the way it does and what you can do to build systems that stay connected instead of collapsing under their own complexity.</p>
<p>By the end, you will see that Bluetooth is not really broken. It is simply overworked. It is a polite translator trying to keep three very different languages in sync. Once you learn how to manage its quirks and give it the structure it needs, Bluetooth becomes not a source of frustration but a quiet, invisible network that holds the modern world together.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-bluetooth-has-two-personalities-meet-classic-and-ble">Bluetooth Has Two Personalities — Meet Classic and BLE</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-android-ios-and-embedded-devices-the-odd-trio">Android, iOS, and Embedded Devices — The Odd Trio</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-architecting-for-scale-herding-cats-but-wirelessly">Architecting for Scale — Herding Cats, but Wirelessly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-connection-discovery-and-data-flow-the-bluetooth-dating-game">Connection, Discovery, and Data Flow — The Bluetooth Dating Game</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-platform-quirks-and-how-to-stay-sane">Platform Quirks — And How to Stay Sane</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-security-and-privacy-at-scale">Security and Privacy at Scale</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-power-and-performance-tuning">Power and Performance Tuning</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-provisioning-and-firmware-updates-welcome-to-device-kindergarten">Provisioning and Firmware Updates — Welcome to Device Kindergarten</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-debugging-monitoring-and-testing-across-platforms">Debugging, Monitoring, and Testing Across Platforms</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-architecture-example-when-bluetooth-finally-behaves">Real-World Architecture Example — When Bluetooth Finally Behaves</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-checklist-building-a-truly-scalable-bluetooth-system">Checklist — Building a Truly Scalable Bluetooth System</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrap-up-lessons-from-the-field">Wrap-Up — Lessons from the Field</a></p>
</li>
</ul>
<h2 id="heading-bluetooth-has-two-personalities-meet-classic-and-ble">Bluetooth Has Two Personalities — Meet Classic and BLE</h2>
<p><img src="https://elainnovation.com/wp-content/uploads/2021/12/Bluetooth-VS-BLE-EN.jpg.webp" alt="What is the difference between Bluetooth and Bluetooth Low Energy (BLE)?" width="600" height="400" loading="lazy"></p>
<p>Before we can talk about scaling Bluetooth, we have to understand that Bluetooth itself has a bit of an identity crisis. It actually comes in two flavors: Classic Bluetooth and Bluetooth Low Energy, also called BLE. They share the same name and sometimes even live on the same chip, but under the hood they behave very differently. Think of them as twins who went to completely different schools and now have opposite personalities.</p>
<p>Classic Bluetooth is the older sibling. It was designed for steady, high-speed data streams. This is the version your headphones, speakers, and car systems use. It is reliable for sending large amounts of data like audio, but it is also chatty and power-hungry. It likes to stay connected all the time, constantly keeping the line open so it can send sound packets smoothly. You could say Classic Bluetooth is like that one friend who calls instead of texting and keeps the conversation going even when there is nothing left to say.</p>
<p>Then there is Bluetooth Low Energy, the younger, more introverted sibling. BLE was designed for devices that need to last for weeks or months on tiny batteries. It does not keep a constant connection open. Instead, it wakes up, sends or receives a little bit of data, and then goes back to sleep. It is the protocol behind fitness trackers, heart rate monitors, smart locks, and most modern IoT devices. If Classic Bluetooth is a full-time conversation, BLE is more like sending quick text messages throughout the day, short, efficient, and battery-friendly.</p>
<p>The funny thing is that even though they share the same wireless spectrum and sometimes even the same antenna, these two modes do not talk to each other directly. A BLE device cannot communicate with a Classic Bluetooth-only device. This is why your wireless headphones can pair with your phone, but your BLE heart rate monitor cannot talk to your old Bluetooth speaker. They live in the same neighborhood but never attend the same parties.</p>
<p>Most of the world’s scaling problems come from BLE, not Classic Bluetooth. Classic has been around long enough that its use cases are stable and well understood. BLE, on the other hand, is used in thousands of different kinds of devices, each with different timing requirements, power limits, and operating systems. When you try to make Android, iOS, and embedded systems all use BLE together, you are juggling three slightly different interpretations of the same rulebook.</p>
<p>To make things trickier, each platform implements BLE its own way. Android exposes it through flexible but sometimes unpredictable APIs. iOS keeps it tidy under Apple’s strict Core Bluetooth framework. Embedded devices rely on lightweight vendor stacks that can vary from chip to chip. Every one of these stacks follows the same Bluetooth specification, but like recipes written by different chefs, the results can taste a little different.</p>
<p>Understanding this dual nature is key to building anything that scales. You must know when to use Classic Bluetooth for high-speed continuous data, when to use BLE for low-power bursts, and how to design your system so that the right devices use the right mode. It is the first step in turning Bluetooth from a confusing mystery into a reliable network you can actually control.</p>
<h2 id="heading-android-ios-and-embedded-devices-the-odd-trio">Android, iOS, and Embedded Devices — The Odd Trio</h2>
<p><img src="https://cdn.dca-design.com/uploads/images/News/_full_width_content_image/105358/Bluetooth_DCA_News_Article_003.webp?v=1749036238" alt="Working with Bluetooth Low Energy across Android and iOS - News - DCA Design" width="600" height="400" loading="lazy"></p>
<p>Now that we know Bluetooth has two personalities, let’s meet the three characters that make scaling it so complicated: Android, iOS, and embedded devices. They all speak Bluetooth, but in their own unique accents. Sometimes they understand each other perfectly, and other times it feels like they’re arguing in three different languages while pretending they’re on the same page.</p>
<p>Let’s start with Android. Android is the enthusiastic extrovert of the group. It gives you tons of control and freedom. You can scan, connect, advertise, read, write, and basically poke around every corner of the Bluetooth stack. But that freedom comes with chaos. Because Android runs on phones made by dozens of manufacturers, each one tweaks the Bluetooth implementation a little differently. On one phone, everything works flawlessly. On another, the same code randomly drops connections or refuses to scan in the background. Even Android engineers joke that if your Bluetooth works the same on every device, you’ve probably entered a parallel universe.</p>
<p>Android is powerful but unpredictable. It’s like a sports car that can win a race on a good day but sometimes refuses to start if it doesn’t like the weather. The trick is to write code that expects weird behavior, to build your own connection queues, add retries, and prepare for the occasional glitch. Developers who survive Android Bluetooth bugs don’t just gain experience, they gain humility.</p>
<p>Then there’s iOS, Apple’s polished and opinionated perfectionist. Unlike Android, iOS is consistent. The same code usually behaves the same way across every iPhone and iPad. Apple’s Bluetooth framework, called Core Bluetooth, is beautifully organized and well-documented. But Apple also has strict rules about what you can and can’t do. Background scanning? Only in very specific cases. Advertising? Only for certain UUIDs. Access to lower-level Bluetooth layers? Absolutely not. Apple’s approach is like a luxury hotel: everything looks gorgeous, but you’re not allowed in the kitchen.</p>
<p>Working with iOS feels calm at first. Your connections are stable, your APIs are clear, and your devices behave predictably. But the moment you need to do something slightly unconventional, like connecting to multiple peripherals at once or keeping the app alive in the background, iOS politely says, “No, that’s not how we do things here.” Developers often end up performing delicate dances with background modes, notifications, and clever reconnection tricks just to make things feel seamless for users.</p>
<p>And then we have the third member of the trio: embedded devices. These are the quiet, uncomplaining ones that actually do most of the work. They live inside your smart sensors, wearables, and IoT nodes. They’re usually built around tiny chips with limited memory and low-power processors. They don’t have fancy operating systems or flashy UI frameworks. All they know is how to advertise, connect, send data, and then go back to sleep to save battery.</p>
<p>Embedded devices are loyal but easily overwhelmed. They can’t handle constant large data transfers, and they get cranky if you make them maintain too many simultaneous connections. Imagine trying to run a marathon after eating one grape, that’s what it’s like for a small BLE chip to handle too much traffic. Yet, these little devices are the backbone of every scalable Bluetooth network. They measure your heart rate, control your smart lights, and track your environmental sensors, all while running quietly in the background.</p>
<p>The real challenge begins when you try to make these three cooperate. Android wants freedom, iOS wants structure, and embedded devices just want a nap. Getting them all to work together is like managing a group project where one person writes essays at midnight, another color-codes everything, and the third forgets to charge their laptop. But when you finally get it right, when Android, iOS, and your embedded nodes connect seamlessly, it feels like magic again.</p>
<p>In the next section, we’ll explore how to actually make that happen. You’ll see how to design a Bluetooth architecture that scales gracefully across these platforms instead of collapsing into a pile of logs and retries. It’s part engineering, part patience, and part diplomacy.</p>
<h2 id="heading-architecting-for-scale-herding-cats-but-wirelessly">Architecting for Scale — Herding Cats, but Wirelessly</h2>
<p>If there’s one secret to scaling Bluetooth, it’s this: treat it like herding cats. You’ll never truly <em>control</em> it, but with enough structure, patience, and a bit of catnip (or clever engineering), you can convince all the cats to move in roughly the same direction.</p>
<p>Building a Bluetooth system that spans Android, iOS, and embedded devices isn’t just about writing code that connects things. It’s about designing <em>relationships</em>, the rules and boundaries that keep those connections healthy. The key idea here is <strong>architecture</strong>, which is a fancy word for “deciding who does what, when, and how.” Without a solid architecture, your Bluetooth project quickly turns into a tangle of callbacks, disconnections, and unanswered packets.</p>
<p>The first principle of Bluetooth architecture is <strong>abstraction</strong>. Every platform has its own Bluetooth API, but the basic idea is always the same: scan for devices, connect, exchange data, and disconnect. So instead of writing separate logic for each platform, you create one unified interface, a sort of translator layer, that hides all the messy differences underneath. In practice, this means you can write something like <code>connect(device)</code> in your app, and whether you’re on Android, iOS, or even a Raspberry Pi, the underlying code figures out how to make it happen.</p>
<p>This abstraction layer is your peacekeeper. It prevents the rest of your app from needing to know whether it’s talking to a Nordic chip on a wristband, a smart bulb using an ESP32, or an iPhone pretending to be a peripheral. When you have hundreds or thousands of devices, abstraction isn’t just convenient, it’s survival.</p>
<p>Next comes <strong>connection management</strong>. BLE connections are like toddlers: they demand constant attention and can vanish the moment you look away. A scalable Bluetooth system can’t afford to panic every time a device disconnects. Instead, you design it to expect chaos. You add automatic retries, reconnection strategies, and timeouts that gracefully handle failures instead of freezing your app. Good systems don’t assume the network will always behave, they assume it won’t.</p>
<p>Then there’s <strong>data orchestration</strong>, deciding who talks first, how much data gets sent, and how you keep multiple connections from tripping over each other. Imagine you’re a conductor in an orchestra where half the instruments fall asleep randomly to save power. You need a plan that lets each device play its part in harmony without draining its battery. That’s what managing Bluetooth data flow feels like.</p>
<p>And finally, there’s <strong>power strategy</strong>. Embedded devices live on tight energy budgets. Every scan, advertisement, and data exchange eats into their lifespan. So, your architecture must schedule communication intelligently, let devices wake up briefly, share data, and return to sleep before they burn out. The best Bluetooth systems look lazy on the surface but are actually brilliant planners underneath.</p>
<p>When you put all of this together, abstraction, connection management, orchestration, and power control, you get something that <em>scales</em>. It doesn’t matter if you’re managing three wearables or three thousand sensors. The system behaves predictably, logs issues instead of panicking, and recovers from disconnections automatically.</p>
<p>Think of it like a well-run airport. Planes (your devices) take off and land constantly. The control tower (your app’s Bluetooth manager) keeps track of who’s in the air, who’s landing next, and who needs maintenance. No single pilot needs to know everything, they just follow the protocol.</p>
<p>Scaling Bluetooth isn’t about being clever with one device. It’s about designing systems that keep working even when dozens of devices act unpredictably. You don’t tame Bluetooth by force; you do it by creating a world where even chaos feels organized.</p>
<p>In the next section, we’ll dig deeper into how these connections actually behave in real time, how devices discover each other, exchange data, and, sometimes, break up without warning.</p>
<h2 id="heading-connection-discovery-and-data-flow-the-bluetooth-dating-game">Connection, Discovery, and Data Flow — The Bluetooth Dating Game</h2>
<p>Every Bluetooth connection starts like a modern love story. One device sends out signals into the air, announcing that it’s available. Another device scans the surroundings, hoping to find something compatible. When they finally spot each other, they exchange a few polite packets, decide they’re a good match, and try to make it official with a connection. It’s wireless romance, until one of them walks away without saying goodbye.</p>
<p>This is the heart of how Bluetooth works: <strong>advertising, discovery, and connection</strong>. An embedded sensor or wearable device usually plays the role of the advertiser. It broadcasts tiny packets called advertisements that contain just enough information to say, “Hey, I’m here, and I can measure temperature or heart rate or unlock your door.” These packets are intentionally small because transmitting data takes energy, and low-power devices have to conserve every drop of battery life.</p>
<p>Meanwhile, your phone or tablet acts as the scanner, it listens to the radio waves around it, searching for those signals. When it finds one that matches what it’s looking for, it sends a request to connect. If the peripheral accepts, they move into a new relationship phase: the <strong>GATT connection</strong>. GATT stands for Generic Attribute Profile, which is basically the language they use to talk. Once connected, your phone can ask the device for specific data, like reading a heart rate measurement or writing a configuration setting.</p>
<p>Now, if all of this sounds peaceful and predictable, that’s because we haven’t talked about what happens in the real world. In reality, devices move around, signals weaken, and phones go into power-saving modes that forget they were even connected. Connections drop. Pairing sometimes fails. And when you have ten or more devices talking at once, managing all those tiny wireless conversations becomes a circus act.</p>
<p>Scaling Bluetooth is all about keeping this circus under control. You can’t force every device to stay connected forever, that would drain batteries and jam the radio channels. Instead, you design a rhythm. Devices connect only when needed, exchange data quickly, and then disconnect to rest. This constant dance of connecting and disconnecting keeps the system efficient and stable.</p>
<p>Think of it like a well-run coffee shop. Customers (phones) walk in, place their order (data request), get their coffee (response), and leave. The barista (the embedded device) doesn’t serve one person all day, it serves everyone in quick cycles. The trick is to make sure no one gets stuck waiting for their latte forever.</p>
<p>Timing is everything in this dance. If a device advertises too infrequently, the phone might not discover it in time. If it advertises too often, it wastes power. If the phone sends too many requests at once, the device might crash or slow down. Bluetooth connections live in this delicate balance between performance and efficiency.</p>
<p>When you scale, you also have to think about coordination. Imagine one phone trying to talk to ten sensors at once. You can’t have it flood them all with requests simultaneously, it needs a queue, a polite way of saying “you first, then me.” This is called <strong>connection orchestration</strong>, and it’s one of the hardest parts of scaling BLE systems.</p>
<p>And then there’s the breakup. Devices disconnect all the time, sometimes intentionally, sometimes accidentally. The best Bluetooth systems treat disconnections not as failures but as normal events. The app automatically retries, reconnects, and syncs data without asking the user to “try again.” To users, it feels seamless. Underneath, there’s a lot of quiet heroism happening, background threads, timers, and reconnection logic all working together to patch up relationships on the fly.</p>
<p>So, at its core, Bluetooth is less like a stable marriage and more like speed dating with excellent scheduling. Everyone meets briefly, exchanges information, and moves on. When done right, this model scales effortlessly. When done wrong, it’s chaos.</p>
<p>In the next section, we’ll explore the quirks that make Android, iOS, and embedded devices behave differently in this dating game, and how to keep the peace when one of them inevitably ghosts the others.</p>
<h2 id="heading-platform-quirks-and-how-to-stay-sane">Platform Quirks — And How to Stay Sane</h2>
<p>Once you start scaling Bluetooth, you’ll notice something odd. The same code that works perfectly on one device suddenly refuses to behave on another. It’s like watching identical twins argue about who gets the last slice of pizza, they may look the same, but their personalities couldn’t be more different.</p>
<p>Let’s start with Android, the unpredictable one. Android gives developers more power than any other mobile platform. You can scan however you like, filter by services, read and write any characteristic, and even customize connection intervals. But that power comes at a price. Every phone manufacturer modifies the Bluetooth stack slightly. Samsung, Pixel, OnePlus, Xiaomi, each adds its own flavor of “enhancement,” which sometimes translates to “surprise, nothing works the same.”</p>
<p>One Android phone might handle ten connections at once without blinking. Another might drop all of them the moment the screen turns off. Some versions ignore Bluetooth permissions until you grant location access. Others claim they’re scanning when they actually stopped five minutes ago. Android developers eventually stop asking <em>why</em> and simply build more logging instead. The rule of thumb with Android Bluetooth is simple: test everything, assume nothing, and expect the unexpected.</p>
<p>Then there’s iOS, which at first feels like a breath of fresh air. Apple’s Core Bluetooth framework is clean, consistent, and almost elegant. You get predictable callbacks, smooth reconnections, and well-behaved devices. But if you step outside Apple’s boundaries, you’ll quickly find invisible fences. iOS doesn’t let apps scan in the background freely. It limits how often you can advertise. And if your app tries to keep too many simultaneous connections alive, iOS politely steps in and shuts them down.</p>
<p>Apple’s philosophy is control. It wants Bluetooth connections to behave in ways that don’t drain the battery or clutter the radio. That’s great for users, but for developers it can feel like being handed the keys to a Ferrari and told you can only drive in the parking lot. It works beautifully, as long as you color inside the lines.</p>
<p>And then we have embedded devices, which are in a category of their own. These are the little chips sitting inside your wearables, sensors, or IoT gadgets. They don’t have operating systems or background processes. They just run tiny loops of firmware that listen, respond, and sleep. Their quirks are more about physics than software. If the antenna isn’t tuned properly, signals drop. If the power supply fluctuates, the radio turns off. Sometimes they disconnect simply because a human walked between two devices and absorbed the signal.</p>
<p>Embedded Bluetooth stacks also differ by manufacturer. Nordic, Espressif, Silicon Labs, Texas Instruments, each has its own libraries, quirks, and limitations. Even small changes like increasing the packet size or adjusting the advertising interval can make or break communication. It’s a careful dance between efficiency and reliability.</p>
<p>Now imagine you’re trying to get all three of these worlds to cooperate. Android wants freedom, iOS enforces discipline, and embedded devices want long naps. Building a Bluetooth system that works across all of them is like running a daycare with overachievers, rule-followers, and kids who fall asleep mid-activity. You can’t treat them all the same, but you can design a routine that keeps everyone content.</p>
<p>The secret is resilience. Instead of expecting perfect behavior, build your system around imperfections. Add retries when connections fail. Cache data so you don’t lose progress during disconnections. Keep your embedded devices simple, your mobile apps forgiving, and your logs brutally honest.</p>
<p>If you design with these quirks in mind, your Bluetooth system will feel almost magical, even though, behind the scenes, it’s a web of error handling, reconnections, and polite compromise.</p>
<p>In the next section, we’ll take a look at another side of scaling: keeping everything secure and private while all these devices whisper secrets over the air.</p>
<h2 id="heading-security-and-privacy-at-scale">Security and Privacy at Scale</h2>
<p>Once your Bluetooth system starts working reliably, there’s another challenge waiting in the wings: keeping it <strong>secure</strong>. It’s one thing to get devices talking to each other, it’s another to make sure no one else is eavesdropping on the conversation. Bluetooth security can sound intimidating, but at its core, it’s about making sure your devices trust each other and that strangers can’t sneak into the chat.</p>
<p>Let’s start with pairing. Pairing is Bluetooth’s version of saying, “Hey, can I trust you?” It’s a handshake where two devices exchange keys that let them communicate securely in the future. There are a few ways this handshake can happen. The simplest is called <em>Just Works</em>, which basically means, “We’ll trust each other without asking too many questions.” It’s convenient but about as safe as leaving your front door unlocked because you live in a nice neighborhood. For harmless gadgets like wireless speakers, that’s fine. But for medical devices or smart locks, “Just Works” can turn into “Just Got Hacked.”</p>
<p>A safer approach is <strong>Passkey Entry</strong>, where one device shows a code and the other types it in, proving they’re physically near each other. Even better is <strong>Out-of-Band (OOB)</strong> pairing, where the devices exchange security information through another method, maybe a QR code, NFC tap, or even an optical blink, before connecting over Bluetooth. OOB pairing is like verifying someone’s identity face-to-face before continuing a conversation online.</p>
<p>Once paired, devices use <strong>encryption</strong> to scramble their communication. Anyone listening nearby will hear only gibberish. The strength of that encryption depends on the version of Bluetooth being used. Modern devices using Bluetooth 4.2 or later support something called <em>LE Secure Connections</em>, which is based on advanced cryptography. Older devices use weaker methods that are easier to crack. So, if you’re building something new, never rely on outdated pairing modes.</p>
<p>But security isn’t just about encryption. It’s also about <strong>privacy</strong>. Every Bluetooth device has an address, kind of like its phone number, that it uses when broadcasting. If that address stays the same, someone could track you by following your device’s broadcasts. That’s why newer standards support <em>random address rotation</em>, where devices periodically change their Bluetooth address. Your phone and smartwatch still recognize each other, but strangers can’t follow your signal around the city.</p>
<p>When you scale Bluetooth systems, these little details become critical. A single insecure device in your network can become the weak link that compromises everything. It’s like locking every door in your house but leaving one window open. Attackers don’t need to break the whole system, they just need to find the lazy one.</p>
<p>Building security into a large Bluetooth deployment means standardizing your pairing process, using strong encryption everywhere, and handling key storage carefully. On embedded devices, that can be tricky because they have limited memory and no secure element by default. Still, even small steps help, like regenerating keys periodically and disabling “Just Works” mode for devices that control anything important.</p>
<p>On mobile platforms, the rules are slightly different. Android and iOS handle much of the heavy lifting for you, but you still have to design your app logic carefully. Always confirm which device you’re connecting to before exchanging sensitive data. Always check bonding state before sending configuration commands. In short, treat Bluetooth communication with the same seriousness you’d give to a login session or an online payment.</p>
<p>At scale, security isn’t something you bolt on later. It’s part of the system’s DNA. You can’t fix a weak handshake by adding a stronger password later. You have to start from the first pairing and make sure every connection trusts the right partner.</p>
<p>The reward is worth it. When done right, your Bluetooth network becomes invisible but secure, a quiet, encrypted web of trust that just works. No drama, no leaks, and no nearby strangers hijacking your sensors.</p>
<p>In the next section, we’ll talk about another invisible problem that decides whether your Bluetooth network lives for days or months: power. Because what good is a secure device if its battery dies halfway through the handshake?</p>
<h2 id="heading-power-and-performance-tuning">Power and Performance Tuning</h2>
<p>If you’ve ever wondered why your Bluetooth gadget dies right when you need it most, you’ve just met the oldest enemy in wireless communication: power consumption. Bluetooth may be clever, flexible, and everywhere, but it also has a bit of a caffeine problem. It loves to talk, and talking burns energy. Keeping your devices alive longer, especially when you scale, means learning the quiet art of power management.</p>
<p>At first, it’s easy to assume that Bluetooth is low power by default. After all, it’s called <strong>Bluetooth Low Energy</strong>, right? But BLE’s efficiency only shines when it’s used correctly. A poorly tuned BLE system can drain a battery faster than streaming music over Classic Bluetooth. The magic lies in controlling when devices talk, how long they talk, and how much they say each time.</p>
<p>Let’s start with the <strong>advertising interval</strong>. This is how often a device shouts, “I’m here!” into the air. If you set it to broadcast every 20 milliseconds, you’ll discover devices quickly, but you’ll also burn through the battery like it’s running a marathon. Increase the interval to once every second, and your device will last much longer, but phones may take a moment to find it. It’s a tradeoff between speed and stamina. Every system has to find its sweet spot.</p>
<p>Next comes the <strong>connection interval</strong>, how often two connected devices exchange data. This is like deciding how frequently you check your messages. If you check every second, you stay perfectly up to date but never get anything else done. If you check once every minute, you save time but risk missing something important. In Bluetooth terms, a shorter connection interval means faster communication but higher power usage. Longer intervals conserve battery but add delay. Smart systems adjust these intervals dynamically depending on what the device is doing.</p>
<p>Then there’s the <strong>MTU</strong>, or Maximum Transmission Unit, the size of each Bluetooth data packet. Bigger packets mean fewer total transmissions for large chunks of data, which can improve efficiency. But some devices, especially older ones, can’t handle large MTUs, so finding the right balance is important.</p>
<p>Power management is not just about numbers, it’s about habits. A well-designed embedded device spends most of its life asleep. It wakes up only to advertise or exchange data, then returns to rest as quickly as possible. Imagine a hummingbird darting out for a sip of nectar and then zipping back to rest before anyone notices. That’s how efficient Bluetooth devices survive on coin-cell batteries for months or even years.</p>
<p>On the phone side, energy management is just as critical, especially when your app needs to handle multiple connections. Constant scanning, reconnecting, or keeping GATT channels open drains your user’s battery, and patience. Android and iOS both have built-in mechanisms that throttle background Bluetooth activity to save power. Developers have to work with these rules, not against them. The best apps schedule scans intelligently, reconnect only when necessary, and avoid holding connections open when no data needs to be sent.</p>
<p>Scaling Bluetooth systems makes these power decisions even more important. When you have one device, wasting a bit of energy doesn’t matter. When you have hundreds of devices, each one burning just a few extra milliwatts, the total waste adds up quickly. Power efficiency becomes the difference between a network that runs for months and one that collapses after a week.</p>
<p>The golden rule of power tuning is simple: talk less, talk smarter. A Bluetooth device that knows when to speak and when to stay quiet can scale beautifully, even in large networks. It’s not about being fast all the time, it’s about being clever with timing.</p>
<p>In the next section, we’ll look at how these devices join your network in the first place and what happens when you need to update their software later. Because once your system scales, you’re not just connecting devices, you’re managing an entire population.</p>
<h2 id="heading-provisioning-and-firmware-updates-welcome-to-device-kindergarten">Provisioning and Firmware Updates — Welcome to Device Kindergarten</h2>
<p>Imagine setting up one Bluetooth device. It’s easy: you pair it, give it a name, and maybe tweak a few settings. Now imagine doing that a hundred times. Or a thousand. Suddenly, what felt like a simple task starts to look like a factory assembly line powered by frustration. That’s where <strong>provisioning</strong> comes in, the process of onboarding new devices into your Bluetooth network so they can start working right away, without manual babysitting.</p>
<p>Provisioning is like a first day at school for your devices. Each new student needs to be identified, assigned to a class, and given a name tag. In the Bluetooth world, a newly manufactured device begins life in an “unprovisioned” state. It doesn’t belong to any network yet, so it advertises with a special signal that says, “Hey, I’m new here.” When your mobile app or gateway spots that advertisement, it can connect, authenticate the device, and hand over the credentials it needs to join the system.</p>
<p>The app usually performs a few key steps during provisioning. It verifies that the device is genuine, assigns it a unique identifier, and exchanges security keys so future connections can happen securely. It might also store metadata like which room the sensor belongs to or what type of data it will report. After provisioning, the device switches to its normal operation mode, where it advertises with its new identity and starts behaving like a member of the family.</p>
<p>When you have just one or two devices, you can do all this manually. But when you scale up to hundreds or thousands, manual setup becomes impossible. That’s when you start thinking about automation, QR codes on packaging, NFC tags for instant pairing, or out-of-band provisioning where a separate channel (like Wi-Fi or a wired link) handles secure onboarding. The goal is to make provisioning quick, repeatable, and error-free, even when your factory or users are adding new devices by the dozens.</p>
<p>Once your devices are out in the world, the next challenge appears: <strong>firmware updates</strong>. Every system eventually needs to fix bugs, patch security holes, or add new features. For Bluetooth devices, this means pushing new firmware over the same wireless link, a process known as <strong>FOTA</strong>, or firmware-over-the-air updates.</p>
<p>Updating firmware over Bluetooth can be nerve-wracking. The connection is relatively slow, and interruptions can leave a device half-updated and confused about who it is. Good update systems handle this carefully. They divide the firmware into chunks, verify each piece with checksums, and only switch to the new version once the whole update has been safely received and validated. If anything fails midway, the device rolls back to the old firmware instead of bricking itself.</p>
<p>Scaling makes this even more complex. Updating ten devices is fine. Updating a thousand can overwhelm your network if you try to do them all at once. Smart systems stagger the updates in waves, track which devices have finished, and retry the ones that didn’t. Some even let devices report their status back to a central dashboard, so you can see which ones are ready and which ones are still stuck halfway through.</p>
<p>Provisioning and firmware updates might not sound glamorous, but they’re the backbone of every scalable Bluetooth system. Without smooth onboarding and reliable updates, your network slowly falls apart as devices drift out of sync or miss critical fixes.</p>
<p>Think of it this way: provisioning is how devices <em>join the family</em>, and firmware updates are how they <em>grow up</em>. Both are essential if you want your Bluetooth ecosystem to stay healthy and dependable over time.</p>
<p>In the next section, we’ll talk about what happens when something inevitably goes wrong, how to debug and monitor a network full of devices without losing your mind.</p>
<h2 id="heading-debugging-monitoring-and-testing-across-platforms">Debugging, Monitoring, and Testing Across Platforms</h2>
<p>At some point, every Bluetooth developer faces the same moment of quiet despair. The logs look fine, the devices are paired, the code hasn’t changed, and yet… nothing works. Connections fail, packets vanish, and everything that worked yesterday now refuses to cooperate. Welcome to the wonderful, mysterious world of Bluetooth debugging, a place where logic takes a vacation and patience becomes your most valuable skill.</p>
<p>Debugging Bluetooth is tricky because so much of it happens invisibly. The data is flying through the air, hopping between frequencies dozens of times per second, and all you can see is whether the connection succeeds or fails. It’s like trying to diagnose a conversation between two people whispering in another room. You can tell they’re talking, but not what they’re saying.</p>
<p>The first rule of Bluetooth debugging is simple: <strong>log everything</strong>. Log when you start scanning, when you find a device, when you connect, and when you disconnect. Log the signal strength, the UUIDs you discover, the number of bytes you read, and the time it took. Bluetooth problems rarely announce themselves loudly, they hide in tiny details. A small delay in a callback or a missing acknowledgment can reveal exactly why your system seems haunted.</p>
<p>Different platforms give you different kinds of help. Android, for example, offers detailed Bluetooth logs through developer options or tools like <code>adb</code>. You can capture the raw Bluetooth HCI logs and analyze them later to see what really happened under the hood. iOS, on the other hand, gives you less direct visibility. Apple handles most of the Bluetooth stack internally, so your only clues come from Core Bluetooth callbacks. Embedded devices often let you log directly from the firmware, showing connection events, error codes, and sometimes even packet-level information if the stack supports it.</p>
<p>Testing across platforms is just as important as debugging. You can’t assume that if it works on one phone, it will work on another. Android devices, especially, have a habit of interpreting Bluetooth timing slightly differently. A system that’s rock-solid on a Pixel may stutter on a Samsung or freeze on a low-cost tablet. The only cure is diversity, test on multiple brands, OS versions, and firmware builds until you’re confident the system behaves everywhere.</p>
<p>For embedded devices, testing is a different challenge. Because they often run continuously, you need long-term endurance tests to catch issues that only appear after hours or days of operation. You might discover that a connection fails only after 300 reconnections, or that a memory leak appears after a week of normal use. Building test rigs that automate these scenarios: connecting, disconnecting, and verifying data repeatedly, is a huge time saver.</p>
<p>Monitoring is what happens after you’ve deployed your devices into the real world. It’s like keeping a health tracker on your entire Bluetooth network. Your mobile apps or gateways can collect statistics such as signal strength, connection failures, uptime, and battery levels. That data tells you which devices are performing well and which ones might be drifting toward trouble.</p>
<p>Adding this kind of visibility pays off enormously at scale. When you’re managing hundreds of devices, it’s impossible to check each one manually. Instead, you rely on trends, for example, if one location shows consistently weak signal strength, maybe there’s interference nearby. If multiple devices drop connections at the same time, maybe the central device needs a firmware update. Monitoring transforms guesswork into insight.</p>
<p>The truth is, debugging and monitoring never really end. Even after your system is stable, new versions of Android and iOS will appear with small Bluetooth changes that break something you didn’t know could break. Treat Bluetooth maintenance like car maintenance: routine, ongoing, and essential.</p>
<p>Once you learn to capture good logs, read them calmly, and build systems that report their own health, debugging stops being a nightmare and becomes a science. Bluetooth may always be a little mysterious, but with the right tools and attitude, you can keep the ghosts out of your connection list.</p>
<p>In the next section, we’ll put everything together with a real-world example of what scaling Bluetooth actually looks like when all the pieces: mobile apps, embedded devices, and architecture, finally work in harmony.</p>
<h2 id="heading-real-world-architecture-example-when-bluetooth-finally-behaves">Real-World Architecture Example — When Bluetooth Finally Behaves</h2>
<p>Let’s take everything we’ve talked about and bring it to life with a real-world scenario. Imagine you’re building a smart factory system with hundreds of Bluetooth sensors scattered across the floor. Each sensor measures temperature, vibration, or humidity. Some are attached to machines, others hang on walls, and a few are hidden in places even the janitor doesn’t know about. Your goal is simple on paper: collect data from all these sensors, send it to a central dashboard, and keep everything running smoothly.</p>
<p>The reality, of course, is much more complicated. Each sensor is an embedded device powered by a coin-cell battery that has to last for months. They advertise periodically to announce they’re alive. Your Android or iOS tablets, placed around the factory as gateways, act as Bluetooth centrals. Their job is to scan, connect to nearby sensors, read data, and upload it to the cloud. It sounds straightforward, but you’re juggling dozens of invisible connections at once, and they all have different moods.</p>
<p>The architecture begins with careful planning. Each gateway tablet knows which part of the factory it’s responsible for. That way, you avoid overcrowding the airwaves with multiple devices trying to connect to the same sensors. The sensors use slightly staggered advertising intervals so they don’t all shout at the same time. The gateways maintain a queue, connecting to a few sensors at a time, reading data, and then disconnecting before moving on to the next group. This rotation keeps everything balanced and prevents Bluetooth traffic jams.</p>
<p>Power management is built into every step. Each sensor wakes up, advertises briefly, sends its data when connected, and goes right back to sleep. The connection interval and MTU size are tuned for efficiency, large enough for smooth data transfer, but not so large that slower devices choke. Every byte is treated like gold because every transmission costs energy.</p>
<p>The gateways handle the messy parts: reconnections, retries, and data aggregation. They buffer readings in case the Wi-Fi link to the cloud goes down and sync later when it’s back. They also monitor each sensor’s signal strength, battery level, and uptime. If a sensor hasn’t reported in a while, the system flags it automatically so a technician can check on it.</p>
<p>Now imagine scaling this setup to multiple factory buildings. Suddenly, you’re managing thousands of sensors, dozens of gateways, and countless wireless interactions. At this scale, the design choices you made early, abstracted Bluetooth logic, retry mechanisms, power optimization, and logging, are the difference between a quiet, self-running network and a system that collapses into constant reconnections.</p>
<p>When everything works as intended, something beautiful happens. The sensors collect data silently. The gateways synchronize automatically. The dashboards stay green. Nobody has to restart anything, and Bluetooth quietly fades into the background where it belongs. It’s the rare moment when technology stops demanding attention and simply does its job.</p>
<p>This kind of architecture isn’t science fiction. Companies use it in factories, hospitals, and warehouses every day. From smart lighting systems to patient monitors, Bluetooth at scale can be astonishingly reliable, but only if you treat it like a distributed system, not a single gadget. Each device is a citizen of a larger ecosystem, and your job as the architect is to keep that ecosystem healthy.</p>
<p>The biggest takeaway is that success doesn’t come from fancy algorithms or expensive hardware. It comes from the small, deliberate decisions that make your system resilient: how you handle disconnections, how you schedule connections, how you monitor performance. Scaling Bluetooth is not about avoiding problems, it’s about designing a system that recovers gracefully when problems happen.</p>
<p>In the next section, we’ll wrap up everything we’ve learned into a practical checklist, a simple guide you can use whenever you’re designing a Bluetooth system that has to survive in the wild.</p>
<h2 id="heading-checklist-building-a-truly-scalable-bluetooth-system">Checklist — Building a Truly Scalable Bluetooth System</h2>
<p>By now, you’ve seen Bluetooth in all its moods, charming, confusing, unpredictable, and surprisingly capable when handled with care. So how do you actually put everything together? What makes a Bluetooth system <em>scalable</em> instead of just “working on my desk”? The answer isn’t a single trick or secret API. It’s a mindset, a way of designing your system to expect chaos and still function gracefully when it happens.</p>
<p>The first part of that mindset is consistency. Every Bluetooth system should have one clear and stable way of communicating. Keep your data formats simple, your GATT profiles predictable, and your naming conventions sensible. If you have ten devices made by ten different vendors, make them all speak the same language. The moment one device starts improvising, the whole orchestra sounds off.</p>
<p>Next comes patience, and in Bluetooth, patience means retries. Connections drop. Devices go out of range. A phone might go to sleep or decide that scanning is no longer fashionable. Instead of treating every disconnection as a crisis, treat it as part of the process. A good Bluetooth app quietly retries in the background, restores the connection, and carries on as if nothing happened. To the user, it feels seamless. Underneath, it’s a flurry of logic keeping the experience smooth.</p>
<p>Then there’s the question of power. Remember that every advertisement and connection eats into battery life. A scalable Bluetooth system doesn’t talk all the time, it talks <em>smart</em>. It plans when to wake up, when to exchange data, and when to stay silent. Devices that last longer need fewer replacements, fewer updates, and far less human attention. Power efficiency is the hidden currency of scalability.</p>
<p>Monitoring is another essential habit. If you can’t see what’s happening inside your system, you’re flying blind. Log your connections, track your signal strengths, record how often devices drop out, and visualize it somewhere. A simple dashboard that shows which devices are healthy and which ones are struggling can save you countless hours later. When you scale, visibility turns guesswork into control.</p>
<p>Security, too, can’t be an afterthought. Use secure pairing, proper encryption, and rotating addresses. The bigger your system gets, the more interesting it becomes to people who might want to peek at it. Make sure they can’t. A secure Bluetooth network doesn’t just protect users, it protects your reputation.</p>
<p>Finally, build for change. Bluetooth isn’t static, Android and iOS update their stacks every year, chip vendors release new firmware, and new security standards appear. A scalable system doesn’t break when something changes, it adapts. That’s why abstraction layers, modular code, and updatable firmware matter so much. They keep your system flexible long after the first version ships.</p>
<p>If you do all of this, keep it consistent, patient, efficient, observable, secure, and adaptable, something magical happens. Your Bluetooth system starts to feel less like a fragile web of devices and more like a living network. It keeps running, keeps healing, and quietly gets the job done without constant supervision. That’s when you know you’ve built something that scales.</p>
<p>In the final section, we’ll step back and reflect on the bigger picture, what scaling Bluetooth really teaches us about building technology that has to work not just once, but over and over again in the messy, beautiful real world.</p>
<h2 id="heading-wrap-up-lessons-from-the-field">Wrap-Up — Lessons from the Field</h2>
<p>If you’ve made it this far, you’ve probably realized that scaling Bluetooth isn’t really about Bluetooth at all. It’s about learning how complex systems behave when they leave the comfort of your desk and enter the real world. It’s about understanding that wireless connections are not just electrical signals, they’re relationships between unpredictable, battery-powered, opinionated little machines.</p>
<p>Bluetooth gets a bad reputation because people expect it to be simple. They imagine it’s like Wi-Fi or USB, plug and play, pair and forget. But in truth, Bluetooth is more like a polite conversation at a crowded party. Everyone is talking at the same time, the music is loud, and you have to keep repeating yourself until the other person hears you correctly. When you think of it that way, it’s a miracle that it works as well as it does.</p>
<p>Scaling Bluetooth across Android, iOS, and embedded devices teaches you humility. You stop assuming things will always behave, and instead you start building systems that <em>recover</em> when they don’t. You learn that error handling is not an afterthought, it’s the main event. You discover that batteries are precious, timing is everything, and the smallest design decisions can ripple through an entire ecosystem of devices.</p>
<p>You also start to appreciate the quiet beauty of resilience. There’s something deeply satisfying about watching dozens of sensors, gateways, and phones connect, share data, and disconnect, all without human intervention. When it works, it feels effortless. You forget about the retries, the power cycles, the reconnections, and the debugging sessions that made it possible. All you see is a smooth network humming quietly in the background, doing exactly what it was meant to do.</p>
<p>And that’s the real magic of Bluetooth, not the flashy tech demos or the pairing animations, but the invisible collaboration that happens beneath the surface. It’s the heartbeat of every wearable, every sensor, every tiny device that quietly makes our lives a little easier. Scaling it isn’t just an engineering challenge; it’s a lesson in patience, design, and empathy for systems that can’t always speak for themselves.</p>
<p>So, the next time your Bluetooth device disconnects, take a breath. Somewhere in the chaos, it’s just trying to reconnect, to find its partner again and pick up where it left off. Because deep down, that’s what Bluetooth really is: a network built on trust, persistence, and tiny packets of hope flying through the air.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Embedded Swift: A Modern Approach to Low-Level Programming ]]>
                </title>
                <description>
                    <![CDATA[ Embedded programming has long been dominated by C and C++, powering everything from microcontrollers to real-time systems. While these languages offer unmatched low-level control, they also introduce persistent challenges, manual memory management, u... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/embedded-swift-a-modern-approach-to-low-level-programming/</link>
                <guid isPermaLink="false">688d5fc7d30be1cecdacf767</guid>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Firmware Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Swift ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C++ ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ memory-management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ programming languages ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Soham Banerjee ]]>
                </dc:creator>
                <pubDate>Sat, 02 Aug 2025 00:45:59 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754090186842/80a42dca-f2c4-49de-b704-2e90134c6397.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Embedded programming has long been dominated by C and C++, powering everything from microcontrollers to real-time systems. While these languages offer unmatched low-level control, they also introduce persistent challenges, manual memory management, unsafe pointer operations, and subtle logic bugs stemming from weak type systems and undefined behavior.</p>
<p>With the release of Swift 6 and its new Embedded Swift compilation mode, developers now have access to a modern, memory-safe, and performant alternative that’s tailored specifically for resource-constrained systems.</p>
<p>While languages like Rust have also emerged to address these issues, Embedded Swift brings the clarity and safety of Swift to microcontroller environments, without giving up on determinism, binary size, or hardware access.</p>
<p>This article introduces Embedded Swift and explores how it compares to traditional C/C++ development. We’ll cover its key features, programming and memory models, how to set up the toolchain for STM32 microcontrollers, and how to link Swift with existing C drivers.</p>
<p>Along the way, we’ll examine performance trade-offs, growing ecosystem support, and the broader industry movement toward memory-safe languages. As I hope you’ll see, Swift is a serious contender in the future of embedded development.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To get the most out of this article, you should have a basic understanding of programming in Swift and C. Familiarity with embedded hardware platforms and firmware development concepts will also be helpful.</p>
<p>If you're new to embedded systems, consider reviewing this <a target="_blank" href="https://www.freecodecamp.org/news/learn-embedded-systems-firmware-basics-handbook-for-devs/">introductory guide to embedded firmware</a> to build foundational knowledge before diving into Embedded Swift.</p>
<h2 id="heading-scope">Scope</h2>
<p>This article is intended as a practical introduction to Embedded Swift. It covers:</p>
<ul>
<li><p>An overview of Embedded Swift and its key language features</p>
</li>
<li><p>Swift’s programming and memory model in an embedded context</p>
</li>
<li><p>Setting up the Embedded Swift toolchain on macOS for STM32 microcontrollers</p>
</li>
<li><p>Interoperability with C code and linking to existing low-level drivers</p>
</li>
<li><p>A look at memory and instruction-level performance</p>
</li>
<li><p>Future directions and use cases for Embedded Swift</p>
</li>
</ul>
<p>Note that this article does not provide a full tutorial on the Swift language itself. While the primary focus is on STM32, similar principles apply to other supported platforms such as ESP32, Raspberry Pi Pico, and nRF52.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-swift-what-is-embedded-swift">What is Swift? What is Embedded Swift?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-swift-programming-model">Swift Programming Model</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-swift-memory-management">Swift Memory Management</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-memory-and-instruction-cycle-comparison">Memory and Instruction Cycle Comparison</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-setup-embedded-swift">How to Setup Embedded Swift</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-c-swift-linkages">C-Swift Linkages:</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-future-work">Future Work</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-swift-what-is-embedded-swift">What is Swift? What is Embedded Swift?</h2>
<p>Swift is a modern programming language developed by Apple that combines the performance of compiled languages with the expressiveness and safety of modern language design. While Swift was originally created for iOS and macOS development, it has evolved into a powerful general-purpose language used in server-side development, systems programming, and increasingly, embedded systems.</p>
<p>Embedded Swift is a special compilation mode introduced in Swift 6 that brings the benefits of Swift to resource-constrained platforms like microcontrollers. It lets developers use a safe, high-level language while still producing compact, deterministic, and performant binaries suitable for embedded applications.</p>
<h3 id="heading-key-features-of-swift">Key Features of Swift</h3>
<p>Embedded Swift retains many of the powerful language features that make Swift an attractive alternative to C/C++ in embedded development:</p>
<p><strong>Type Safety</strong>: Swift uses a strong static type system, which prevents many programming errors at compile time. Unlike C, where type mismatches can result in undefined behavior, Swift ensures all types are used correctly before code even runs.</p>
<p><strong>Strict Type Checking</strong>: Swift doesn't allow implicit type conversions that could lose data or cause unexpected behavior. For example:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// This won't compile in Swift</span>
<span class="hljs-keyword">let</span> integer: <span class="hljs-type">Int</span> = <span class="hljs-number">42</span>
<span class="hljs-keyword">let</span> decimal: <span class="hljs-type">Double</span> = <span class="hljs-number">3.14</span>
<span class="hljs-keyword">let</span> result = integer + decimal  <span class="hljs-comment">// Error: Cannot convert value of type 'Int' to expected argument type 'Double'</span>

<span class="hljs-comment">// You must be explicit about conversions</span>
<span class="hljs-keyword">let</span> result = <span class="hljs-type">Double</span>(integer) + decimal  <span class="hljs-comment">// Correct</span>
</code></pre>
<p><strong>Non-nullable Types by Default</strong>: In C, pointers can be null by default, which introduces risk. In Swift, variables cannot be nil unless explicitly marked as optionals:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">var</span> name: <span class="hljs-type">String</span> = <span class="hljs-string">"John"</span>
name = <span class="hljs-literal">nil</span>  <span class="hljs-comment">// Compile error - String cannot be nil</span>

<span class="hljs-keyword">var</span> optionalName: <span class="hljs-type">String?</span> = <span class="hljs-string">"John"</span>
optionalName = <span class="hljs-literal">nil</span>  <span class="hljs-comment">// This is allowed</span>
</code></pre>
<h4 id="heading-memory-safety-via-arc-covered-in-detail-later">Memory Safety via ARC (Covered in detail later):</h4>
<p>Swift manages memory automatically using Automatic Reference Counting (ARC). Unlike manual memory management in C/C++, ARC handles object lifecycles efficiently without unpredictable garbage collection pauses. We'll cover ARC and its impact in embedded contexts in a dedicated section later.</p>
<p><strong>Modern Syntax</strong>:<br>Swift's syntax is clean, consistent, and designed for readability. It supports modern paradigms including:</p>
<ul>
<li><p>Functional programming (map, filter, reduce)</p>
</li>
<li><p>Generics (type-safe abstractions)</p>
</li>
<li><p>Protocol-Oriented Programming (discussed in the next section)</p>
</li>
</ul>
<p>These features allow you to write more expressive and maintainable code compared to procedural C or inheritance-heavy C++.</p>
<p><strong>Performance</strong>:<br>Swift is designed to perform on par with C++ in many scenarios. Optimizations such as inlining, dead code elimination, and static dispatch help ensure that high-level abstractions don’t compromise performance. In embedded mode, Swift disables features like runtime reflection and dynamic dispatch to further reduce overhead.</p>
<p>To fully leverage Swift for embedded development, it's important to understand its programming model. Unlike C’s procedural approach or C++’s class-heavy design, Swift promotes protocol-oriented programming and composition, which offers both flexibility and safety in embedded system design.</p>
<h2 id="heading-swift-programming-model">Swift Programming Model</h2>
<p>Swift embraces a multi-paradigm programming model that blends object-oriented, functional, and protocol-oriented programming, all underpinned by strong type safety and memory safety.</p>
<p>For embedded developers coming from C or C++, this model may feel different at first. But it provides a more modular and testable way to build complex systems, something especially valuable in embedded applications where hardware abstraction and strict reliability are critical.</p>
<h3 id="heading-protocol-oriented-programming-pop">Protocol-Oriented Programming (POP)</h3>
<p>Swift emphasizes protocols over inheritance, encouraging developers to define behaviors through protocols and implement them using value types like <code>struct</code> and <code>enum</code>, rather than relying heavily on classes.</p>
<p>This philosophy favors composition over inheritance, allowing you to build complex functionality by combining smaller, well-defined components.</p>
<p>Key Concepts<strong>:</strong></p>
<ul>
<li><p><code>protocol</code> defines required behavior.</p>
</li>
<li><p>Protocol extensions provide default behavior.</p>
</li>
<li><p>Prefer value semantics using <code>struct</code>.</p>
</li>
</ul>
<p>Example<strong>:</strong></p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">Speakable</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">speak</span><span class="hljs-params">()</span></span>
}

<span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">Speakable</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">speak</span><span class="hljs-params">()</span></span> {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"Default sound"</span>)
    }
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Dog</span>: <span class="hljs-title">Speakable</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">speak</span><span class="hljs-params">()</span></span> {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"Woof!"</span>)
    }
}
</code></pre>
<p>Embedded Swift uses protocols with static dispatch. With static dispatch, the compiler knows the exact memory address of the function to call and can generate a direct jump instruction. There's no runtime lookup, no indirection, and no uncertainty.</p>
<h4 id="heading-why-pop-matters-for-embedded-systems">Why POP Matters for Embedded Systems</h4>
<p>First, you get flexible hardware extraction. Protocols make it easy to define interfaces for hardware components, allowing for mock implementations during testing or platform-specific variations.</p>
<p>Second, you have nice low overhead. Embedded Swift uses static dispatch for protocols, meaning there’s no runtime lookup, and calls are resolved at compile time for maximum performance.</p>
<p>Also, <code>struct</code> and <code>enum</code> types avoid heap allocations, making code more efficient and predictable in low-memory environments.</p>
<p>Now that we’ve explored how Swift’s programming model enables safer and more modular embedded code, let’s turn to another critical piece of the puzzle: memory management. Swift’s use of Automatic Reference Counting (ARC) replaces manual memory handling and offers important benefits, and tradeoffs, for embedded systems.</p>
<h2 id="heading-swift-memory-management">Swift Memory Management</h2>
<p>One of Swift’s most impactful features, especially in the context of embedded systems, is its use of Automatic Reference Counting (ARC) for memory management. Unlike C/C++, where memory must be manually allocated and freed using <code>malloc</code> and <code>free</code>, Swift automates this process while maintaining deterministic performance.</p>
<p>This automation significantly reduces the risk of common memory-related bugs like leaks, dangling pointers, or use-after-free errors, all of which are notorious in low-level C code.</p>
<h3 id="heading-how-arc-works">How ARC works</h3>
<p>Swift supports ARC not only for the Cocoa Touch API's but for all APIs, providing a streamlined approach to memory management. Unlike garbage collection systems that can cause unpredictable pauses, ARC works deterministically at compile time and runtime to manage memory.</p>
<p>ARC automatically tracks and manages the lifetime of objects in memory based on how many references point to them.</p>
<ul>
<li><p>Reference Counting: Every object has a counter that tracks how many strong references point to it.</p>
</li>
<li><p>Retain / Release: The compiler inserts <code>retain</code> and <code>release</code> calls automatically during assignment and deinitialization.</p>
</li>
<li><p>Immediate Deallocation: When the reference count reaches zero, the object is deallocated immediately.</p>
</li>
<li><p>Deterministic: Unlike garbage collectors, ARC doesn’t introduce unpredictable pauses or runtime scanning.</p>
</li>
</ul>
<p>Swift offers multiple reference types to give you precise control over memory behavior and prevent cycles:</p>
<p><strong>Strong References</strong> (default)</p>
<ul>
<li><p>Keeps the referenced object alive.</p>
</li>
<li><p>Used in most cases.</p>
</li>
</ul>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MotorController</span> </span>{
    <span class="hljs-keyword">var</span> sensor: <span class="hljs-type">SensorData?</span>  <span class="hljs-comment">// Strong reference</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateReading</span><span class="hljs-params">(newData: SensorData)</span></span> {
        <span class="hljs-keyword">self</span>.sensor = newData  <span class="hljs-comment">// Previous sensor data automatically deallocated</span>
    }
}
</code></pre>
<p><strong>Weak References</strong></p>
<ul>
<li><p>Used to break reference cycles (especially in two-way object relationships).</p>
</li>
<li><p>Automatically becomes <code>nil</code> when the referenced object is deallocated.</p>
</li>
</ul>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Device</span> </span>{
    <span class="hljs-keyword">var</span> controller: <span class="hljs-type">MotorController?</span>

    <span class="hljs-keyword">deinit</span> {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"Device deallocated"</span>)
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MotorController</span> </span>{
    <span class="hljs-keyword">weak</span> <span class="hljs-keyword">var</span> device: <span class="hljs-type">Device?</span>  <span class="hljs-comment">// ← Weak reference breaks the cycle</span>

    <span class="hljs-keyword">deinit</span> {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"MotorController deallocated"</span>)
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">breakCycle</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">let</span> device = <span class="hljs-type">Device</span>()
    <span class="hljs-keyword">let</span> controller = <span class="hljs-type">MotorController</span>()

    device.controller = controller
    controller.device = device  <span class="hljs-comment">// ← This is now a weak reference</span>

    <span class="hljs-comment">// When this function ends, both objects are properly deallocated</span>
}

breakCycle()
<span class="hljs-comment">// Output:</span>
<span class="hljs-comment">// Device deallocated</span>
<span class="hljs-comment">// MotorController deallocated</span>
</code></pre>
<p><strong>Unowned References</strong></p>
<ul>
<li><p>Non-optional version of <code>weak</code>.</p>
</li>
<li><p>Assumes the object will never be deallocated while still in use.</p>
</li>
<li><p>More lightweight than <code>weak</code>, but unsafe if misused.</p>
</li>
</ul>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SensorSystem</span> </span>{
    <span class="hljs-keyword">unowned</span> <span class="hljs-keyword">let</span> controller: <span class="hljs-type">MotorController</span>  <span class="hljs-comment">// unowned reference</span>

    <span class="hljs-keyword">init</span>(controller: <span class="hljs-type">MotorController</span>) {
        <span class="hljs-keyword">self</span>.controller = controller
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MotorController</span> </span>{
    <span class="hljs-keyword">var</span> sensorSystem: <span class="hljs-type">SensorSystem?</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setupSensors</span><span class="hljs-params">()</span></span> {
        sensorSystem = <span class="hljs-type">SensorSystem</span>(controller: <span class="hljs-keyword">self</span>)
    }

    <span class="hljs-keyword">deinit</span> {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"MotorController deallocated"</span>)
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">testUnowned</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">let</span> controller = <span class="hljs-type">MotorController</span>()
    controller.setupSensors()
    <span class="hljs-comment">// sensorSystem deallocates before controller ends</span>
}

testUnowned()
<span class="hljs-comment">// Output: MotorController deallocated</span>
</code></pre>
<h3 id="heading-arc-overhead-in-embedded-systems">ARC Overhead in Embedded Systems</h3>
<p>While ARC provides safety benefits, it does introduce some overhead compared to manual memory management:</p>
<h4 id="heading-memory-overhead">Memory Overhead:</h4>
<p>ARC-managed class instances in Swift typically include an additional 4 or 8 bytes to store reference count metadata, depending on the system architecture, 4 bytes on 32-bit systems and 8 bytes on 64-bit systems. This metadata allows the runtime to track how many active references exist to a given object and deallocate it when no references remain. When developers use weak or unowned references, the memory footprint increases further. These references require additional data structures, such as side tables or tracking mechanisms, to manage object liveness and cleanup. In the case of weak references specifically, Swift maintains zeroing weak reference tables that automatically null out pointers once the referenced object is deallocated, ensuring memory safety.</p>
<h4 id="heading-cpu-overhead">CPU Overhead:</h4>
<p>ARC introduces some runtime overhead due to retain and release operations, which are inserted automatically during reference assignments. These operations involve incrementing or decrementing the reference count and are especially common in code that passes objects between functions or stores them in collections. To ensure thread safety, these updates are typically implemented using atomic operations, which add further instruction cycles. In complex object graphs, ARC may also engage in cycle detection and cleanup through the use of weak references to prevent memory leaks caused by strong reference cycles. While Swift's ARC provides deterministic and efficient memory management, it does so with both memory and CPU costs that developers should consider carefully, especially in performance-critical embedded systems.</p>
<h3 id="heading-type-safety-and-error-prevention">Type Safety and Error Prevention</h3>
<p>Swift's type system prevents many common errors that plague C/C++ programs:</p>
<ul>
<li><p><strong>Buffer Overflows</strong>: Swift arrays are bounds-checked, preventing buffer overflow vulnerabilities that are common in C.</p>
</li>
<li><p><strong>Null Pointer Dereferences</strong>: Swift's optional types make null pointer dereferences impossible at compile time.</p>
</li>
<li><p><strong>Use After Free</strong>: Swift's ownership model prevents use-after-free errors that can cause crashes or security vulnerabilities.</p>
</li>
</ul>
<p>Now that we’ve covered Swift's memory model and ARC behavior, let’s explore how it compares to C in terms of memory usage and instruction cycles, a crucial aspect when evaluating Embedded Swift for real-world deployment.</p>
<h2 id="heading-memory-and-instruction-cycle-comparison">Memory and Instruction Cycle Comparison</h2>
<p>Understanding the performance characteristics of Swift versus C is essential for embedded systems, where every instruction cycle and byte of memory matters. While Swift brings advantages like safety and expressiveness, these benefits come with certain trade-offs in terms of memory usage and runtime behavior that embedded developers must evaluate carefully.</p>
<h3 id="heading-memory-management">Memory Management:</h3>
<p>Swift uses Automatic Reference Counting (ARC) to manage memory. ARC tracks the number of references to each object and deallocates it when no references remain. This eliminates the need for explicit <code>free()</code> calls but introduces overhead.</p>
<p>C, in contrast, uses manual memory management. Developers allocate memory using <code>malloc</code> and release it using <code>free</code>, or rely on the stack for most short-lived data.</p>
<p>The table below provides the memory management comparison between Swift and C:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Feature</strong></td><td><strong>Swift (ARC)</strong></td><td><strong>C (Manual)</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Memory strategy</td><td>Automatic reference counting</td><td>Manual with <code>malloc</code>/<code>free</code></td></tr>
<tr>
<td>Overhead per object</td><td>4–8 bytes (for ref count)</td><td>None for stack; variable for heap</td></tr>
<tr>
<td>Deallocation</td><td>Deterministic, triggered by ARC</td><td>Developer-controlled</td></tr>
<tr>
<td>Weak reference support</td><td>Requires additional metadata</td><td>Not built-in</td></tr>
<tr>
<td>Thread safety</td><td>Atomic operations in ARC</td><td>Not guaranteed</td></tr>
<tr>
<td>Layout control</td><td>Limited, compiler-managed</td><td>Full control (via structs/pointers)</td></tr>
</tbody>
</table>
</div><p>Swift ensures safety through deterministic cleanup and predictable memory usage. But this comes at the cost of added memory and CPU overhead.</p>
<p>C’s approach offers complete control over memory layout and minimal runtime cost, but increases the risk of memory leaks and fragmentation without disciplined practices.</p>
<h3 id="heading-instruction-cycle-analysis">Instruction Cycle Analysis</h3>
<p>The safety features in Swift, such as bounds checking, optional unwrapping, and ARC updates, translate into additional CPU instructions. While this can impact performance, the Swift compiler is aggressive about optimization in release builds. For example, inlining and ARC elision can remove much of the overhead in performance-critical paths.</p>
<p>C has no built-in safety checks, allowing it to generate highly efficient, predictable code. Developers can even use inline assembly for tight control over performance.</p>
<p>The table below provides the instruction cycle comparison between Swift and C:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Instruction-Level Feature</strong></td><td><strong>Swift</strong></td><td><strong>C</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Reference count updates</td><td>2–4 instructions per assignment</td><td>N/A</td></tr>
<tr>
<td>Bounds checking</td><td>1–3 instructions per array access</td><td>None</td></tr>
<tr>
<td>Optional unwrapping</td><td>1–2 instructions per check</td><td>N/A</td></tr>
<tr>
<td>Method dispatch</td><td>Protocols introduce indirection</td><td>Direct calls or function pointers</td></tr>
<tr>
<td>Optimization potential</td><td>ARC elision, inlining, dead code removal</td><td>Full manual control, inline assembly</td></tr>
<tr>
<td>Predictability</td><td>High in optimized builds, with some abstraction overhead</td><td>Very high, minimal abstraction</td></tr>
</tbody>
</table>
</div><p>Although Swift inserts extra instructions for safety, much of this cost can be mitigated through compiler optimization.</p>
<p>C has no such features by default, making it ideal for applications where performance must be tightly controlled and the developer is willing to take full responsibility for safety.</p>
<h3 id="heading-instruction-count-comparison-swift-vs-c-loop-performance">Instruction Count Comparison: Swift vs C Loop Performance</h3>
<p>When evaluating Swift and C for embedded use, it's helpful to analyze instruction-level performance on basic operations, such as a loop that processes an array of floating-point numbers. This gives us a concrete sense of the computational cost of each language's safety and abstraction features.</p>
<p>Let’s consider a simple example: summing an array of <code>Float</code> values and returning the average. In Swift, the code uses a high-level <code>for-in</code> loop over an array:</p>
<p>Simple loop performance:</p>
<pre><code class="lang-swift"><span class="hljs-comment">// Swift loop with safety checks</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">processData</span><span class="hljs-params">(<span class="hljs-number">_</span> data: [Float])</span></span> -&gt; <span class="hljs-type">Float</span> {
    <span class="hljs-keyword">var</span> sum: <span class="hljs-type">Float</span> = <span class="hljs-number">0.0</span>
    <span class="hljs-keyword">for</span> value <span class="hljs-keyword">in</span> data {  <span class="hljs-comment">// Iterator with bounds checking</span>
        sum += value     <span class="hljs-comment">// Safe arithmetic</span>
    }
    <span class="hljs-keyword">return</span> sum / <span class="hljs-type">Float</span>(data.<span class="hljs-built_in">count</span>)  <span class="hljs-comment">// Safe division</span>
}
<span class="hljs-comment">// Estimated: ~8-10 instructions per iteration</span>
</code></pre>
<p>Although elegant and safe, this loop includes several safety mechanisms:</p>
<ol>
<li><p>Bounds checking on every array access</p>
</li>
<li><p>Reference counting if <code>data</code> is passed as a reference type</p>
</li>
<li><p>Overflow protection in debug mode</p>
</li>
<li><p>Optional handling or runtime checks if <code>data</code> might be empty</p>
</li>
</ol>
<p>These checks introduce runtime overhead, resulting in an estimated 8–10 instructions per iteration on most platforms (depending on optimization level and target architecture). In release builds, Swift aggressively inlines and strips redundant checks, but some level of abstraction cost remains, especially compared to raw memory access in C.</p>
<p>Now, compare that to its equivalent in C:</p>
<pre><code class="lang-c"><span class="hljs-comment">// C loop without safety checks</span>
<span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">process_data</span><span class="hljs-params">(<span class="hljs-keyword">float</span>* data, <span class="hljs-keyword">int</span> count)</span> </span>{
    <span class="hljs-keyword">float</span> sum = <span class="hljs-number">0.0f</span>;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; count; i++) {  <span class="hljs-comment">// Direct pointer arithmetic</span>
        sum += data[i];                <span class="hljs-comment">// Direct memory access</span>
    }
    <span class="hljs-keyword">return</span> sum / count;  <span class="hljs-comment">// Direct division (no safety check)</span>
}
<span class="hljs-comment">// Estimated: ~4-5 instructions per iteration</span>
</code></pre>
<p>This version performs direct memory access with pointer arithmetic, no bounds checks, and no type safety. The C code is lower-level, with fewer runtime checks, and compiles down to just 4–5 instructions per iteration, depending on the target CPU and compiler flags. It is lean and fast, ideal for cycles-per-instruction-critical scenarios.</p>
<p>The table below shows the comparison of single loop performance between Swift and C:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Aspect</td><td>Swift</td><td>C</td></tr>
</thead>
<tbody>
<tr>
<td>Array access</td><td>Bounds-checked</td><td>Direct pointer access</td></tr>
<tr>
<td>Loop iteration</td><td>High-level iterator abstraction</td><td>Raw loop with pointer increment</td></tr>
<tr>
<td>Instruction count (per loop)</td><td>~8–10 (in debug), ~6–8 (in release)</td><td>~4–5</td></tr>
<tr>
<td>Division</td><td>Safe (avoids divide-by-zero in dev)</td><td>Direct</td></tr>
<tr>
<td>Overflow behavior</td><td>Checked in debug, unchecked in release</td><td>Unchecked</td></tr>
<tr>
<td>Readability and safety</td><td>High</td><td>Low</td></tr>
<tr>
<td>Performance</td><td>Lower (but optimizable)</td><td>Higher (manual)</td></tr>
</tbody>
</table>
</div><p>Now that we’ve compared Swift and C in terms of memory and cycle costs, let’s move into the practical side: how to set up Embedded Swift on an STM32 platform and get started with real-world development.</p>
<h2 id="heading-how-to-setup-embedded-swift">How to Setup Embedded Swift</h2>
<p>In this section, we'll walk through how to configure and use Embedded Swift for development on STM32 microcontrollers. STM32 is a popular family of ARM Cortex-M–based microcontrollers, commonly used in industrial, consumer, and IoT applications.</p>
<h3 id="heading-prerequisites-1">Prerequisites</h3>
<p><strong>Required Software:</strong></p>
<ul>
<li><p>Swift Development Snapshot (includes the Embedded Swift toolchain)</p>
</li>
<li><p>Swiftly - Easiest way to manage and install swift toolchains</p>
</li>
<li><p>Swiftc - Swift Compiler command-line tool</p>
</li>
<li><p>Python3 - Required to run scripts to convert Mach-O to binary files</p>
</li>
<li><p>Git (to clone sample repositories) like <a target="_blank" href="https://github.com/swiftlang/swift-embedded-examples">https://github.com/swiftlang/swift-embedded-examples</a></p>
</li>
<li><p>A Unix-like development environment (macOS is currently best supported)</p>
</li>
</ul>
<p><strong>Target Hardware:</strong> This guide focuses on STM32 microcontrollers, which are widely used in embedded applications and have excellent community support.</p>
<p>This guide walks you through the full setup process, from installing the required Swift toolchain to flashing the final binary onto your board. We’ll begin by installing the Swift Development Snapshot using Swiftly, a simple command-line utility for managing Swift toolchains. From there, we’ll configure the build system, set up the correct board variant, customize the build script, and compile the Swift and C source code into a binary. Finally, we’ll flash the firmware onto the STM32 using standard tools</p>
<h3 id="heading-install-swift-development-snapshot">Install Swift Development Snapshot</h3>
<p>The easiest way to install and manage Embedded Swift toolchains is by using the swiftly tool, which simplifies downloading and using Swift snapshots.</p>
<h4 id="heading-macos-installation">macOS Installation:</h4>
<p>The below steps will help install the Swift embedded toolchain:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Using Swiftly (Recommended)</span>
curl -O https://download.swift.org/swiftly/darwin/swiftly.pkg
installer -pkg swiftly.pkg -target CurrentUserHomeDirectory
~/.swiftly/bin/swiftly init --quiet-shell-followup
<span class="hljs-built_in">source</span> <span class="hljs-string">"<span class="hljs-variable">${SWIFTLY_HOME_DIR:-<span class="hljs-variable">$HOME</span>/.swiftly}</span>/env.sh"</span>

<span class="hljs-comment"># Install and use development snapshot</span>
swiftly install main-snapshot
swiftly use main-snapshot

<span class="hljs-comment"># Verify installation</span>
swift --version
</code></pre>
<p>You can clone this Github example repository:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/swiftlang/swift-embedded-examples.git 
<span class="hljs-built_in">cd</span> swift-embedded-examples/projects/stm32-blink
</code></pre>
<p>The stm32-blink contains:</p>
<ul>
<li><p>Swift code that toggles GPIOs</p>
</li>
<li><p>A C startup file with vector table</p>
</li>
<li><p>A build.sh script that uses swiftc, clang, and a custom linker setup</p>
</li>
</ul>
<h3 id="heading-setup-the-stm32-board">Setup the STM32 Board</h3>
<p>Tell the build script which STM32 board is being used:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> STM_BOARD=STM32F746G_DISCOVERY
</code></pre>
<p>You can add your own board variant by defining the appropriate memory map and compiler flags in the script.</p>
<h3 id="heading-modify-buildsh-optional">Modify build.sh (Optional)</h3>
<p>Ensure the script correctly locates the following:</p>
<ul>
<li><p>swiftc: should point to the toolchain you installed with Swiftly</p>
</li>
<li><p>clang: can be macOS’s default Clang</p>
</li>
<li><p>libBuiltin.a, crt0.s, and macho2bin.py: used to provide minimal runtime support and convert output to flashable binaries</p>
</li>
</ul>
<p>If needed, update these paths:</p>
<pre><code class="lang-bash">SWIFT_EXEC=<span class="hljs-variable">${SWIFT_EXEC:-$(swiftly which swiftc)}</span>
CLANG_EXEC=<span class="hljs-variable">${CLANG_EXEC:-$(xcrun -f clang)}</span>
PYTHON_EXEC=<span class="hljs-variable">${PYTHON_EXEC:-$(which python3)}</span>
</code></pre>
<p>Ensure the linker flags match your target’s flash and RAM sizes.</p>
<h3 id="heading-build-and-flash-the-project">Build and Flash the Project:</h3>
<p>Run:</p>
<pre><code class="lang-bash">./build.sh
</code></pre>
<p>This compiles Swift and C code, links them, and produces a blink.bin file.</p>
<p>If successful, you’ll see:</p>
<pre><code class="lang-bash">.build/blink.bin  <span class="hljs-comment"># ready to flash Step 6: Flash the Firmware to STM32</span>
</code></pre>
<p>Use ST-Link tools or openocd to flash your board. Example using st-flash:</p>
<pre><code class="lang-bash">brew install stlink
st-flash write .build/blink.bin 0x8000000
</code></pre>
<p>You should now see an LED blinking.</p>
<p><a target="_blank" href="https://docs.swift.org/embedded/documentation/embedded/stm32baremetalguide">Here’s</a> a more detailed step by step approach to writing a bare metal code on STM32. For comprehensive installation guides covering other platforms (Raspberry Pi Pico, ESP32, nRF52), detailed IDE configuration, troubleshooting, and advanced examples, you can check out the official documentation:</p>
<ul>
<li><p>Complete Setup Guide: <a target="_blank" href="https://docs.swift.org/embedded/documentation/embedded/installembeddedswift/">Install Embedded Swift</a></p>
</li>
<li><p>Platform Examples: <a target="_blank" href="https://github.com/apple/swift-embedded-examples">Swift Embedded Examples Repository</a></p>
</li>
<li><p>Getting Started Tutorial: <a target="_blank" href="https://docs.swift.org/embedded/documentation/embedded">Embedded Swift on Microcontrollers</a></p>
</li>
</ul>
<p>Now that we’ve set up Embedded Swift and explored how to build and run an example project, let’s look at a critical real-world scenario: interfacing Swift with low-level C drivers.</p>
<h2 id="heading-c-swift-linkages">C-Swift Linkages</h2>
<p>In many embedded projects, low-level hardware drivers are written in C because of its close-to-metal control and widespread ecosystem support. Embedded Swift supports seamless interoperability with C, which lets you reuse existing C libraries and drivers, write hardware control logic in C, and implement higher-level application logic in Swift.</p>
<p>This hybrid model lets you combine Swift’s safety and productivity with C’s hardware-level control, with no runtime overhead or object translation.</p>
<p>Let’s walk through an example where a low-level sensor driver is implemented in C and the application logic is written in Swift.</p>
<h3 id="heading-c-header-file-sensordriverh">C Header File (sensor_driver.h):</h3>
<p>This C header file defines the public interface for a low-level sensor driver. It includes standard fixed-width integer types and declares four functions:</p>
<ul>
<li><p>sensor_init(): Initializes the hardware sensor</p>
</li>
<li><p>sensor_read_temperature() and sensor_read_humidity(): Read raw sensor values</p>
</li>
<li><p>sensor_delay_ms(): Delays execution for a given number of milliseconds</p>
</li>
</ul>
<p>This interface acts as a bridge between Swift and C. Swift will link to these functions by name, no wrappers or bindings required.</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">ifndef</span> SENSOR_DRIVER_H</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> SENSOR_DRIVER_H</span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdint.h&gt;</span></span>

<span class="hljs-comment">// Low-level sensor driver functions</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sensor_init</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">uint32_t</span> <span class="hljs-title">sensor_read_temperature</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">uint32_t</span> <span class="hljs-title">sensor_read_humidity</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sensor_delay_ms</span><span class="hljs-params">(<span class="hljs-keyword">uint32_t</span> milliseconds)</span></span>;

<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span></span>
</code></pre>
<h3 id="heading-c-implementation-sensordriverc">C Implementation (sensor_driver.c):</h3>
<p>This implementation assumes the sensor is memory-mapped at a fixed address (<code>0x40001000</code>). Each register, temperature, humidity, and control, is accessed by offset from that base address.</p>
<p>The <code>sensor_init</code>() function writes <code>0x01</code> to the control register, presumably enabling or starting the sensor hardware.</p>
<p>The <code>sensor_read_temperature()</code> method and <code>sensor_read_humidity()</code> method reads from memory-mapped registers and return the raw ADC values from the sensor.</p>
<p>The <code>sensor_delay_ms()</code> method performs a simple busy-wait loop using nop (no-operation) instructions to approximate a delay. This is suitable for short, coarse-grained delays in bare-metal contexts.</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"sensor_driver.h"</span></span>

<span class="hljs-comment">// Hardware register addresses</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> SENSOR_BASE_ADDR    0x40001000</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> TEMP_REG_OFFSET     0x00</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> HUMIDITY_REG_OFFSET 0x04</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> CONTROL_REG_OFFSET  0x08</span>

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sensor_init</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>{
    <span class="hljs-comment">// Initialize sensor hardware</span>
    <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">uint32_t</span>* control_reg = (<span class="hljs-keyword">volatile</span> <span class="hljs-keyword">uint32_t</span>*)(SENSOR_BASE_ADDR + CONTROL_REG_OFFSET);
    *control_reg = <span class="hljs-number">0x01</span>; <span class="hljs-comment">// Enable sensor</span>
}

<span class="hljs-function"><span class="hljs-keyword">uint32_t</span> <span class="hljs-title">sensor_read_temperature</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>{
    <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">uint32_t</span>* temp_reg = (<span class="hljs-keyword">volatile</span> <span class="hljs-keyword">uint32_t</span>*)(SENSOR_BASE_ADDR + TEMP_REG_OFFSET);
    <span class="hljs-keyword">return</span> *temp_reg;
}

<span class="hljs-function"><span class="hljs-keyword">uint32_t</span> <span class="hljs-title">sensor_read_humidity</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>{
    <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">uint32_t</span>* humidity_reg = (<span class="hljs-keyword">volatile</span> <span class="hljs-keyword">uint32_t</span>*)(SENSOR_BASE_ADDR + HUMIDITY_REG_OFFSET);
    <span class="hljs-keyword">return</span> *humidity_reg;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sensor_delay_ms</span><span class="hljs-params">(<span class="hljs-keyword">uint32_t</span> milliseconds)</span> </span>{
    <span class="hljs-comment">// Simple delay implementation</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint32_t</span> i = <span class="hljs-number">0</span>; i &lt; milliseconds * <span class="hljs-number">1000</span>; i++) {
        __asm__(<span class="hljs-string">"nop"</span>);
    }
}
</code></pre>
<h3 id="heading-swift-code-using-c-driver">Swift Code Using C Driver:</h3>
<p>To use these C functions from Swift, you declare them using <code>@_silgen_name</code>, which tells the Swift compiler to link directly to these symbol names at runtime.</p>
<p>The <code>SensorController</code> class encapsulates sensor-related logic. In its <code>init()</code> method, it calls the <code>sensor_init()</code> function defined in C to initialize the sensor hardware.</p>
<p>The <code>readSensors()</code> method reads the raw values from the C driver, converts them into human-readable units using helper functions, stores them internally, and returns the processed values.</p>
<p>The <code>convertTemperature()</code> and <code>convertHumidity()</code> conversion methods apply a basic linear formula to turn raw ADC values into temperature in Celsius and humidity in percentage, respectively. These formulas would be based on the specific sensor’s datasheet.</p>
<p>The <code>checkThresholds()</code> method applies simple threshold logic, a good example of where Swift’s readability and type safety shine. You could easily expand this logic to include error bounds, state machines, or alerts.</p>
<pre><code class="lang-swift"><span class="hljs-comment">// Import C driver functions</span>

<span class="hljs-comment">/*
These declarations match the C function signatures exactly. 
They allow Swift to invoke the C functions as if they were native Swift functions 
— with zero overhead.
*/</span>
@_silgen_name(<span class="hljs-string">"sensor_init"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sensor_init</span><span class="hljs-params">()</span></span>

@_silgen_name(<span class="hljs-string">"sensor_read_temperature"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sensor_read_temperature</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">UInt32</span>

@_silgen_name(<span class="hljs-string">"sensor_read_humidity"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sensor_read_humidity</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">UInt32</span>

@_silgen_name(<span class="hljs-string">"sensor_delay_ms"</span>)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sensor_delay_ms</span><span class="hljs-params">(<span class="hljs-number">_</span> ms: UInt32)</span></span>

<span class="hljs-comment">// Swift sensor controller using C driver</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SensorController</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> lastTemperature: <span class="hljs-type">Float</span> = <span class="hljs-number">0.0</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> lastHumidity: <span class="hljs-type">Float</span> = <span class="hljs-number">0.0</span>

    <span class="hljs-keyword">init</span>() {
        <span class="hljs-comment">// Initialize the C driver</span>
        sensor_init()
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">readSensors</span><span class="hljs-params">()</span></span> -&gt; (temperature: <span class="hljs-type">Float</span>, humidity: <span class="hljs-type">Float</span>) {
        <span class="hljs-comment">// Read raw values from C driver</span>
        <span class="hljs-keyword">let</span> rawTemp = sensor_read_temperature()
        <span class="hljs-keyword">let</span> rawHumidity = sensor_read_humidity()

        <span class="hljs-comment">// Convert raw values to meaningful units in Swift</span>
        <span class="hljs-keyword">let</span> temperature = convertTemperature(rawValue: rawTemp)
        <span class="hljs-keyword">let</span> humidity = convertHumidity(rawValue: rawHumidity)

        <span class="hljs-comment">// Store for comparison</span>
        lastTemperature = temperature
        lastHumidity = humidity

        <span class="hljs-keyword">return</span> (temperature: temperature, humidity: humidity)
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">convertTemperature</span><span class="hljs-params">(rawValue: UInt32)</span></span> -&gt; <span class="hljs-type">Float</span> {
        <span class="hljs-comment">// Convert raw ADC value to Celsius</span>
        <span class="hljs-keyword">return</span> (<span class="hljs-type">Float</span>(rawValue) * <span class="hljs-number">3.3</span> / <span class="hljs-number">4095.0</span> - <span class="hljs-number">0.5</span>) * <span class="hljs-number">100.0</span>
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">convertHumidity</span><span class="hljs-params">(rawValue: UInt32)</span></span> -&gt; <span class="hljs-type">Float</span> {
        <span class="hljs-comment">// Convert raw ADC value to percentage</span>
        <span class="hljs-keyword">return</span> <span class="hljs-type">Float</span>(rawValue) * <span class="hljs-number">100.0</span> / <span class="hljs-number">4095.0</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">checkThresholds</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Bool</span> {
        <span class="hljs-comment">// Swift logic for threshold checking</span>
        <span class="hljs-keyword">let</span> tempThreshold: <span class="hljs-type">Float</span> = <span class="hljs-number">25.0</span>
        <span class="hljs-keyword">let</span> humidityThreshold: <span class="hljs-type">Float</span> = <span class="hljs-number">60.0</span>

        <span class="hljs-keyword">return</span> lastTemperature &gt; tempThreshold || lastHumidity &gt; humidityThreshold
    }
}

<span class="hljs-comment">// Main application loop</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> -&gt; <span class="hljs-type">Never</span> {
    <span class="hljs-keyword">let</span> sensorController = <span class="hljs-type">SensorController</span>()

    <span class="hljs-keyword">while</span> <span class="hljs-literal">true</span> {
        <span class="hljs-comment">// Read sensors using Swift controller with C driver</span>
        <span class="hljs-keyword">let</span> readings = sensorController.readSensors()

        <span class="hljs-comment">// Process data with Swift's type safety and expressiveness</span>
        <span class="hljs-keyword">if</span> sensorController.checkThresholds() {
            <span class="hljs-built_in">print</span>(<span class="hljs-string">"Warning: Temperature: \(readings.temperature)°C, Humidity: \(readings.humidity)%"</span>)
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">print</span>(<span class="hljs-string">"Normal: Temperature: \(readings.temperature)°C, Humidity: \(readings.humidity)%"</span>)
        }

        <span class="hljs-comment">// Delay using C driver function</span>
        sensor_delay_ms(<span class="hljs-number">1000</span>) <span class="hljs-comment">// 1 second delay</span>
    }
}
</code></pre>
<p>The <code>func main()</code> is the main event loop standard for embedded systems. It creates the sensor controller, reads sensor data in a loop, checks thresholds, and prints results accordingly. The loop includes a delay (via the C driver) to avoid hammering the sensor continuously.</p>
<p>In an actual embedded context, instead of using <code>print()</code>, you might blink an LED, send UART messages, or log data to memory.</p>
<p>With Embedded Swift and C now working together, let’s explore what lies ahead. The next section outlines ongoing improvements, emerging use cases, and research directions that are shaping the future of Embedded Swift.</p>
<h2 id="heading-future-work">Future Work</h2>
<p>Embedded Swift is still a young but rapidly evolving technology. Its modern language features, type safety, and performance make it an attractive option for embedded development, and ongoing work is expanding its capabilities, reach, and ecosystem.</p>
<h3 id="heading-ongoing-improvements">Ongoing Improvements</h3>
<p><strong>Compiler Optimizations</strong>: The Swift compiler team is actively improving code generation for embedded targets, including:</p>
<ul>
<li><p>Reducing binary size</p>
</li>
<li><p>Minimizing ARC overhead</p>
</li>
<li><p>Improving static dispatch performance</p>
</li>
</ul>
<p><strong>Hardware Support</strong>: Embedded Swift can target a wide variety of ARM and RISC-V microcontrollers, which are popular for building industrial applications. Support for additional architectures is being developed.</p>
<p><strong>Tooling Enhancements</strong>: Tooling support for Embedded Swift is still evolving, but several community-driven and open-source efforts are making development more accessible:</p>
<ul>
<li><p><strong>Build Systems</strong>: The Swift Embedded Working Group provides example projects that adapt Swift Package Manager (SwiftPM) for cross-compilation. Custom linker scripts and build helpers are available for platforms like STM32 and nRF52.</p>
</li>
<li><p><strong>Debugging Support</strong>: Developers can debug Embedded Swift programs using existing tools like GDB or OpenOCD, provided the build includes appropriate debug symbols. While not yet officially streamlined, this approach enables step-through debugging on real hardware.</p>
</li>
<li><p><strong>IDE Integration</strong>: There is no official IDE support yet, but some developers use VSCode with Swift syntax highlighting and external build tasks. These setups are still manual but serve as early prototypes for embedded workflows.</p>
</li>
</ul>
<h3 id="heading-emerging-use-cases">Emerging Use Cases</h3>
<p>There are a number of emerging use cases for embedded Swift. For example, Swift’s memory safety, type guarantees, and protocol-oriented design make it ideal for secure and scalable IoT devices, especially where firmware bugs could affect user safety or privacy.</p>
<p>The automotive sector is also exploring Swift for infotainment systems, driver assistance features, and safety-critical logic (where deterministic execution and safety matter).</p>
<p>Swift’s expressive syntax and compile-time safety make it suitable for industrial automation – think real-time control loops, sensor fusion systems, and edge devices in smart manufacturing.</p>
<p>It’s also useful for medical devices, as it aligns well with strict medical regulations around memory safety, type guarantees, and predictable resource usage.</p>
<h3 id="heading-community-and-ecosystem">Community and Ecosystem</h3>
<h4 id="heading-open-source-projects">Open Source Projects</h4>
<p>The Swift Embedded working group maintains <a target="_blank" href="https://github.com/swiftlang/swift-embedded-examples">example repositories</a> showcasing how to use Embedded Swift on microcontrollers such as STM32, nRF52, and ESP32. Early-stage libraries for UART, GPIO, and basic peripherals are emerging, though the ecosystem is still young compared to C or Rust.</p>
<h4 id="heading-learning-resources">Learning Resources</h4>
<p>While <a target="_blank" href="https://docs.swift.org/embedded/documentation/embedded">Embedded Swift</a> is not yet widely taught in formal curricula, community tutorials and exploratory projects (for example, Swift for Arduino) are lowering the barrier for hobbyists and independent learners. As tooling matures, educational adoption is likely to follow.</p>
<h4 id="heading-industry-interest">Industry Interest</h4>
<p>Embedded Swift is beginning to draw attention from developers and companies looking for safer, more maintainable alternatives to C. Although large-scale adoption remains limited, use cases like rapid prototyping, IoT development, and internal experimentation are gaining traction.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Embedded Swift represents a major step forward in embedded programming. By combining the power and safety of Swift with the low-level control needed for microcontrollers, it offers an exciting alternative to traditional C and C++ development.</p>
<p>While C will remain essential for hardware-level programming and performance-critical paths, Swift brings compelling advantages to many embedded scenarios:</p>
<ul>
<li><p><strong>Memory safety</strong>: Swift eliminates entire categories of bugs such as buffer overflows, use-after-free, and null pointer dereferencing.</p>
</li>
<li><p><strong>Type safety</strong>: Many logic errors are caught at compile time, long before they can cause runtime failures.</p>
</li>
<li><p><strong>Modern language features</strong>: Developers can use functional paradigms, generics, and protocol-oriented design even in embedded code.</p>
</li>
<li><p><strong>C interoperability</strong>: Swift works seamlessly with existing C libraries, allowing gradual adoption without rewriting low-level drivers.</p>
</li>
<li><p><strong>Developer productivity</strong>: Clear syntax, automatic memory management, and strong tooling lead to faster development and easier maintenance.</p>
</li>
</ul>
<p>Government and regulatory bodies are increasingly encouraging or mandating the use of memory-safe programming languages to reduce vulnerabilities in critical software systems. For example:</p>
<ul>
<li><p>In 2022, the <a target="_blank" href="https://media.defense.gov/2025/Jun/23/2003742198/-1/-1/0/CSI_MEMORY_SAFE_LANGUAGES_REDUCING_VULNERABILITIES_IN_MODERN_SOFTWARE_DEVELOPMENT.PDF"><strong>U.S. National Security Agency (NSA)</strong></a> recommended moving away from unsafe languages like C/C++ for new software projects, promoting memory-safe alternatives.</p>
</li>
<li><p>In June 2025, the NSA and CISA released a joint Cybersecurity Information Sheet titled “<a target="_blank" href="https://www.nsa.gov/Press-Room/Press-Releases-Statements/Press-Release-View/Article/4223298/nsa-and-cisa-release-csi-highlighting-importance-of-memory-safe-languages-in-so/">Memory Safe Languages: Reducing Vulnerabilities in Modern Software Development</a>”, which emphasized that memory safety flaws remain a persistent risk, and organizations should develop strategies to adopt memory-safe programming languages in new systems.</p>
</li>
<li><p>The <a target="_blank" href="https://www.trust-in-soft.com/resources/blogs/memory-safety-is-key-the-shift-in-u.s.-cyber-standards"><strong>U.S. Cybersecurity and Infrastructure Security Agency (CISA)</strong></a> and <a target="_blank" href="https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-218.pdf"><strong>NIST</strong></a> have echoed similar guidance in the context of national cybersecurity.</p>
</li>
</ul>
<p>While these documents do not mention Swift explicitly, Swift's strong type system, ARC-based memory model, and compile-time safety guarantees align closely with the goals outlined in these recommendations. As such, it offers a practical, developer-friendly path toward safer embedded development.</p>
<p>Swift may not be the right fit for every embedded system. In applications where every byte of memory or instruction cycle is critical, real-time guarantees are hard requirements, or toolchain maturity is essential (for example, RTOS integration, static analyzers), C or Rust may still be preferred.</p>
<p>But in many modern embedded applications, especially those involving rapid prototyping, fast product iteration, safety-critical or maintainable firmware, and interoperability with existing C codebases, Swift offers a highly productive and safe development experience.</p>
<p>Embedded Swift is still maturing, but its momentum is undeniable. With ongoing compiler work, community-driven examples, and growing interest from developers, it’s poised to play a major role in the future of embedded systems.</p>
<p>Whether you're building an IoT device, a piece of industrial equipment, or a proof-of-concept wearable, Swift can help you write safer, more expressive firmware, without giving up performance or control.</p>
<p>Swift can be especially powerful during the prototyping phase, when the primary goal is to validate functionality quickly and safely. And with its increasing support for multiple hardware platforms, it offers a strong foundation for bringing modern software development practices to the embedded world.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use a Resistive Soil Moisture Sensor ]]>
                </title>
                <description>
                    <![CDATA[ A resistive soil moisture sensor is a widely used, simple, and affordable way of estimating the amount of water in the soil. In this tutorial, you will learn how to interface a resistive soil moisture sensor with an Arduino UNO microcontroller. You w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-a-resistive-soil-moisture-sensor/</link>
                <guid isPermaLink="false">686f06fb2134825dffc4a228</guid>
                
                    <category>
                        <![CDATA[ arduino ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Michael Ikoko ]]>
                </dc:creator>
                <pubDate>Thu, 10 Jul 2025 00:19:07 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752106699262/6ae871df-e1fb-4019-a446-9bd8cca1cab0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A resistive soil moisture sensor is a widely used, simple, and affordable way of estimating the amount of water in the soil.</p>
<p>In this tutorial, you will learn how to interface a resistive soil moisture sensor with an Arduino UNO microcontroller. You will learn about the parts and components of the sensor, how to calibrate the sensor for your soil type, and how to read both analog and digital output data from the sensor.</p>
<p>You will implement two practical examples in this tutorial:</p>
<ol>
<li><p>The first example illustrates how you can read the analog output data from the sensor and convert the analog reading to a percentage value.</p>
</li>
<li><p>The second example illustrates how you can use the digital output from the sensor to determine if the soil is wet or dry, and indicate the result using a red and green LED.</p>
</li>
</ol>
<p>At the end of the tutorial, you will have a solid understanding of how a resistive soil moisture sensor works and how to integrate the sensor into your microcontroller-based projects.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To effectively follow along with this tutorial, you should have the following components:</p>
<ul>
<li><p>Arduino UNO</p>
</li>
<li><p>Resistive Soil Moisture Sensor</p>
</li>
<li><p>Breadboard</p>
</li>
<li><p>5 LEDs (any color for the analog example)</p>
</li>
<li><p>1 red LED and 1 green LED (for the digital example)</p>
</li>
<li><p>220-ohm resistors (one per LED)</p>
</li>
<li><p>Jumper wires</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-soil-moisture-sensor">What is a Soil Moisture Sensor?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-types-of-soil-moisture-sensors">Types of Soil Moisture Sensors</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-parts-of-a-resistive-soil-moisture-sensor">Parts of a Resistive Soil Moisture Sensor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-calibrate-the-sensor-for-your-soil">How to Calibrate the Sensor for Your Soil</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-example-1-how-to-determine-soil-moisture-level-in-percentage-from-analog-output">Example 1: How to Determine Soil Moisture Level in Percentage from Analog Output</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-example-2-how-to-determine-soil-moisture-state-from-digital-output">Example 2: How to Determine Soil Moisture State from Digital Output</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-soil-moisture-sensor">What is a Soil Moisture Sensor?</h2>
<p>A soil moisture sensor is a device that estimates the moisture content in the soil. Soil moisture sensors typically operate by measuring the soil's electrical properties, such as dielectric and resistance. Some soil moisture sensors also use time domain methods to determine the propagation speed of electromagnetic waves through the soil.</p>
<p>Soil moisture sensors have various applications in different fields. These applications include, but are not limited to:</p>
<ul>
<li><p>Climate and environmental research</p>
</li>
<li><p>Automated/smart irrigation system</p>
</li>
<li><p>Greenhouse monitoring systems</p>
</li>
<li><p>Urban planning</p>
</li>
</ul>
<h2 id="heading-types-of-soil-moisture-sensors">Types of Soil Moisture Sensors</h2>
<p>Soil moisture sensors are categorized based on the property of the soil that they measure as an indicator of the moisture content. The most common soil moisture sensors for small-scale projects are:</p>
<ul>
<li><p>Resistive soil moisture sensors</p>
</li>
<li><p>Capacitive soil moisture sensors</p>
</li>
</ul>
<h3 id="heading-resistive-soil-moisture-sensor">Resistive Soil Moisture Sensor</h3>
<p>A resistive soil moisture sensor estimates the moisture content based on the relationship between water content and soil resistivity. The electrical resistivity of the soil reduces exponentially as the water content increases.</p>
<p>The resistive soil moisture sensor has two probes inserted into the soil. It measures the electrical resistance of the soil between the two probes.</p>
<h3 id="heading-capacitive-soil-moisture-sensor">Capacitive Soil Moisture Sensor</h3>
<p>A capacitive soil moisture sensor determines the moisture content of the soil based on the relationship between water content and the dielectric properties of the soil. The dielectric constant of a soil increases as the water content increases.</p>
<p>A capacitive soil moisture sensor typically has a positive plate and a negative plate with space between them. When inserted into the soil, the soil becomes the dielectric medium between both plates. The sensor measures the change in the soil's dielectric property.</p>
<h3 id="heading-how-to-choose-between-resistive-and-capacitive-sensors">How to Choose Between Resistive and Capacitive Sensors</h3>
<p>The choice between a resistive and capacitive soil moisture sensor depends on a couple of factors:</p>
<ul>
<li><p><strong>Cost</strong>: A resistive soil moisture sensor is typically less expensive than a capacitive sensor.</p>
</li>
<li><p><strong>Accuracy</strong>: Capacitive soil moisture sensors are more accurate than their resistive counterparts. Factors like soil type and application of fertilizers have a lesser effect on the sensitivity of the capacitive soil moisture sensor.</p>
</li>
<li><p><strong>Long-term use</strong>: Resistive soil moisture sensors are prone to corrosion with frequent use. This happens because the current flowing through the probes in contact with the soil causes electrolysis of the metal electrodes in the probes. On the other hand, capacitive soil moisture sensors are resistant to corrosion. This is because the contact plates are embedded in corrosion-resistant materials and don't need to be in direct contact with the soil.</p>
</li>
</ul>
<h2 id="heading-parts-of-a-resistive-soil-moisture-sensor">Parts of a Resistive Soil Moisture Sensor</h2>
<p>The resistor soil moisture sensor module is usually made of two parts: the sensor probes and the voltage comparator module.</p>
<h3 id="heading-the-sensor-probes">The Sensor Probes</h3>
<p>The probes are the part of the sensor that is placed in the soil. The probes are used to detect the electrical resistance across two points in the soil.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909048810/7c2367b5-872c-4e98-acf8-d8b2f0851f6c.jpeg" alt="Sensor probes labelled diagram" class="image--center mx-auto" width="2048" height="1152" loading="lazy"></p>
<p>As shown in the diagram above, the sensor probes have the following components:</p>
<ul>
<li><p><strong>Electrodes</strong>: The metal electrodes conduct current through the soil. When the sensor is powered, current flows from one electrode through the soil to the other electrode and back to the comparator module. The sensor then measures the soil's resistance to the electrical signal flowing through the soil across the electrodes to determine the moisture level.</p>
</li>
<li><p><strong>Non-polarized connector pins</strong>: The two connector pins are used to connect the probes to the voltage comparator module. The pins have no polarity and can be connected in any order to the respective connector pins on the voltage comparator module. The probe is connected to the comparator module using female-to-female jumper wires.</p>
</li>
</ul>
<h3 id="heading-the-voltage-comparator-module">The Voltage Comparator Module</h3>
<p>The voltage comparator module interprets the electrical signal from the probes, processes the signals, and provides both analog and digital outputs that can be read by the microcontroller.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909173338/454ae6bf-2d34-4131-af88-c990eb988cbd.jpeg" alt="Sensor voltage comparator module labelled diagram" class="image--center mx-auto" width="2048" height="1152" loading="lazy"></p>
<p>The voltage comparator module has the following components:</p>
<ul>
<li><p><strong>LM393 Comparator Chip</strong>: The LM393 comparator is a dual comparator that compares the electrical signal from the probes to a reference set by the potentiometer and produces a digital output. The reference signal corresponds to a certain soil moisture level (threshold) and is set using the potentiometer.</p>
<ul>
<li><p>The comparator outputs a <code>HIGH</code> when the analog signal read from the probes is above the reference, this means the soil has less moisture than the threshold moisture level.</p>
</li>
<li><p>The comparator outputs a <code>LOW</code> when the analog signal read from the probes is less than the reference; this means the soil has more moisture than the threshold moisture level.</p>
</li>
</ul>
</li>
<li><p><strong>Potentiometer</strong>: The potentiometer is used to set the reference electrical signal that is used by the LM393 comparator chip. The potentiometer raises or lowers the threshold moisture level. It consists of a knob that can be turned either clockwise or counterclockwise.</p>
</li>
<li><p><strong>Power Indicator (PWR-LED)</strong>: The power indicator is an LED that turns on when the module is powered on.</p>
</li>
<li><p><strong>Digital Output Indicator (DO-LED)</strong>: The digital output indicator is an LED that turns on when the sensor detects wet soil. That is, the current moisture level read from the sensor is above the threshold, and the comparator outputs a <code>LOW</code>.</p>
</li>
<li><p><strong>Power Supply Pin (VCC)</strong>: This pin is used to supply power to the sensor. The sensor module can be powered by a 5V or 3.3V voltage source. You should note that changing the voltage source also changes the analog output from the sensor. In this tutorial, you will be using one of the digital pins to power the sensor. The digital pins of the Arduino output 5V. The sensor typically has an operating current of 15mA, and the Arduino digital output pin can provide a maximum current of 40mA, so it can safely power the sensor.</p>
</li>
<li><p><strong>Ground Pin (GND)</strong>: This pin is used to provide a ground reference for the sensor. It is usually connected to any ground pin in your microcontroller.</p>
</li>
<li><p><strong>Digital Output Pin (DO)</strong>: The digital output pin outputs a <code>HIGH</code> or a <code>LOW</code> based on the value obtained from the LM393 comparator. This pin is usually used by a microcontroller to read the digital output of the LM393 comparator.</p>
</li>
<li><p><strong>Analog Output Pin (AO)</strong>: The analog output pin provides a 10-bit analog voltage value. The values range from 0 to 1023, and they indicate the moisture level of the soil. Typically, in most sensors, higher analog values indicate drier soil and lower analog values indicate wetter soil.</p>
</li>
<li><p><strong>Sensor Probe Connector Pins</strong>: These two pins are used to connect the sensor probes to the voltage comparator module.</p>
</li>
</ul>
<h2 id="heading-how-to-calibrate-the-sensor-for-your-soil">How to Calibrate the Sensor for Your Soil</h2>
<p>As explained earlier in the article, the resistive soil moisture sensor is sensitive to soil type. This means that it’s important that you calibrate the sensor to the soil type you intend to use it on. You do this to improve the accuracy of your readings on a particular soil.</p>
<p>One way of calibrating the sensor is to determine the possible range of values for the soil type. That means you measure the sensor's output when the soil is totally dry and when the soil is totally wet. You can then use this range of values to map the sensor's reading to a new scale, like a percentage. The following steps describe how you can calibrate the sensor:</p>
<h3 id="heading-step-1-connect-the-sensor-to-the-arduino">Step 1: Connect the Sensor to the Arduino</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909238974/d84a8df3-52d0-4f5e-90a4-9a4fa6c198fb.jpeg" alt="Schematic diagram for soil calibration" class="image--center mx-auto" width="1393" height="1553" loading="lazy"></p>
<p>Using the image above as a reference, connect the sensor to the Arduino microcontroller as follows:</p>
<ol>
<li><p>Connect the <strong>VCC</strong> or <strong>Power</strong> pin of the sensor's module to digital pin <strong>7</strong> of the Arduino. This allows you to control the supply voltage to the sensor in the Arduino sketch, ensuring that the sensor is only powered when you want to take a reading. Doing this can help improve the durability of the sensor.</p>
</li>
<li><p>Connect the <strong>GND</strong> pin of the sensor's module to a ground pin in the microcontroller.</p>
</li>
<li><p>Connect the analog output pin <strong>AO</strong> of the sensor's module to the analog pin <strong>A0</strong> of the Arduino. This is the pin where you will read the analog data from the sensor.</p>
</li>
<li><p>Connect the two pins on the probes to the two connector pins on the sensor's module. The connector pins have no polarity.</p>
</li>
</ol>
<h3 id="heading-step-2-upload-calibration-sketch-to-microcontroller">Step 2: Upload Calibration Sketch to Microcontroller</h3>
<p>Upload the following sketch into the Arduino:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> sensorPin = A0; <span class="hljs-comment">// Analog input pin for sensor</span>
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> powerPin = <span class="hljs-number">7</span>; <span class="hljs-comment">// Digital pin to power the sensor</span>

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">getAverageReading</span><span class="hljs-params">(<span class="hljs-keyword">int</span> analogPin, <span class="hljs-keyword">int</span> powerPin, <span class="hljs-keyword">int</span> samples = <span class="hljs-number">10</span>)</span> </span>{
  <span class="hljs-keyword">long</span> total = <span class="hljs-number">0</span>;

  digitalWrite(powerPin, HIGH); <span class="hljs-comment">// Power ON sensor</span>
  delay(<span class="hljs-number">500</span>); <span class="hljs-comment">// Wait for sensor to stabilize</span>

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; samples; i++) {                    
 total += analogRead(analogPin);
    delay(<span class="hljs-number">10</span>); <span class="hljs-comment">// Short gap between cycles</span>
 }

  digitalWrite(powerPin, LOW); <span class="hljs-comment">// Power OFF sensor</span>
  <span class="hljs-keyword">return</span> total / samples;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);

  pinMode(powerPin, OUTPUT);
  digitalWrite(powerPin, LOW); <span class="hljs-comment">// Ensure sensor is off at start</span>

  Serial.println(<span class="hljs-string">"Calibration mode: Insert into DRY or WET soil and observe values."</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-keyword">int</span> avgReading = getAverageReading(sensorPin, powerPin, <span class="hljs-number">20</span>); <span class="hljs-comment">// Take 20 averaged samples</span>
  Serial.print(<span class="hljs-string">"Average analog reading: "</span>);
  Serial.println(avgReading);
  delay(<span class="hljs-number">2000</span>); <span class="hljs-comment">// Update every 2 seconds</span>
}
</code></pre>
<p>In the sketch, you begin by defining the pins for powering the sensor and reading the analog data from the sensor. The digital pin <code>7</code> powers the sensor, and the analog pin <code>A0</code> reads data from the sensor.</p>
<p>The <code>getAverageReading</code> function powers on the sensor, takes multiple readings from the sensor, powers off the sensor, and returns the average of the readings taken. The function has three parameters:</p>
<ul>
<li><p><code>analogPin</code> – The analog input pin, which reads data from the sensor to the microcontroller.</p>
</li>
<li><p><code>powerPin</code> – The pin used to power the sensor. The sensor is powered only when you want to take a reading.</p>
</li>
<li><p><code>samples</code> – The number of readings taken from the sensor. This defaults to <code>10</code>. You take multiple readings as a way of filtering out noise in the sensor data.</p>
</li>
</ul>
<p>In the <code>setup</code> function, you begin by setting the baud rate, which is <code>9600</code> for Arduino UNO and differs across different microcontrollers. Then you set the digital pin <code>7</code> used to power the sensor as an output pin, and write a <code>LOW</code> to the sensor to ensure it is off at the start.</p>
<p>Lastly, in the <code>loop</code> function, you get the average analog reading from the <code>getAverageReading</code> function and print it to the Serial Monitor. You should note that in the sketch, the readings are taken every 2 seconds – this delay should be longer in a practical application in order to improve the durability of the sensor.</p>
<h3 id="heading-step-3-record-the-value-for-dry-soil">Step 3: Record the Value for Dry Soil</h3>
<p>Insert the sensor's probe into a completely dry soil sample, and record the average analog reading. You can dry the soil by baking it in an oven to remove moisture. Note that you must allow the soil to cool before inserting the sensor's probe. Hot soil may damage the sensor. The analog readings from the sensor differ according to soil type but are typically very high for dry soil. This is what I obtained for a dry soil sample:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909305339/35d99200-d169-4419-9292-e54a9c544d77.png" alt="Serial monitor showing analog reading for dry soil" class="image--center mx-auto" width="1368" height="738" loading="lazy"></p>
<h3 id="heading-step-4-record-the-value-for-wet-soil">Step 4: Record the Value for Wet Soil</h3>
<p>Insert the sensor's probe into a completely wet soil sample, and record the average analog reading. The value is typically low when the soil is saturated. This is what I obtained for a wet soil sample:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909623291/5478b034-ce56-4f9b-a6d7-758508eb0c1a.png" alt="Serial monitor showing analog reading for wet soil" class="image--center mx-auto" width="1370" height="738" loading="lazy"></p>
<p>You will use these recorded analog values for wet and dry soil in the subsequent section as reference points to map future readings onto a percent scale. In the example provided later in this tutorial, the moisture level is expressed as a percentage, between 0%(dry) and 100%(wet). Using the calibration values ensures that the percentage output accurately reflects the conditions of your soil type.</p>
<h2 id="heading-example-1-how-to-determine-soil-moisture-level-in-percentage-from-analog-output">Example 1 – How to Determine Soil Moisture Level in Percentage from Analog Output</h2>
<p>In this example, you will learn how to read the analog data from the soil moisture sensor, convert the analog reading to a percentage value, and visually represent the moisture level using five LEDs.</p>
<p>The analog reading from the sensor is inversely proportional to the soil moisture level. This means that higher analog readings indicate drier soil and lower readings indicate wetter soil.</p>
<p>The LEDs act as a visual indicator of the moisture percentage. The LEDs are lit based on a range of soil moisture levels:</p>
<ul>
<li><p>1 LED: 0-20% (very dry)</p>
</li>
<li><p>2 LEDs: 20-40%</p>
</li>
<li><p>3 LEDs: 40-60%</p>
</li>
<li><p>4 LEDs: 60-80%</p>
</li>
<li><p>5 LEDs: 80-100% (very wet)</p>
</li>
</ul>
<h3 id="heading-circuit-diagram">Circuit Diagram</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909722810/0aff204d-2ddd-44d5-80b5-5765b9cf5fe9.jpeg" alt="Circuit diagram for Example 1" class="image--center mx-auto" width="1797" height="1893" loading="lazy"></p>
<p>Using the schematic above as a reference, design the circuit as follows:</p>
<ol>
<li><p>Connect the sensor to the Arduino in the same way as described in the calibration section. That is, connect the sensor's power pin (VCC) to digital pin 7, analog output pin <strong>AO</strong> to analog pin <strong>A0</strong>, and ground pin to any ground pin of the microcontroller.</p>
</li>
<li><p>Connect each of the LEDs in series with a 220-ohm resistor.</p>
</li>
<li><p>Connect the anode of the five LEDs to digital pins 3, 4, 5, 6, and 8 of the microcontroller.</p>
</li>
</ol>
<p>This is how my setup looked:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909770251/226bd848-2a58-4e2f-9bf4-796a54983d47.jpeg" alt="Physical setup for Project 1" class="image--center mx-auto" width="1561" height="1688" loading="lazy"></p>
<h3 id="heading-arduino-code">Arduino Code</h3>
<p>Upload the following sketch to your Arduino:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> sensorPin = A0; <span class="hljs-comment">// Analog input from soil moisture sensor</span>
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> powerPin = <span class="hljs-number">7</span>; <span class="hljs-comment">// Digital pin to power the sensor</span>

<span class="hljs-comment">// LED bar pins (from lowest to highest)</span>
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> ledPins[<span class="hljs-number">5</span>] = {<span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">8</span>};

<span class="hljs-comment">// Calibrated analog values - replace with your values</span>
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> dryValue = <span class="hljs-number">1005</span>;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> wetValue = <span class="hljs-number">254</span>;

<span class="hljs-comment">// Global to hold the last analog value</span>
<span class="hljs-keyword">int</span> lastAnalogReading = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Read and calculate soil moisture percentage</span>
<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">getMoisturePercent</span><span class="hljs-params">(<span class="hljs-keyword">int</span> analogPin, <span class="hljs-keyword">int</span> powerPin, <span class="hljs-keyword">int</span> samples = <span class="hljs-number">10</span>)</span> </span>{
  <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> total = <span class="hljs-number">0</span>;

  digitalWrite(powerPin, HIGH);
  delay(<span class="hljs-number">10</span>); <span class="hljs-comment">// Allow sensor to stabilize</span>

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; samples; i++) {
 total += analogRead(analogPin);
    delay(<span class="hljs-number">10</span>);
 }

  digitalWrite(powerPin, LOW);

  <span class="hljs-keyword">int</span> avgReading = total / samples;
 lastAnalogReading = avgReading;

  <span class="hljs-keyword">int</span> percent = <span class="hljs-built_in">map</span>(avgReading, dryValue, wetValue, <span class="hljs-number">0</span>, <span class="hljs-number">100</span>);
  <span class="hljs-keyword">return</span> constrain(percent, <span class="hljs-number">0</span>, <span class="hljs-number">100</span>);
}

<span class="hljs-comment">// Update LED states using boolean array</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">updateLEDBar</span><span class="hljs-params">(<span class="hljs-keyword">int</span> percent)</span> </span>{
  <span class="hljs-keyword">bool</span> ledStates[<span class="hljs-number">5</span>] = {<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>}; <span class="hljs-comment">// Default all OFF</span>

  <span class="hljs-keyword">if</span> (percent &lt;= <span class="hljs-number">20</span>) {
    ledStates[<span class="hljs-number">0</span>] = <span class="hljs-literal">true</span>;
 } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (percent &lt;= <span class="hljs-number">40</span>) {
    ledStates[<span class="hljs-number">0</span>] = ledStates[<span class="hljs-number">1</span>] = <span class="hljs-literal">true</span>;
 } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (percent &lt;= <span class="hljs-number">60</span>) {
    ledStates[<span class="hljs-number">0</span>] = ledStates[<span class="hljs-number">1</span>] = ledStates[<span class="hljs-number">2</span>] = <span class="hljs-literal">true</span>;
 } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (percent &lt;= <span class="hljs-number">80</span>) {
    ledStates[<span class="hljs-number">0</span>] = ledStates[<span class="hljs-number">1</span>] = ledStates[<span class="hljs-number">2</span>] = ledStates[<span class="hljs-number">3</span>] = <span class="hljs-literal">true</span>;
 } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++) ledStates[i] = <span class="hljs-literal">true</span>;
 }

 <span class="hljs-comment">// Write LED states to pins</span>
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++) {
    digitalWrite(ledPins[i], ledStates[i] ? HIGH : LOW);
 }
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
  pinMode(sensorPin, INPUT);
  pinMode(powerPin, OUTPUT);
  digitalWrite(powerPin, LOW); <span class="hljs-comment">// Start with sensor powered off</span>

  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++) {
    pinMode(ledPins[i], OUTPUT);
    digitalWrite(ledPins[i], LOW); <span class="hljs-comment">// Ensure all LEDs start OFF</span>
 }

  Serial.println(<span class="hljs-string">"Soil Moisture Monitor with 5-LED Bar (Boolean Array)"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-keyword">int</span> moisturePercent = getMoisturePercent(sensorPin, powerPin, <span class="hljs-number">20</span>);

 <span class="hljs-comment">// Print both analog reading and percent</span>
  Serial.print(<span class="hljs-string">"Analog Reading: "</span>);
  Serial.print(lastAnalogReading);
  Serial.print(<span class="hljs-string">"  |  Moisture: "</span>);
  Serial.print(moisturePercent);
  Serial.println(<span class="hljs-string">" %"</span>);

  updateLEDBar(moisturePercent);

  delay(<span class="hljs-number">2000</span>); <span class="hljs-comment">// Update every 2 seconds</span>
}
</code></pre>
<p>In the sketch, you start by defining the analog pin for reading data from the sensor and the digital pin for powering the sensor. You also define the following variables, which will be used in the code:</p>
<ul>
<li><p><code>ledPins[5]</code> – An array that stores the digital pins used to power each LED. The pins are arranged from the first LED to the last one. That is the visual display order from left to right.</p>
</li>
<li><p><code>dryValue</code> – This variable stores the analog value recorded for dry soil during the calibration section.</p>
</li>
<li><p><code>wetValue</code> – This variable stores the analog value recorded for wet soil during the calibration section.</p>
</li>
<li><p><code>lastAnalogReading</code> – This variable stores the last reading taken by the sensor. You use this variable to log the actual analog reading to the Serial Monitor.</p>
</li>
</ul>
<p>The <code>getMoisturePercent</code> function powers on the sensor, takes multiple readings, powers off the sensor, calculates the average analog reading, represents the analog reading in percent, and returns the percent value. The function also saves the average analog reading to the <code>lastAnalogReading</code> variable. You can print it directly here, but this sketch saves it in a separate variable so that you can print it later in the <code>loop</code> function for readability.</p>
<p>You can express the average analog reading in percentage with the <code>map(avgReading, dryValue, wetValue, 0, 100)</code> function. The function remaps the average reading stored in <code>avgReading</code> from the range of your calibration values <code>dryValue</code> and <code>wetValue</code> to a new range between <code>0</code> and <code>100</code> (where <code>0</code> is the driest and <code>100</code> is the wettest). You then use the <code>constrain</code> function to keep values within the <code>0</code> and <code>100</code> range.</p>
<p>The <code>updateLEDBar</code> function displays the percent value using the LEDs. The <code>ledStates</code> array in the function stores the logic state of each LED. You begin by setting all LEDs off – that is, having a state <code>0</code>. The next bit of logic is a simple <code>if</code> statement where you turn on the required LEDs corresponding to a particular percent range by setting the elements in the array to <code>true</code> (equivalent to <code>1</code>). You end the function by writing the states in the <code>ledStates</code> to the pins in <code>ledPins</code>.</p>
<p>The <code>setup</code> function is pretty routine: you set the baud rate for serial communication, define input and output digital pins, and write a <code>LOW</code> to the digital output pins to ensure they are all turned off at the start.</p>
<p>In the <code>loop</code> function, you call the <code>getMoisturePercent</code> function to get the percent moisture value. You then print the percent value and the average analog reading to the Serial Monitor for clarity. Lastly, you call the <code>updateLEDBar</code> function with the percent value as a parameter to turn on the respective LEDs to indicate the moisture level.</p>
<h3 id="heading-test-the-system">Test the System</h3>
<p>You can proceed to test the example with different moisture levels. For example:</p>
<ul>
<li>At around a 37% moisture level, two LEDs are lit.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909854736/5312e441-d9fd-498f-bd87-b902231b27f4.jpeg" alt="Test example 1 with 37% moisture level" width="2106" height="738" loading="lazy"></p>
<ul>
<li>At about a 76% moisture level, four LEDs are lit.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909910096/eabf2f8d-7499-4c6a-be49-74d4c8a3da30.jpeg" alt="Test example 1 with 76% moisture level" width="2108" height="738" loading="lazy"></p>
<p>You can also simulate the example on Tinkercad here: <a target="_blank" href="https://www.tinkercad.com/embed/6s47ZvIrNOP">https://www.tinkercad.com/embed/6s47ZvIrNOP</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752100775640/12f92988-7697-4069-9068-86f663b20794.png" alt="Example 1 Tinkercad simulation image" class="image--center mx-auto" width="1366" height="615" loading="lazy"></p>
<h2 id="heading-example-2-how-to-determine-soil-moisture-state-from-digital-output">Example 2 – How to Determine Soil Moisture State from Digital Output</h2>
<p>In the previous project, you learned how to read the analog data from the sensor and convert the value into a percentage. If your application requires a binary output, you can use the digital output pin. In this section, you'll learn how to use the digital output pin of the sensor.</p>
<p>The digital output pin has only two states:</p>
<ul>
<li><p><code>LOW</code> – This state corresponds to 0V and is the output when the soil is wet, that is, the moisture level is above the set threshold.</p>
</li>
<li><p><code>HIGH</code> – This state corresponds to 5V and is the output when the soil is dry, that is, the moisture level is below the threshold moisture level.</p>
</li>
</ul>
<h3 id="heading-circuit-diagram-1">Circuit Diagram</h3>
<p>The example built in this section is very simple. It consists of a red and green LED. When the sensor outputs a <code>LOW</code> from the digital output pin, which means the soil is wet, the green LED turns on. When the sensor outputs a <code>HIGH</code> from the digital output pin, which means the soil is dry, the red LED turns on.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751909967743/761e3422-ad95-4f8e-9b4d-4963970c926e.jpeg" alt="Circuit diagram for example 2" width="1987" height="1826" loading="lazy"></p>
<p>Using the circuit diagram above as a reference, design the circuit as follows:</p>
<ol>
<li><p>Place the sensor module on the breadboard.</p>
</li>
<li><p>As in the previous project, connect the power (VCC) pin of the sensor module to digital pin 7 of the microcontroller, and the ground (GND) pin to a ground pin on the microcontroller.</p>
</li>
<li><p>Connect the digital output (DO) pin of the sensor to digital pin 2 of the microcontroller. This pin is where you will read the data from the sensor.</p>
</li>
<li><p>Connect the red and green LEDs each to a 220-ohm resistor in series.</p>
</li>
<li><p>Connect the anode of the red and green LEDs to digital pins 12 and 13 of the microcontroller, respectively.</p>
</li>
</ol>
<p>This is how my physical connection looked:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751910000801/55382b8f-cdd4-4f85-9b6a-342af252329b.jpeg" alt="Physical setup for example 2" width="1400" height="1100" loading="lazy"></p>
<h3 id="heading-how-to-set-the-threshold-moisture-level">How to Set the Threshold Moisture Level</h3>
<p>Before using the digital output pin of the soil sensor module, you first have to set a threshold moisture level.</p>
<p>The sensor's module has a built-in potentiometer that allows you to adjust the moisture level, which will be used as a threshold. The sensor also has a built-in LM393 comparator, which continuously compares the actual reading from the sensor to the threshold set by the potentiometer and outputs the appropriate state.</p>
<p>You can set the threshold moisture level by rotating the knob on the potentiometer. Rotating the potentiometer clockwise lowers the threshold. Rotating the potentiometer counterclockwise raises the threshold. The sensor module also has a built-in LED labeled <strong>DO-LED</strong> that turns on when the sensor output is <code>LOW</code>. Set the threshold as follows:</p>
<ol>
<li><p>Place the sensor's probe in soil just dry enough that it requires irrigation. That is the soil whose moisture level you want to use as a threshold. Note that the DO-LED should be off.</p>
</li>
<li><p>Use a small screwdriver to rotate the potentiometer clockwise until the DO-LED comes on. Turning clockwise reduces the threshold level until it is slightly lower than the current moisture level, thereby triggering the digital output indicator (DO-LED) to turn on.</p>
</li>
<li><p>Turn the screw slightly counterclockwise just enough to turn the built-in digital LED off. Given that the current moisture level of the soil is dry enough that it needs to be watered, you still want the sensor to read this level as dry. So turning counterclockwise reduces the threshold to a level just above the current moisture level of the soil, which triggers the DO-LED to turn off.</p>
</li>
</ol>
<h3 id="heading-arduino-code-1">Arduino Code</h3>
<p>Upload the following sketch to the Arduino:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> sensorPower = <span class="hljs-number">7</span>; <span class="hljs-comment">// Digital pin to power the sensor</span>
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> sensorPin = <span class="hljs-number">2</span>; <span class="hljs-comment">// Digital input from the sensor</span>
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> greenLED = <span class="hljs-number">13</span>; <span class="hljs-comment">// Green LED pin</span>
<span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> redLED = <span class="hljs-number">12</span>; <span class="hljs-comment">// Red LED pin</span>

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">getSensorReading</span><span class="hljs-params">(<span class="hljs-keyword">int</span> digitalPin, <span class="hljs-keyword">int</span> powerPin)</span> </span>{  
  digitalWrite(powerPin, HIGH); <span class="hljs-comment">// Power ON sensor</span>
  delay(<span class="hljs-number">500</span>); <span class="hljs-comment">// Wait for sensor to stabilize</span>

  <span class="hljs-keyword">int</span> reading = digitalRead(digitalPin);

  digitalWrite(powerPin, LOW); <span class="hljs-comment">// Power OFF sensor</span>
  <span class="hljs-keyword">return</span> reading;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);

  pinMode(sensorPower, OUTPUT);
  pinMode(sensorPin, INPUT);
  pinMode(greenLED, OUTPUT);
  pinMode(redLED, OUTPUT);

 <span class="hljs-comment">// Ensure everything starts OFF</span>
  digitalWrite(sensorPower, LOW);
  digitalWrite(greenLED, LOW);
  digitalWrite(redLED, LOW);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-keyword">int</span> sensorReading = getSensorReading(sensorPin, sensorPower);

  Serial.println(<span class="hljs-string">"==================================="</span>);
  Serial.print(<span class="hljs-string">"Digital Reading: "</span>);
  Serial.println(sensorReading);

  <span class="hljs-keyword">if</span> (sensorReading == HIGH) {
    Serial.println(<span class="hljs-string">"Status: Soil moisture is LOW (dry)"</span>);
    Serial.println(<span class="hljs-string">"Action: Water the soil"</span>);
    digitalWrite(redLED, HIGH);
    digitalWrite(greenLED, LOW);
 } <span class="hljs-keyword">else</span> {
    Serial.println(<span class="hljs-string">"Status: Soil moisture is GOOD (wet)"</span>);
    Serial.println(<span class="hljs-string">"Action: No watering needed"</span>);
    digitalWrite(greenLED, HIGH);
    digitalWrite(redLED, LOW);
 }

  Serial.println(<span class="hljs-string">"==================================="</span>);
  Serial.println(); 
  delay(<span class="hljs-number">2000</span>); <span class="hljs-comment">// Update every 2 seconds</span>
}
</code></pre>
<p>In the sketch, you define the digital pin used to read data from the sensor, the digital pins to power the sensor, and the green and red LEDs.</p>
<p>The <code>getSensorReading</code> function powers on the sensor, takes the digital reading from the sensor, powers off the sensor, and returns the digital reading taken from the sensor.</p>
<p>The <code>setup</code> function is routine: you set the baud rate, define the digital pins as input or output, and write a <code>LOW</code> to the output pins to ensure they are off at the start.</p>
<p>In the <code>loop</code> function, you call the <code>getSensorReading</code> to get the digital reading from the sensor. If the sensor outputs a <code>HIGH</code>, you turn on the red LED and print a message that the soil is dry. If the sensor outputs a <code>LOW</code>, you turn on the green LED and print a message that the soil is wet.</p>
<h3 id="heading-test-the-system-1">Test the System</h3>
<p>You can proceed to test the project using different moisture levels. For example:</p>
<ul>
<li>For wet soil, that is, if the moisture level is above the threshold, you should have the following output:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751910035826/19edc7e0-09a2-404a-8ce4-fb978c5b9f6b.jpeg" alt="Test example 2 with wet soil" width="2293" height="724" loading="lazy"></p>
<ul>
<li>For dry soil, that is, if the moisture level is below the threshold, you should have the following output:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751910068607/33881bb3-ba7b-41fd-a637-001016298312.jpeg" alt="Test example 2 with dry soil" width="2293" height="724" loading="lazy"></p>
<p>You can also simulate the example on Tinkercad here: <a target="_blank" href="https://www.tinkercad.com/embed/2wHwfKherNz">https://www.tinkercad.com/embed/2wHwfKherNz</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752100906816/cd1b3de4-f5e6-4760-ab03-8f46443ba270.png" alt="Example 2 Tinkercad simulation" class="image--center mx-auto" width="1366" height="611" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you learned what a resistive soil moisture sensor is, how to calibrate the sensor for your soil type, and how to use the sensor's analog and digital data to determine the moisture level of the soil.</p>
<p>The examples provided a solid foundation for working with the sensor. You can extend these examples into more complex projects, such as an automated irrigation system or remote monitoring of soil moisture.</p>
<p>For projects that require greater accuracy and frequent or sustained sensor operation, you should explore the capacitive sensor.</p>
<p>You can access all the code on <a target="_blank" href="https://github.com/michaelikoko/resistive-soil-moisture-sensor">GitHub</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Embedded Systems Firmware Basics – A Handbook for Developers ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever wondered how your fridge knows when to cool, or how a coffee machine knows when to stop pouring? Behind the scenes, these devices are powered by embedded systems – small, dedicated computers designed to perform specific tasks reliably a... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-embedded-systems-firmware-basics-handbook-for-devs/</link>
                <guid isPermaLink="false">6859c55cad0bcef0be044476</guid>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Firmware Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ sensors ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embeddedcourses ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ debugging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Soham Banerjee ]]>
                </dc:creator>
                <pubDate>Mon, 23 Jun 2025 21:21:32 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750701027343/86918e8c-4348-4845-b048-6203ae0fcb38.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever wondered how your fridge knows when to cool, or how a coffee machine knows when to stop pouring? Behind the scenes, these devices are powered by embedded systems – small, dedicated computers designed to perform specific tasks reliably and efficiently.</p>
<p>An embedded system typically goes through a simple but powerful cycle:</p>
<ol>
<li><p>Sense – Gather information from the environment using sensors.</p>
</li>
<li><p>Process – Use software logic to decide what to do with the data.</p>
</li>
<li><p>Act – Trigger a response, like turning on a motor or lighting an LED.</p>
</li>
</ol>
<p>Each project begins with a use case – a specific goal like brewing coffee or controlling a car’s fuel injection. From that, engineers define system requirements, which are split into:</p>
<ul>
<li><p>Hardware (for example, microcontrollers, sensors, actuators)</p>
</li>
<li><p>Software (what we call embedded software)</p>
</li>
</ul>
<p>This handbook focuses on the software side of embedded systems: how we write code to make embedded systems intelligent. Embedded software runs on resource-constrained devices like microcontrollers, which may have just a few kilobytes of memory. The software might need to be highly efficient, reliable, and often capable of working in real-time.</p>
<p>But embedded software isn't just about writing code – it’s also about understanding:</p>
<ul>
<li><p>How hardware works</p>
</li>
<li><p>How to manage memory and power</p>
</li>
<li><p>How to handle timing and communication</p>
</li>
<li><p>How to build robust, fail-safe systems</p>
</li>
</ul>
<p>While embedded systems development isn’t typically research-focused in most industry roles, it demands a broad skill set, from low-level programming to system-level design. What makes this field especially exciting is how it brings together diverse domains like machine learning, digital signal processing (DSP), and control systems, all of which can be applied directly in real-world devices.</p>
<p>In this article, I’ll give you:</p>
<ul>
<li><p>A high-level overview of what embedded software involves</p>
</li>
<li><p>Key concepts every developer should know</p>
</li>
<li><p>A tour of commonly used tools and frameworks</p>
</li>
<li><p>Resources to help you learn and understand basics.</p>
</li>
</ul>
<p>Whether you're just curious or planning a career in embedded systems, this guide is your launchpad.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-hw-layer-microcontroller">HW Layer: Microcontroller</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-firmware-design-and-tools">Firmware Design and Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tools-and-concepts-for-embedded-development">Tools and Concepts for Embedded Development</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-bare-metal-rtos-and-embedded-operating-systems">Bare Metal, RTOS, and Embedded Operating Systems</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-designing-drivers-for-embedded-systems">Designing Drivers for Embedded Systems</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-security-in-embedded-systems">Security in Embedded Systems</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-debugging-and-forensics-in-embedded-systems">Debugging and Forensics in Embedded Systems</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-automation-and-testing-in-embedded-systems">Automation and Testing in Embedded Systems</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-where-to-go-from-here">Where to Go from Here</a></p>
</li>
</ul>
<p>This article offers a broad overview of embedded firmware development, but it doesn’t cover every aspect, particularly advanced software architecture frameworks or comprehensive lists of open source software and tools. Where appropriate, I have included external resources that were valuable in expanding my own understanding.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>You don’t need to be an expert to follow this guide, but some prior knowledge will help you get the most out of it:</p>
<ul>
<li><p>Basic C or C++ programming**:** Familiarity with functions, pointers, and memory concepts is helpful.</p>
</li>
<li><p>Computer architecture fundamentals**:** Understanding what a CPU does, how memory works, and basic instruction execution will make embedded concepts clearer.</p>
</li>
<li><p>Electronics basics (optional)<strong>:</strong> Knowing how sensors, resistors, or microcontrollers interact at a circuit level is useful but not mandatory.</p>
</li>
<li><p>Comfort with the command line**:** Especially for working with build systems, compilers, and flashing tools.</p>
</li>
</ul>
<p>This guide is ideal for students, engineers, or hobbyists looking to deepen their understanding of how software interacts with hardware in real-world systems.</p>
<p>With that, let’s start from the ground up, hardware. Throughout this guide, most examples will reference ARM Cortex-M microcontrollers, as they are among the most commonly used in the embedded world.</p>
<h2 id="heading-hw-layer-microcontroller">HW Layer: Microcontroller</h2>
<p>One of the most important knowledge blocks in embedded firmware development is understanding how a microcontroller (MCU) works and how it connects to sensors, actuators, and other microcontrollers.</p>
<p>If you’re familiar with basic computer architecture (like instruction sets and memory organization), that knowledge translates well to embedded systems. In fact, Computer System Organization, often taught in computer science and electrical engineering programs, is a great foundation for understanding microcontrollers.</p>
<h3 id="heading-what-is-a-microcontroller">What is a Microcontroller?</h3>
<p>A microcontroller is a compact computing unit that includes:</p>
<ul>
<li><p>A CPU (Central Processing Unit or Microprocessor)</p>
</li>
<li><p>Memory (Flash and RAM)</p>
</li>
<li><p>Peripherals (for I/O, timers, communication, and so on)</p>
</li>
</ul>
<p>In essence, it's a tiny computer-on-a-chip, optimized for specific control tasks like reading sensors or driving motors.</p>
<p>By contrast, a microprocessor is just the CPU. It requires external memory and peripherals to function. Microcontrollers are self-contained and better suited for embedded applications.</p>
<p>For example, this <a target="_blank" href="https://www.st.com/resource/en/reference_manual/dm00031020-stm32f405-415-stm32f407-417-stm32f427-437-and-stm32f429-439-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf">reference manual</a> for the STM32F4 series (from STMicroelectronics) provides detailed documentation on not just the CPU but each peripheral’s functionality and the register map.</p>
<h3 id="heading-instruction-set-architecture-isa">Instruction Set Architecture (ISA)</h3>
<p>A microprocessor executes a series of instructions defined by its Instruction Set Architecture (ISA). ISA as defined by <a target="_blank" href="https://www.arm.com/glossary/isa">ARM</a> is a part of the abstract model of a computer that defines how the CPU is controlled by the software. The ISA acts as an interface between the hardware and the software, specifying both what the processor is capable of doing as well as how it gets done.</p>
<p>For example:</p>
<ul>
<li><p>ARMv7 – used in ARM Cortex-M3.</p>
</li>
<li><p>ARMv7E – used in Cortex-M4 and M7.</p>
</li>
</ul>
<p>Many vendors (for example, STMicroelectronics, NXP, TI) manufacture MCUs that support ARM ISAs but include their own peripheral sets. Understanding the ISA is essential for low-level coding and interpreting assembly instructions.</p>
<p>This <a target="_blank" href="https://developer.arm.com/documentation/ddi0403/ee/?lang=en">ARMv7-M architecture reference manual</a> provides more details on v7 Architecture.</p>
<h3 id="heading-memory-in-microcontrollers">Memory in Microcontrollers</h3>
<p>Most microcontrollers typically feature two types of memory:</p>
<ul>
<li><p><strong>Flash</strong> – Stores your code and read-only data.</p>
</li>
<li><p><strong>RAM</strong> – Used during program execution to hold:</p>
<ul>
<li><p>The heap (for dynamic memory)</p>
</li>
<li><p>The stack</p>
</li>
<li><p>The .data and .bss sections (initialized/uninitialized global/static variables)</p>
</li>
</ul>
</li>
</ul>
<p>Later sections have resources that go deeper into memory mapping and how these regions interact during runtime.</p>
<h3 id="heading-clock-and-power-management">Clock and Power Management</h3>
<p>Microcontrollers are digital logic devices built from:</p>
<ul>
<li><p>Combinatorial logic – Logic gates that evaluate outputs instantly</p>
</li>
<li><p>Sequential logic – Relies on clocks to move through states</p>
</li>
</ul>
<p>The clock tree distributes timing signals across the CPU and peripherals. MCUs often support multiple clock sources (internal RC, external crystal, PLL), and use prescalers to drive components at different frequencies.</p>
<p>For power-sensitive applications, MCUs offer multiple low-power modes:</p>
<ul>
<li><p>Sleep – CPU off, timers and peripherals are mostly active, memory is retained</p>
</li>
<li><p>Deep Sleep – CPU off, most clocks off, memory is retained, wake-up is slower than sleep, power consumption is lower than Sleep</p>
</li>
<li><p>Standby – CPU off, few interrupts are active, everything else is powered down, memory is not retained. Lowest power mode.</p>
</li>
</ul>
<p>These modes reduce power consumption by turning off clocks and disabling unused peripherals. Designing the system to switch in and out of low-power states effectively is a core skill in embedded software development.</p>
<p>This article talks about <a target="_blank" href="https://www.playembedded.org/blog/arm-cortex-clock-tree-101/">Clock Trees and Oscillators</a> for the ARM Cortex microcontrollers.</p>
<h3 id="heading-interrupts">Interrupts</h3>
<p>Interrupts let MCUs react to asynchronous events, like button presses or sensor signals.</p>
<p>An interrupt temporarily pauses normal code execution to run a dedicated handler. After it’s serviced, the CPU resumes its previous task. They are vital for:</p>
<ul>
<li><p>Fast event response</p>
</li>
<li><p>Reduced polling</p>
</li>
<li><p>Efficient power use (for example, waking from sleep)</p>
</li>
</ul>
<h3 id="heading-timers">Timers</h3>
<p>Timers are built-in peripherals used to track time or generate events.</p>
<p>Common uses are:</p>
<ul>
<li><p>Implementing software delays</p>
</li>
<li><p>Creating precise software timers</p>
</li>
<li><p>Waking up from low-power modes</p>
</li>
</ul>
<p>Mastering timers helps with real-time behavior and precise event scheduling.</p>
<h3 id="heading-communication-protocols">Communication Protocols</h3>
<p>Microcontrollers often need to talk to other devices via built-in communication peripherals:</p>
<ul>
<li><p><strong>UART (Universal Asynchronous Receiver/Transmitter):</strong> Serial communication between two devices, great for logs and debugging.</p>
</li>
<li><p><strong>I²C (Inter-Integrated Circuit):</strong> Two wire protocol for talking to sensors and EEPROMs.</p>
</li>
<li><p><strong>SPI (Serial Peripheral Interface):</strong> High Speed, full-duplex protocol for devices like Flash or displays.</p>
</li>
<li><p><strong>USB (Universal Serial Bus):</strong> Complex but widely used for PCs, data acquisition and HID devices.</p>
</li>
</ul>
<p>Here’s a figure showing multiple peripherals connected to a MCU:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750017729550/799b8649-bb39-4d5d-a309-9c3b76898eb8.png" alt="A MCU that is connected to Flash over SPI, connected to another MCU2 over UART, connected to Temperature Sensor over I2C and connected to Host Computer over USB. This picture shows how multiple peripherals are connected to a Host Computer" class="image--center mx-auto" width="2068" height="786" loading="lazy"></p>
<p>DMA or Direct Memory Access is an important peripheral which can be used to transfer data to/from memory without CPU involvement. It improves performance and allows the CPU to perform other tasks or enter low power mode to reduce power consumption.</p>
<p>This <a target="_blank" href="https://www.parlezvoustech.com/en/comparaison-protocoles-communication-i2c-spi-uart/">article</a> provides a good overview of the communication protocols I2C, UART and SPI.</p>
<p>We’ve now covered the essential building blocks of microcontroller hardware – from memory and clocks to interrupts and communication buses.</p>
<p>Next, we’ll explore the software principles and tools that bring these microcontrollers to life, including compilers, debuggers, and embedded development frameworks.</p>
<h2 id="heading-firmware-design-and-tools">Firmware Design and Tools</h2>
<h3 id="heading-designing-embedded-software">Designing Embedded Software</h3>
<p>Even though embedded systems operate under unique hardware constraints, software design principles are still crucial. Applying them thoughtfully becomes even more important when memory, CPU cycles, and responsiveness are limited.</p>
<p>Most Embedded firmware projects begin with a structured design approach:</p>
<ol>
<li><p>Understand the problem statement</p>
</li>
<li><p>List assumptions</p>
</li>
<li><p>Define use cases</p>
</li>
<li><p>Define system and software requirements</p>
</li>
<li><p>Create high-level architecture</p>
</li>
<li><p>Drill down to detailed design and implementation</p>
</li>
</ol>
<p>If you’re new to software design, check out my <a target="_blank" href="https://www.freecodecamp.org/news/learn-software-design-basics/">article</a> on design principles.</p>
<p>Here’s a figure showing the five blocks of software design:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750557879213/eab45a1f-ec1a-4c3d-81ce-c67365a451d4.png" alt="Blocks of software design: Problem statement describes the problem, Use cases describe the use case for which the problem statement is valid, then comes collecting the requirements, creating the architecture and the final design  " class="image--center mx-auto" width="1880" height="326" loading="lazy"></p>
<h3 id="heading-using-design-patterns">Using Design Patterns</h3>
<p>Once you're designing individual components, design patterns help you write scalable and maintainable code. Here are some common patterns in embedded systems:</p>
<ul>
<li><p>Publisher-Subscriber (Observer) – Useful for decoupling event producers and consumers (for example, sensor data being broadcast to multiple modules).</p>
</li>
<li><p>Singleton – Ensures only one instance of a module or resource manager exists (for example, for drivers or HAL layers).</p>
</li>
<li><p>Adapter – Translates between incompatible interfaces (for example, wrapping platform-specific code into a portable application layer).</p>
</li>
<li><p>State Machine – Represents system behavior as transitions between states (for example, Bluetooth states: <code>IDLE → SCANNING → CONNECTING → CONNECTED → DISCONNECTED</code>).</p>
</li>
</ul>
<p>Design patterns often need to be adapted for memory and timing constraints, but the core concepts remain highly relevant.</p>
<p>There are lot of great resources on design patterns – here are a few that helped me:</p>
<ol>
<li><p>Book: <a target="_blank" href="https://www.amazon.com/Head-First-Design-Patterns-Object-Oriented/dp/149207800X/">Head-first Design patterns</a> - A great book to get understand the concept of design patterns</p>
</li>
<li><p>Book: <a target="_blank" href="https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612/">Design Patterns: Elements of Reusable Object-Oriented Software</a></p>
</li>
<li><p>Course: <a target="_blank" href="https://www.freecodecamp.org/news/master-object-oriented-programming-and-design-patterns-in-c/">Object-Oriented Programming and Design Patterns in C#</a></p>
</li>
<li><p>Article on HSM: <a target="_blank" href="https://barrgroup.com/blog/introduction-hierarchical-state-machines">Hierarchical State Machine Overview (Barr Group)</a></p>
</li>
</ol>
<h3 id="heading-programming-languages-for-embedded-systems">Programming Languages for Embedded Systems</h3>
<p>While any language can theoretically be used if it compiles to machine code, in practice, three dominate the embedded world:</p>
<ul>
<li><p>C – The industry standard. Provides deterministic behavior and low-level access, making it ideal for memory and timing-sensitive code.</p>
</li>
<li><p>C++ – Adds object-oriented features while maintaining control. Once considered risky in embedded due to synthesized code and overhead, it’s now widely adopted where systems benefit from abstraction and modularity.</p>
</li>
<li><p>Rust – A memory-safe alternative gaining traction in safety-critical and open-source embedded development.</p>
</li>
</ul>
<p>Languages like Python (via MicroPython or CircuitPython) are used in educational or prototyping contexts but are not suitable for production due to performance and memory overhead.</p>
<p>Some resources on programming languages that might be helpful to understand concepts:</p>
<ol>
<li><p><a target="_blank" href="https://docs.rust-embedded.org/book/">The Embedded Rust Book</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/learn-c-programming-classic-book-dr-chuck/">C Programming Language by K&amp;R</a></p>
</li>
<li><p><a target="_blank" href="https://www.google.com/aclk?sa=L&amp;ai=DChcSEwi31JG8pvSNAxUpFa0GHX8lIoEYABAHGgJwdg&amp;co=1&amp;gclid=CjwKCAjw3rnCBhBxEiwArN0QE9cC5kuS7nAxauOzmDpkIoD63W3Ki8X0sTYfsUfrr8HYOdmqQQG5MBoCty4QAvD_BwE&amp;cce=1&amp;sig=AOD64_2a4D154E-aGKmSJlj_yP-RUq3HkQ&amp;ctype=5&amp;q=&amp;ved=2ahUKEwj_l428pvSNAxWaEzQIHb4eN3cQ9aACKAB6BAgLEA8&amp;adurl=">Inside the C++ Object model</a> – There are a lot of books and lectures on C++, but for embedded, understanding the object model benefits a lot.</p>
</li>
</ol>
<h3 id="heading-data-structures-matter">Data Structures Matter</h3>
<p>Embedded systems require careful data handling due to strict memory and timing constraints. Mastering core data structures is essential:</p>
<ul>
<li><p>Arrays – fixed-size data.</p>
</li>
<li><p>Linked Lists – Common in software timers, queues.</p>
</li>
<li><p>Stacks and Queues – Task scheduling, event management and data storage.</p>
</li>
<li><p>Bitfields/Flags – Memory efficient state representation.</p>
</li>
<li><p>Binary Trees – Used in routing tables or decision logic.</p>
</li>
</ul>
<p>You'll often build event queues, circular buffers, or timer lists, all of which rely on these foundational structures.</p>
<p>There are a lot of resources for understanding data structures, but I have found this one to be helpful for learning and practicing: <a target="_blank" href="https://www.geeksforgeeks.org/dsa/dsa-tutorial-learn-data-structures-and-algorithms/">GeeksForGeeks DSA Tutorial</a>. And <a target="_blank" href="https://www.freecodecamp.org/news/learn-data-structures-and-algorithms-2/">here’s a full course on DSA</a> if you want to dive deeper.</p>
<h3 id="heading-bit-manipulation-a-core-embedded-skill">Bit Manipulation: A Core Embedded Skill</h3>
<p>Unlike general-purpose software, embedded systems often require low-level access to registers and require precise bit control:</p>
<ul>
<li><p>Setting and clearing individual bits</p>
</li>
<li><p>Using bitwise operators like <code>AND (&amp;)</code>, <code>OR (|)</code>, <code>XOR (^)</code></p>
</li>
<li><p>Bit masking and shifting (<code>&lt;&lt;</code>, <code>&gt;&gt;</code>)</p>
</li>
</ul>
<p>Mastering bit hacks is essential for writing hardware drivers or manipulating control registers.</p>
<p>This resource provides a good number of examples for bit manipulation: <a target="_blank" href="https://graphics.stanford.edu/~seander/bithacks.html">Stanford Bit Hacks</a>.</p>
<h2 id="heading-tools-and-concepts-for-embedded-development">Tools and Concepts for Embedded Development</h2>
<h3 id="heading-cross-compilation">Cross Compilation</h3>
<p>Embedded code is compiled on a host (like your PC) for a target architecture using cross-compilers.</p>
<p>To do this, you need:</p>
<ul>
<li><p>A compiler (for example, <code>arm-none-eabi-gcc</code> for ARM Cortex-M) that compiles high level language code into Assembly language instructions.</p>
</li>
<li><p>A linker to layout and combine object files.</p>
</li>
<li><p>A Makefile or build system to organize and automate compilation, linking and binary creation.</p>
</li>
</ul>
<p>Here’s an example to compile a main.c to create a main.elf that can be flashed on the device:</p>
<pre><code class="lang-plaintext">arm-none-eabi-gcc main.c -o main.elf
</code></pre>
<p>A Makefile is a script used by the <code>make</code> build automation tool to compile and link programs to create a binary. It defines how to build your program from source files, manages compilation order based on dependencies and defines commands to complete the build.</p>
<p>For example, lets write a Makefile for building a project for an ARM Cortex-M4 target that has three source files: a main.c, utils.c, and sensor.c</p>
<pre><code class="lang-makefile">CC = arm-none-eabi-gcc
CFLAGS = -c -mcpu=cortex-m4 -mthumb -Wall -O2
LDFLAGS = -mcpu=cortex-m4 -mthumb
TARGET = main.elf
OBJS = main.o utils.o sensor.o
SRC = main.c utils.c sensor.c

<span class="hljs-variable">$(TARGET)</span>: <span class="hljs-variable">$(OBJS)</span>
    <span class="hljs-variable">$(CC)</span> <span class="hljs-variable">$(OBJS)</span> -o <span class="hljs-variable">$(TARGET)</span>

<span class="hljs-section">main.o: main.c</span>
    <span class="hljs-variable">$(CC)</span> <span class="hljs-variable">$(CFLAGS)</span> main.c

<span class="hljs-section">utils.o: utils.c</span>
    <span class="hljs-variable">$(CC)</span> <span class="hljs-variable">$(CFLAGS)</span> utils.c

<span class="hljs-section">sensor.o: sensor.c</span>
    <span class="hljs-variable">$(CC)</span> <span class="hljs-variable">$(CFLAGS)</span> sensor.c

<span class="hljs-section">clean:</span>
    rm -f *.o *.elf
</code></pre>
<p>In the above makefile, here’s a description of the flags:</p>
<ul>
<li><p><code>-mcpu=cortex-m4</code>: Targets the ARM Cortex-M4 processor.</p>
</li>
<li><p><code>-mthumb</code>: Enables Thumb instruction set, which is used by ARM Cortex-M series.</p>
</li>
<li><p><code>-Wall</code>: Enables all common warnings.</p>
</li>
<li><p><code>-O2</code>: Optimization level 2 for balance between performance and code size.</p>
</li>
</ul>
<p>Makefiles can seem intimidating, but they’re just scripts that define how to build your program from source. Once you understand the basics, they’re a huge productivity booster.</p>
<p>A linker script tells the linker (<code>ld</code>) how to organize the program in memory where to place code, data, stack, heap, and so on. It's crucial for embedded systems because you're working with limited memory and specific memory-mapped hardware.</p>
<p>Here’s an example of a simple linker script for a STM32F4 microcontroller:</p>
<pre><code class="lang-makefile">/* STM32F4 Cortex‑M4 Simple Linker Script */

ENTRY(Reset_Handler)

/* Define memory regions based on STM32F4 datasheet */
MEMORY
{
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
  RAM   (rwx): ORIGIN = 0x20000000, LENGTH = 128K
}

/* Section layout */
SECTIONS
{
  /* Interrupt vectors and code go into Flash */
  .isr_vector :
  {
    KEEP(*(.isr_vector))    /* Keep vector table (reset, etc.) */
  } &gt; FLASH

  .text :
  {
    *(.text*)               /* All code */
    *(.rodata*)             /* Read-only data */
    . = ALIGN(4)
    _etext = .             /* End of code (used for data init) */
  } &gt; FLASH

  /* Initialized data: load from Flash, run in RAM */
  .data : AT(_etext)
  {
    _sdata = .            /* Start of .data in RAM */
    *(.data*)
    . = ALIGN(4)
    _edata = .            /* End of .data */
  } &gt; RAM

  /* Uninitialized data (zero-filled) */
  .bss :
  {
    _sbss = .
    *(.bss*)
    *(COMMON)
    . = ALIGN(4)
    _ebss = .
  } &gt; RAM

  /* Define stack end (top of RAM) */
  _estack = ORIGIN(RAM) + LENGTH(RAM);
}
</code></pre>
<p>Descriptions of the above file:</p>
<ul>
<li><p>MEMORY: Defines your microcontroller’s memory layout – 1 MB Flash and 128 KB SRAM.</p>
</li>
<li><p>ENTRY(Reset_Handler): Sets the reset handler as the program entry point.</p>
</li>
<li><p>.isr_vector and **.**text: Code sections placed in Flash. <code>.isr_vector</code> must use <code>KEEP()</code> so it's not removed during linking.</p>
</li>
<li><p>.data : AT(_etext): Loads initialized variables from Flash but places them in RAM.</p>
</li>
<li><p>**.**bss: Zero-initialized data, allocated in RAM</p>
</li>
<li><p>_estack: Defines the initial stack pointer using the end of RAM.</p>
</li>
</ul>
<p>Here are some sources to understand Makefiles, cross-compilation, and Linkers. And just note that using Makefile in a project is the best way to learn and master Makefiles:</p>
<ol>
<li><p>Makefiles:</p>
<ul>
<li><p><a target="_blank" href="https://www.gnu.org/software/make/manual/make.pdf">GNU Make Manual</a></p>
</li>
<li><p><a target="_blank" href="https://makefiletutorial.com/">Makefile Tutorial</a></p>
</li>
<li><p><a target="_blank" href="https://www.gnu.org/software/make/manual/make.pdf">In Pyjama</a> <a target="_blank" href="https://inpyjama.com/post/makefile-2/">M</a><a target="_blank" href="https://makefiletutorial.com/">akef</a><a target="_blank" href="https://www.gnu.org/software/make/manual/make.pdf">ile Article</a></p>
</li>
</ul>
</li>
<li><p>Linker Scripts:</p>
<ul>
<li><p><a target="_blank" href="https://interrupt.memfault.com/blog/how-to-write-linker-scripts-for-firmware">Interrupt Blog on Linker Scripts</a></p>
</li>
<li><p><a target="_blank" href="https://medium.com/%40pc0is0me/an-introduction-to-linker-file-59ce2e9c5e73">Intro to Linker Files – Medium</a></p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-flashing-the-binary">Flashing the Binary</h3>
<p>Once you’ve compiled your code into a binary file, the next step is to <strong>flash</strong> it into the target microcontroller’s non-volatile memory via <strong>SWD</strong> (Serial Wire Debug) or <strong>JTAG</strong>. Flashing tools like OpenOCD, ST-Link, J-Link, or vendor-specific utilities manage this process.</p>
<h4 id="heading-what-is-flashing">What Is Flashing?</h4>
<p>Flashing is the process of writing a compiled firmware image (typically a <code>.bin</code> or <code>.hex</code> file) into the microcontroller’s Flash memory. This enables the embedded system to retain and run your code even after power is removed.</p>
<p>The flashing tool communicates with the microcontroller over SWD or JTAG to:</p>
<ul>
<li><p>Halt the MCU (if needed)</p>
</li>
<li><p>Access the internal flash controller</p>
</li>
<li><p>Erase the relevant flash sectors</p>
</li>
<li><p>Write the binary data to specific memory addresses</p>
</li>
<li><p>Verify that the data was written correctly</p>
</li>
</ul>
<p>OpenOCD (Open On-Chip Debugger) is a powerful, open-source utility that facilitates debugging and flashing of ARM-based microcontrollers. It supports a wide variety of hardware interfaces and microcontroller families, including STM32.</p>
<p>OpenOCD provides:</p>
<ul>
<li><p>Flashing capabilities for <code>.elf</code>, <code>.bin</code>, and <code>.hex</code> files</p>
</li>
<li><p>Debugging via GDB (GNU’s open source debugger) integration</p>
</li>
<li><p>Support for multiple debug probes (J-Link, ST-Link, CMSIS-DAP)</p>
</li>
<li><p>Scripting via configuration files for board-specific and target-specific setups</p>
</li>
</ul>
<p>A simple command to flash a binary using OpenOCD might look like this:</p>
<pre><code class="lang-makefile">bashCopyEditopenocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c <span class="hljs-string">"program main.elf verify reset exit"</span>
</code></pre>
<p>This tells OpenOCD to:</p>
<ul>
<li><p>Use the ST-Link interface</p>
</li>
<li><p>Load the STM32F4 target configuration</p>
</li>
<li><p>Program <code>main.elf</code> into flash</p>
</li>
<li><p>Verify it was written correctly</p>
</li>
<li><p>Reset the MCU</p>
</li>
<li><p>Exit the session</p>
</li>
</ul>
<p>For a detailed walkthrough, check out: <a target="_blank" href="https://kickstartembedded.com/2024/03/26/openocd-one-software-to-rule-debug-them-all/">OpenOCD Deep Dive – Kickstart Embedded</a></p>
<h2 id="heading-bare-metal-rtos-and-embedded-operating-systems">Bare Metal, RTOS, and Embedded Operating Systems</h2>
<p>When writing embedded software, you can approach the problem in three main ways, each with its own trade-offs:</p>
<ol>
<li><p>Bare-Metal Programming</p>
</li>
<li><p>Real-Time Operating Systems (RTOS) (like FreeRTOS, Zephyr)</p>
</li>
<li><p>Embedded Operating Systems (like Embedded Linux)</p>
</li>
</ol>
<p>The best choice depends on your use case, application’s complexity, hardware constraints, and real-time needs.</p>
<p>Most Modern 32-bit microcontrollers (for example, STM32, NXP, Renesas) come with vendor-provided development tools that include:</p>
<ul>
<li><p>HAL (Hardware Abstraction Layer) libraries</p>
</li>
<li><p>Startup code and linker scripts</p>
</li>
<li><p>Peripheral drivers</p>
</li>
<li><p>Sometimes even middleware like USB, BLE, or file system stacks</p>
</li>
</ul>
<p>These tools (like <a target="_blank" href="https://www.st.com/en/ecosystems/stm32cube.html">STM32Cube</a> Config Tools) simplify setup and peripheral configuration, helping you get started quickly, without needing to write low-level code manually.</p>
<p><strong>Benefits of HALs</strong>:</p>
<ul>
<li><p>Rapid prototyping and development</p>
</li>
<li><p>Clean, reusable APIs for peripherals</p>
</li>
<li><p>Great for onboarding and small teams</p>
</li>
</ul>
<p><strong>Drawbacks</strong>:</p>
<ul>
<li><p>Code bloat – HALs support many edge cases and configurations, which can inflate your binary size</p>
</li>
<li><p>Extra latency – HAL often inserts unnecessary layers that reduce performance.</p>
</li>
</ul>
<p>For performance-critical systems, developers often replace HAL drivers with custom, low-level implementations.</p>
<h3 id="heading-bare-metal-programming">Bare-Metal Programming</h3>
<p>Bare-metal programming is the most direct and lightweight approach. There’s no OS, and your code runs directly on the hardware with full control.</p>
<p>Typical setup includes:</p>
<ul>
<li><p>Include the correct header files, especially MCU and peripheral-specific headers provided by the vendor’s HAL (Hardware Abstraction Layer).</p>
</li>
<li><p>Implement a <code>main()</code> function with an infinite loop (<code>while(1)</code>)</p>
</li>
<li><p>Perform all hardware initialization before entering the loop</p>
</li>
<li><p>Use Interrupts to handle asynchronous events.</p>
</li>
<li><p>Continuously check and control inputs/outputs inside the loop</p>
</li>
</ul>
<p>This assumes your toolchain provides startup code and memory setup from the vendor.</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"MCU_Header.h"</span></span>

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span> </span>{
    <span class="hljs-comment">/* Initialize the MCU and the peripherals */</span>
    init_clock();
    init_peripherals();

    <span class="hljs-comment">/* runs in a loop forever */</span>
    <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>) {
        <span class="hljs-comment">// Task 1 : Read sensor data</span>
        read_sensor(); 
        <span class="hljs-comment">// Task 2 : Update the actuator based on the sensor data</span>
        update_actuator(); 
    }
}
</code></pre>
<h4 id="heading-how-does-it-run">How does it run?</h4>
<p>When the device powers on or resets, the startup code provided by the vendor is executed first. This code:</p>
<ul>
<li><p>Initializes the reset vector</p>
</li>
<li><p>Copies initialized data from Flash to RAM</p>
</li>
<li><p>Zeros out the <code>.bss</code> section (for uninitialized global/static variables)</p>
</li>
<li><p>Calls your <code>main()</code> function</p>
</li>
</ul>
<p>After calling <code>main()</code>, the system enters an infinite loop where your logic runs. The only other context switch occurs when an interrupt is triggered, briefly diverting control to an Interrupt Service Routine (ISR), after which it returns to the main loop.</p>
<p><strong>When to use it</strong>:</p>
<ul>
<li><p>Simpler applications (for example, blinking LEDs, reading sensors)</p>
</li>
<li><p>Ultra-low-power or ultra-low-latency needs</p>
</li>
<li><p>When every byte of Flash and RAM matters</p>
</li>
</ul>
<p><strong>Pros</strong>:</p>
<ul>
<li><p>Minimal memory usage</p>
</li>
<li><p>Maximum control</p>
</li>
<li><p>Great for learning</p>
</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li><p>No built-in task management or scheduling</p>
</li>
<li><p>Can become hard to maintain for complex systems</p>
</li>
</ul>
<p>This resource provides good details and example on <a target="_blank" href="https://github.com/cpq/bare-metal-programming-guide">Bare Metal Programming</a>. For more details, this book is great as well: <a target="_blank" href="https://umanovskis.se/files/arm-baremetal-ebook.pdf">ARM Baremetal Ebook</a>.</p>
<h3 id="heading-real-time-operating-systems-rtos">Real-Time Operating Systems (RTOS)</h3>
<p>A Real-Time Operating System (like <a target="_blank" href="https://www.freertos.org/Documentation/01-FreeRTOS-quick-start/01-Beginners-guide/00-Overview">FreeRTOS</a>, <a target="_blank" href="https://docs.zephyrproject.org/latest/">Zephyr</a>) adds lightweight multitasking capabilities to your embedded application. It allows you to split your software into independent tasks that run concurrently and communicate through queues, semaphores, or message passing.</p>
<p>RTOS kernels often support different scheduling strategies like:</p>
<ul>
<li><p>Rate Monotonic Scheduling (RMS) – Tasks with shorter periods get higher priority</p>
</li>
<li><p>Earliest Deadline First (EDF) – Tasks are prioritized based on impending deadlines</p>
</li>
</ul>
<p><strong>Example use cases</strong>:</p>
<ul>
<li><p>A drone where sensor data, motor control, and telemetry need to run in parallel</p>
</li>
<li><p>A medical device where timing is critical for safety</p>
</li>
<li><p>Rockets</p>
</li>
</ul>
<p><strong>Typical RTOS features</strong>:</p>
<ul>
<li><p>Task scheduling</p>
</li>
<li><p>Timers</p>
</li>
<li><p>Inter-task communication</p>
</li>
<li><p>Interrupt handling integration</p>
</li>
<li><p>Power management</p>
</li>
</ul>
<p><strong>Pros</strong>:</p>
<ul>
<li><p>Modular code structure with tasks</p>
</li>
<li><p>Easier to scale as complexity grows</p>
</li>
<li><p>Deterministic execution (when configured correctly)</p>
</li>
</ul>
<p><strong>Cons</strong>:</p>
<ul>
<li><p>Slightly higher memory footprint than bare-metal</p>
</li>
<li><p>Learning curve for scheduling and priority tuning</p>
</li>
</ul>
<p>RTOS Scheduling techniques are interesting – this part of the docs talks about <a target="_blank" href="https://docs.zephyrproject.org/latest/kernel/services/scheduling/index.html#scheduling-algorithm">Zephyr</a> scheduling.</p>
<h3 id="heading-embedded-operating-systems">Embedded Operating Systems</h3>
<p>Sometimes an embedded system is powerful enough to run a full-fledged OS like Embedded Linux, Android Things, or Windows IoT Core. This is common on devices with a display, networking stack, or file system.</p>
<p>It’s best used when the system requires multitasking, user interfaces, file systems, or network stacks, and when there’s plenty of processing power (for example, ARM Cortex-A).</p>
<p>Think of:</p>
<ul>
<li><p>Smart home hubs</p>
</li>
<li><p>Automotive infotainment</p>
</li>
<li><p>Industrial gateways</p>
</li>
</ul>
<p>This table provides a high level methodology for choosing the right type of OS based on your application:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Criteria</strong></td><td><strong>Bare Metal</strong></td><td><strong>RTOS</strong></td><td><strong>Embedded OS</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>System</strong> <strong>Complexity</strong></td><td>Low</td><td>Medium</td><td>High</td></tr>
<tr>
<td><strong>Memory</strong> <strong>Footprint</strong></td><td>Very Low</td><td>Moderate</td><td>High</td></tr>
<tr>
<td><strong>Real-Time Guarantees</strong></td><td>Limited</td><td>Yes</td><td>Depends on Kernel Design</td></tr>
<tr>
<td><strong>Learning Curve</strong></td><td>Steep for scaling</td><td>Moderate</td><td>Steeper (OS internals, tools)</td></tr>
<tr>
<td><strong>Use Case Examples</strong></td><td>Blinking LED, sensor polling</td><td>Drones, medical devices</td><td>Gateways, touchscreens</td></tr>
</tbody>
</table>
</div><p>To understand OS fundamentals, this is a great book: <a target="_blank" href="https://www.amazon.com/Operating-System-Concepts-Abraham-Silberschatz/dp/0470128720">Operating System Concepts</a> and this is a great course: <a target="_blank" href="https://www.youtube.com/playlist?list=PLF2K2xZjNEf97A_uBCwEl61sdxWVP7VWC">UC Berkeley: CS162</a>.</p>
<p>So far, we’ve looked at how embedded applications are structured, whether using bare-metal loops, RTOS multitasking, or full operating systems. But regardless of which execution model you choose, your software ultimately needs to interact with the hardware.</p>
<p>This is where driver development comes in. Drivers form the crucial link between your code and the peripherals it controls, whether it's reading temperature, blinking an LED, or transmitting data over SPI. Let’s take a closer look at how to design robust, portable drivers for embedded systems.</p>
<h2 id="heading-designing-drivers-for-embedded-systems">Designing Drivers for Embedded Systems</h2>
<p>When working with embedded software, one of the most practical and common tasks you’ll encounter is driver development.</p>
<p>A driver is a piece of software that enables the microcontroller (MCU) to interface with a hardware peripheral. This could be a temperature sensor, a motor controller, a display, or even a wireless module.</p>
<p>Drivers act as a bridge between your hardware and the application logic. They abstract away the raw register-level programming so that higher-level code can use clear function calls like <code>read_temperature()</code> or <code>start_motor()</code>.</p>
<h3 id="heading-what-goes-into-a-driver">What Goes Into a Driver?</h3>
<p>A typical embedded driver will include:</p>
<ul>
<li><p>Configuration – Setting up the peripheral with initial parameters (for example, baud rate for UART)</p>
</li>
<li><p>Initialization – Preparing the peripheral for use, including enabling clocks and interrupts</p>
</li>
<li><p>Calibration (if needed) – Adjusting the peripheral based on specific environment or use case</p>
</li>
<li><p>Register Access – Reading from and writing to hardware registers (if applicable)</p>
</li>
<li><p>Power Management – Enabling/disabling the peripheral to save power or putting the peripheral into a low power mode</p>
</li>
<li><p>Interrupt Management – Handling asynchronous events triggered by the peripheral</p>
</li>
</ul>
<p>Here’s a simplified view of a sensor driver API:</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sensor_init</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sensor_calibrate</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">sensor_read_temperature</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sensor_sleep</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sensor_write</span><span class="hljs-params">(<span class="hljs-keyword">uint8_t</span> reg, <span class="hljs-keyword">uint8_t</span> value)</span></span>; <span class="hljs-comment">// Assumption : 8 bit register address and 8 bit data value</span>
</code></pre>
<p>The actual implementation might involve:</p>
<ul>
<li><p>Register definitions from the peripheral’s datasheet</p>
</li>
<li><p>Bit manipulations for control and status registers</p>
</li>
<li><p>Interrupt Service Routines (ISRs)</p>
</li>
<li><p>Timing and delay management</p>
</li>
</ul>
<h3 id="heading-platform-abstraction-why-it-matters">Platform Abstraction: Why It Matters</h3>
<p>One of the most important principles in driver design is decoupling the application from the platform. This makes your code easier to:</p>
<ul>
<li><p>Port to different MCUs</p>
</li>
<li><p>Adapt for similar hardware (for example, different sensor models)</p>
</li>
<li><p>Test across simulated or real environments</p>
</li>
</ul>
<h4 id="heading-platform-agnostic-design-example-in-c">Platform-Agnostic Design Example (in C++) :</h4>
<p>Let’s say you're writing a driver for a temperature sensor:</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// Abstracts the HW platform on which the sensor driver is being written</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TemperatureSensorPlatform</span> {</span>
<span class="hljs-keyword">public</span>:
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">i2cInit</span><span class="hljs-params">(<span class="hljs-keyword">void</span>)</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">i2cWrite</span><span class="hljs-params">(<span class="hljs-keyword">uint8_t</span> reg, <span class="hljs-keyword">uint8_t</span> value)</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">uint8_t</span> <span class="hljs-title">i2cRead</span><span class="hljs-params">(<span class="hljs-keyword">uint8_t</span> reg)</span></span>;
};

<span class="hljs-comment">// Creates a generic Temperature sensor driver interface</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TemperatureSensor</span> {</span>
<span class="hljs-keyword">public</span>:
    <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">init</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;
    <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">float</span> <span class="hljs-title">read</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;
    <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-keyword">void</span> <span class="hljs-title">sleep</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;
};
</code></pre>
<p>You can implement this interface differently for a specific type of temperature sensor and also add the platform support for the HW platform you are writing the driver on for example STM32.</p>
<pre><code class="lang-cpp"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TempSensorTMP117</span> :</span> <span class="hljs-keyword">public</span> TemperatureSensor {
<span class="hljs-keyword">public</span>:

    TempSensorTMP117(TemperatureSensorPlatform platform) : 
    _platform(platform)
    TemperatureSensor()
    {}

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">init</span><span class="hljs-params">()</span> <span class="hljs-keyword">override</span> </span>{
        <span class="hljs-comment">// TMP117-specific register configuration</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">read</span><span class="hljs-params">()</span> <span class="hljs-keyword">override</span> </span>{
        <span class="hljs-comment">// Read ADC value and convert</span>
        <span class="hljs-keyword">return</span> <span class="hljs-number">25.4f</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sleep</span><span class="hljs-params">()</span> <span class="hljs-keyword">override</span> </span>{
        <span class="hljs-comment">// Put sensor in low-power mode</span>
    }
<span class="hljs-keyword">private</span>:
    TemperatureSensorPlatform _platform; <span class="hljs-comment">// Implements the I2C driver for STM32</span>
};
</code></pre>
<p>Your application code now depends on the <code>TemperatureSensor</code> interface and Temperature Sensor Platform passed in the constructor making it portable and testable across temperature sensors and HW platforms.</p>
<p>One of my previous <a target="_blank" href="https://www.freecodecamp.org/news/connect-read-process-sensor-data-on-microcontrollers-for-beginners/">articles</a> provides details on how to interface a sensor and how to design a driver for it.</p>
<p>Designing robust and modular drivers helps your firmware interact seamlessly with hardware, but in today’s connected world, that’s only part of the challenge. As embedded devices increasingly communicate with other systems, security becomes just as critical as functionality.</p>
<p>Now that we’ve covered how to interface with hardware, let’s explore how to protect those systems from unauthorized access, tampering, and data breaches.</p>
<h2 id="heading-security-in-embedded-systems">Security in Embedded Systems</h2>
<p>Security is often overlooked in embedded development but it shouldn’t be. Embedded systems are increasingly connected to networks, cloud services, or other devices, which makes them vulnerable to attacks like unauthorized access, firmware tampering, or data leaks.</p>
<p>Even simple devices like smart plugs or fitness trackers can be exploited if their firmware is insecure.</p>
<h3 id="heading-key-security-practices">Key Security Practices</h3>
<ul>
<li><p><strong>Secure Boot:</strong> Ensure the firmware is cryptographically signed and verified before execution. This prevents unauthorized firmware from running.</p>
</li>
<li><p><strong>Firmware Update Integrity:</strong> Use encrypted or signed updates, especially for Over-the-Air (OTA) upgrades. Unprotected updates can be a major attack vector.</p>
</li>
<li><p><strong>Lock Debug Interfaces:</strong> After flashing the final firmware, disable or lock access to JTAG, SWD, or UART debug ports to prevent reverse engineering.</p>
</li>
<li><p><strong>Minimal Exposure:</strong> Disable unused peripherals (for example, Bluetooth, USB, network interfaces) and avoid exposing debug info (like UART prints) in production.</p>
</li>
<li><p><strong>Watchdog Timers:</strong> While not security features per se, watchdogs help ensure system recovery in the event of unexpected software behavior – which could result from attacks or bugs.</p>
</li>
</ul>
<p>Security should be layered, as no single mechanism is sufficient on its own. Build security into every stage of the development process, from boot to communication to update handling.</p>
<p>Whether you're designing a consumer product or an industrial controller, proactive security practices are essential for protecting user data, system reliability, and device reputation.</p>
<p>This resource provides a good understanding of Embedded Systems Security: <a target="_blank" href="https://blackberry.qnx.com/en/ultimate-guides/embedded-system-security">BlackBerry QNX: Embedded System Security Guide</a></p>
<h2 id="heading-debugging-and-forensics-in-embedded-systems">Debugging and Forensics in Embedded Systems</h2>
<p>Debugging embedded systems is one of the most challenging and fascinating aspects of development. Unlike in desktop or web applications, bugs in embedded systems often manifest as unexpected hardware behavior rather than error messages.</p>
<p>For example, suppose your code is supposed to blink an LED once per second:</p>
<ul>
<li><p>If the LED stays on, your delay code might be broken.</p>
</li>
<li><p>If it blinks erratically, you might have a timing bug.</p>
</li>
<li><p>If it doesn’t blink at all, you might never be reaching that part of your code or the hardware might not be configured correctly.</p>
</li>
</ul>
<h3 id="heading-why-debugging-is-critical">Why Debugging is Critical</h3>
<p>Embedded systems directly control real-world hardware, often in critical or safety-sensitive environments. A small bug can lead to large consequences.</p>
<p>Historical Note: During the Apollo 11 moon landing, the onboard computer started throwing alarms due to a task overflow. The system restarted and was able to recover itself and allowing the mission to continue safely.</p>
<p>Debugging and post-mortem analysis (forensics) are essential skills for embedded developers.</p>
<h3 id="heading-common-debugging-tools-and-techniques">Common Debugging Tools and Techniques</h3>
<h4 id="heading-1-print-statements-uart-logging">1. Print Statements (UART Logging)</h4>
<p>The simplest and most common method. They send debug messages over a serial connection (UART).</p>
<p>You can use <code>printf()</code> or similar to track variable values, function entries/exits, and system state</p>
<ul>
<li><p>Pros: Easy to implement</p>
</li>
<li><p>Cons: Can affect timing – not usable if UART is unavailable or disabled</p>
</li>
</ul>
<h4 id="heading-2-trace-variables">2. Trace Variables</h4>
<p>In systems without output peripherals (like UART), you can use trace flags, setting bits in a global variable to indicate code progress.</p>
<pre><code class="lang-c"><span class="hljs-keyword">uint32_t</span> trace_flags = <span class="hljs-number">0</span>;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">init_sensor</span><span class="hljs-params">()</span> 
</span>{
    trace_flags |= (<span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">0</span>); <span class="hljs-comment">// Bit 0: sensor init started</span>
    <span class="hljs-comment">// ...</span>
    trace_flags |= (<span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">1</span>); <span class="hljs-comment">// Bit 1: sensor init complete</span>
}
</code></pre>
<p>You can then examine <code>trace_flags</code> in memory to track execution flow, even post-mortem. The trace flags can be printed out or dumped via lldb or gdb.</p>
<p><strong>3. Hardware Debugging: JTAG, SWD, and Debuggers</strong></p>
<p>Modern microcontrollers (like ARM Cortex-Ms) support hardware debugging interfaces such as:</p>
<ul>
<li><p>JTAG (Joint Test Action Group)</p>
</li>
<li><p>SWD (Serial Wire Debug)</p>
</li>
</ul>
<p>These allow a debugger to:</p>
<ul>
<li><p>Pause execution</p>
</li>
<li><p>Set breakpoints</p>
</li>
<li><p>Inspect and modify memory</p>
</li>
<li><p>Single-step through code</p>
</li>
</ul>
<p><a target="_blank" href="https://developer.arm.com/documentation/102520/0100">ARM CoreSight</a> is a debug and trace architecture developed by ARM for its processor cores (like Cortex-M, Cortex-A, Cortex-R). It provides a set of hardware modules built into ARM-based chips that allow developers to:</p>
<ul>
<li><p>Debug the system while it's running (non-intrusively)</p>
</li>
<li><p>Trace code execution, memory accesses, and peripheral activity</p>
</li>
<li><p>Analyze system performance and find hard-to-catch bugs</p>
</li>
</ul>
<p>In short: CoreSight lets you look inside your embedded system while it's alive and working, without halting it unnecessarily.</p>
<h3 id="heading-why-coresight-exists">Why CoreSight Exists</h3>
<p>Traditional debugging tools (like breakpoints or single-stepping with JTAG) are often intrusive (they pause the system), limited (can't capture what happened right before a crash), or not suitable for real-time systems.</p>
<p>CoreSight solves these by enabling real-time tracing and non-intrusive observation of what's happening inside the chip.</p>
<h4 id="heading-popular-debug-tools">Popular Debug Tools:</h4>
<ul>
<li><p>ST-Link – HW from STMicrocontrollers</p>
</li>
<li><p>J-Link – Universal debugger supporting a wide range of MCUs</p>
</li>
<li><p>OpenOCD – Open-source interface for hardware debugging</p>
</li>
<li><p>GDB / LLDB – Command-line debuggers used alongside the above</p>
</li>
</ul>
<p>Single-stepping is most effective when compiler optimizations are off. With optimization, code might be reordered, inlined, or even eliminated.</p>
<h3 id="heading-4-using-map-and-disassembly-files">4. Using Map and Disassembly Files</h3>
<p>When debugging complex issues, especially crashes or memory overflows, you'll need to go deeper.</p>
<p>Map Files show the layout of functions and variables in memory (Flash and RAM). They help you locate:</p>
<ul>
<li><p>Stack overflows</p>
</li>
<li><p>Unexpected memory usage</p>
</li>
<li><p>Function addresses</p>
</li>
</ul>
<p>Disassembly Files let you see the machine code generated from your source. This is critical when:</p>
<ul>
<li><p>Code is heavily optimized</p>
</li>
<li><p>You’re diagnosing instruction-level failures</p>
</li>
<li><p>You’re working without source code (e.g., binary-only drivers)</p>
</li>
</ul>
<p>This resource provides a good overview on Map files, linkers and ELF format: <a target="_blank" href="https://www.tenouk.com/ModuleW.html">Tenouk’s ELF/Map/Linker Guide</a></p>
<h3 id="heading-common-bug-buffer-overflows">Common Bug: Buffer Overflows</h3>
<p>Buffer overflows are one of the most frequent (and dangerous) issues in embedded systems. They happen when data is written past the end of an allocated array, overwriting nearby memory and causing unpredictable behavior.</p>
<p>Symptoms:</p>
<ul>
<li><p>Code crashes mysteriously</p>
</li>
<li><p>Data appears to “corrupt itself”</p>
</li>
<li><p>Variables change value without explanation</p>
</li>
</ul>
<p>You can learn more in my article on <a target="_blank" href="https://www.freecodecamp.org/news/how-to-debug-and-prevent-buffer-overflows-in-embedded-systems/">Debugging Buffer Overflows</a>, which walks through ways to debug a buffer overflow and build robust buffer code.</p>
<h3 id="heading-embedded-forensics">Embedded Forensics</h3>
<p>Sometimes, a device fails in the field, where you can’t attach a debugger. That’s where forensics comes in:</p>
<ul>
<li><p>Use watchdog timers to reset the system and log failure info</p>
</li>
<li><p>Save crash signatures to non-volatile memory (for example, EEPROM, Flash)</p>
</li>
<li><p>Implement assert handlers that log file names, line numbers, or fault types</p>
</li>
</ul>
<p>These techniques help you reconstruct what went wrong after the device has rebooted or been recovered.</p>
<p>You can learn more here: <a target="_blank" href="https://medium.com/@lanceharvieruntime/debugging-techniques-for-embedded-systems-94d00582074a">Debugging Techniques for Embedded Systems – Medium</a>.</p>
<p>Debugging and forensics are invaluable when something goes wrong – but a robust system should aim to catch issues before they reach deployment.</p>
<p>That’s where automated testing becomes essential. With embedded software increasingly powering critical applications, the ability to run consistent, repeatable tests across hardware configurations saves time, improves reliability, and enables faster development cycles.</p>
<p>Next, let’s explore how embedded testing works, the challenges unique to hardware, and how automation frameworks help streamline validation.</p>
<h2 id="heading-automation-and-testing-in-embedded-systems">Automation and Testing in Embedded Systems</h2>
<p>Like all other areas of software engineering, testing is essential in embedded systems. But testing embedded software comes with its own set of challenges, mainly because it interacts with hardware.</p>
<p>Manual testing can be time-consuming and resource-intensive, especially when tests need to be repeated for multiple firmware versions or configurations. That’s where automated testing becomes invaluable.</p>
<h3 id="heading-why-automated-testing">Why Automated Testing?</h3>
<p>Automated testing helps:</p>
<ul>
<li><p>Catch regressions early</p>
</li>
<li><p>Test edge cases consistently</p>
</li>
<li><p>Reduce human error</p>
</li>
<li><p>Scale testing across versions and hardware setups</p>
</li>
</ul>
<p>But automating tests for embedded systems isn’t just writing test cases – it’s about setting up an infrastructure that connects your code to the physical hardware under test.</p>
<h3 id="heading-test-architecture-host-dut">Test Architecture: Host + DUT</h3>
<p>Most embedded test setups involve two components:</p>
<ul>
<li><p>Host: Your development PC or CI test controller, which sends test commands and receives data.</p>
</li>
<li><p>DUT (Device Under Test): The microcontroller board or embedded system running the firmware.</p>
</li>
</ul>
<p>These two communicate over a physical link, commonly USB, UART, or FTDI, which carries commands and test data between them.</p>
<h4 id="heading-diagram-suggested-structure">Diagram (suggested structure)</h4>
<p>You could visualize this as:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749953253453/4a94ae37-dd17-4be1-aece-d1c2bee0248d.png" alt="Describes the flow of automation, Automation Manager on the host that takes CSV and Config Files and is the control center of Automation. Automation Manager on the DUT helps parse commands coming from host and provide replies to the host, the automation manager on the DUT will forward queries to different modules in the DUT for actions and queries. The communication protocol between Host and DUT is over USB or UART over FTDI" class="image--center mx-auto" width="1910" height="444" loading="lazy"></p>
<h3 id="heading-key-components-of-embedded-test-automation">Key Components of Embedded Test Automation</h3>
<h4 id="heading-1-file-management">1. <strong>File Management</strong></h4>
<p>Many automated tests rely on <strong>CSV or JSON files</strong> to define:</p>
<ul>
<li><p>Input configurations</p>
</li>
<li><p>Expected outputs</p>
</li>
<li><p>Test parameters</p>
</li>
</ul>
<p>Python makes it easy to:</p>
<ul>
<li><p>Read input vectors from CSVs</p>
</li>
<li><p>Write logs or pass/fail results</p>
</li>
<li><p>Parse structured data</p>
</li>
</ul>
<h4 id="heading-2-data-communication">2. <strong>Data Communication</strong></h4>
<p>Maintaining a stable and reliable link between the Host and DUT is critical. This includes:</p>
<ul>
<li><p>Opening and managing UART or USB connections (for example, with <code>pyserial</code>)</p>
</li>
<li><p>Framing test commands using opcodes or simple protocols</p>
</li>
<li><p>Handling timeouts, retries, and error recovery</p>
</li>
</ul>
<h5 id="heading-example-python-with-pyserial">Example (Python with PySerial):</h5>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> serial

ser = serial.Serial(<span class="hljs-string">'/dev/ttyUSB0'</span>, <span class="hljs-number">115200</span>) <span class="hljs-comment">#set Baud rate</span>
ser.write(<span class="hljs-string">b'\x01'</span>)  <span class="hljs-comment"># Send opcode for "start test"</span>
response = ser.read(<span class="hljs-number">64</span>)  <span class="hljs-comment"># Read 64 bytes of response</span>
</code></pre>
<h4 id="heading-3-automation-manager-dut-side">3. <strong>Automation Manager (DUT-side)</strong></h4>
<p>A lightweight software agent runs on the embedded device. Its responsibilities:</p>
<ul>
<li><p>Parse incoming commands</p>
</li>
<li><p>Trigger specific test routines</p>
</li>
<li><p>Send response data back to the host</p>
</li>
</ul>
<p>This is often implemented using a <code>switch-case</code> structure in <code>C</code> or <code>C++</code>:</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">automation_manager</span><span class="hljs-params">(<span class="hljs-keyword">uint8_t</span> opcode)</span> </span>{
    <span class="hljs-keyword">switch</span>(opcode) {
        <span class="hljs-keyword">case</span> <span class="hljs-number">0x01</span>: run_sensor_test(); <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-number">0x02</span>: run_motor_test(); <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">default</span>: <span class="hljs-keyword">break</span>;
    }
}
</code></pre>
<h4 id="heading-4-automation-manager-host-side">4. <strong>Automation Manager (Host-side)</strong></h4>
<p>This is the control center of your test workflow:</p>
<ul>
<li><p>Sends test commands and parameters to the DUT</p>
</li>
<li><p>Waits for and logs results</p>
</li>
<li><p>Compares responses to expected output</p>
</li>
<li><p>Handles communication retries or failures</p>
</li>
</ul>
<p>Often written in Python using:</p>
<ul>
<li><p><code>pyserial</code> for communication</p>
</li>
<li><p><code>pandas</code> for file/data processing</p>
</li>
<li><p><code>unittest</code> or <code>pytest</code> for test structure</p>
</li>
</ul>
<h3 id="heading-tips-for-effective-automation">Tips for Effective Automation</h3>
<ul>
<li><p>Use unique opcodes for each test command to avoid ambiguity</p>
</li>
<li><p>Implement timeout handling to avoid hanging scripts</p>
</li>
<li><p>Log everything, responses, errors, test timestamps</p>
</li>
<li><p>Use versioned test input files to track changes over time</p>
</li>
<li><p>Include self-tests on the DUT to validate hardware state before running full tests</p>
</li>
</ul>
<p>Automated testing in embedded systems is not just about running scripts, it's about building a bridge between your host PC and your device, managing the flow of commands and data, and ensuring tests are consistent, repeatable, and reliable.</p>
<p>While this requires effort to set up, the payoff is huge: confidence in your firmware, faster development cycles, and reduced risk of bugs making it into production.</p>
<h2 id="heading-where-to-go-from-here">Where to Go from Here</h2>
<h3 id="heading-building-your-embedded-project">Building your Embedded Project</h3>
<p>After exploring the theory and tooling of embedded systems, it's time to apply what you've learned. This section walks you through the steps to create your own embedded system – from concept to code and deployment.</p>
<p>Use the checklist below to guide your first project, whether you're prototyping a sensor device or automating a simple process.</p>
<h4 id="heading-project-setup-checklist">Project Setup Checklist:</h4>
<ol>
<li><p><strong>Define the Goal</strong></p>
<ul>
<li><p>What task does the system perform?</p>
</li>
<li><p>Identify inputs (for example, temperature sensor) and outputs (for example, relay or LED).</p>
</li>
</ul>
</li>
<li><p><strong>Requirements Gathering</strong></p>
<ul>
<li><p>Functional: What features must it support?</p>
</li>
<li><p>Non-functional: Memory limits, real-time behavior, power constraints.</p>
</li>
<li><p>Any security or safety-critical elements?</p>
</li>
</ul>
</li>
<li><p><strong>Choose Your Hardware</strong></p>
<ul>
<li><p>Microcontroller (for example, STM32F4)</p>
</li>
<li><p>Sensors and actuators</p>
</li>
<li><p>Communication interfaces (UART, I2C, SPI, and so on)</p>
</li>
</ul>
</li>
<li><p><strong>Software Architecture</strong></p>
<ul>
<li><p>Bare-metal, RTOS, or embedded OS?</p>
</li>
<li><p>Driver abstraction: will you use HAL or custom low-level code?</p>
</li>
<li><p>Organize code into layers: application logic, drivers, hardware init.</p>
</li>
</ul>
</li>
<li><p><strong>Toolchain Setup</strong></p>
<ul>
<li><p>Install GCC toolchain (for example, <code>arm-none-eabi-gcc</code>)</p>
</li>
<li><p>Configure Makefile and linker script</p>
</li>
<li><p>Set up debugger and flashing tools (for example, OpenOCD, ST-Link)</p>
</li>
</ul>
</li>
<li><p><strong>Firmware Implementation</strong></p>
<ul>
<li><p>Initialize peripherals</p>
</li>
<li><p>Implement control logic inside <code>main()</code> or tasks</p>
</li>
<li><p>Use interrupts or timers for responsiveness</p>
</li>
</ul>
</li>
<li><p><strong>Flashing and Initial Tests</strong></p>
<ul>
<li><p>Use OpenOCD or ST-Link to flash the binary</p>
</li>
<li><p>Test peripheral behavior and debug with UART or GDB</p>
</li>
</ul>
</li>
<li><p><strong>Debug and Profile</strong></p>
<ul>
<li><p>Use JTAG/SWD, CoreSight, and trace logs</p>
</li>
<li><p>Check memory layout with map/disassembly files</p>
</li>
<li><p>Identify bottlenecks and edge cases</p>
</li>
</ul>
</li>
<li><p><strong>Security Hardening</strong></p>
<ul>
<li><p>Disable debug interfaces post-flash</p>
</li>
<li><p>Add firmware signing and secure boot</p>
</li>
<li><p>Minimize surface area: disable unused features</p>
</li>
</ul>
</li>
<li><p><strong>Testing and Automation</strong></p>
</li>
</ol>
<ul>
<li><p>Connect Host to DUT via UART/USB</p>
</li>
<li><p>Use Python + PySerial to send test vectors</p>
</li>
<li><p>Log, compare, and report test outcomes</p>
</li>
</ul>
<p>Embedded firmware development is a deep and rewarding field where software meets the hardware. Whether you're controlling an LED, reading from a sensor, or orchestrating multiple tasks in real time, the embedded stack teaches you how hardware, software, timing, and efficiency all come together.</p>
<h2 id="heading-summary">Summary:</h2>
<p>In this guide, we walked through the essential building blocks at a high level:</p>
<ul>
<li><p>What embedded systems are, and how they sense → process → act</p>
</li>
<li><p>How microcontrollers work, from memory layout to interrupts and protocols</p>
</li>
<li><p>How to design robust, scalable embedded software with clean architecture</p>
</li>
<li><p>When to choose bare-metal, RTOS, or full OS solutions</p>
</li>
<li><p>How to build drivers, write modular code, and interface with peripherals</p>
</li>
<li><p>Tools for debugging, tracing, and analyzing system behavior</p>
</li>
<li><p>Strategies for automating embedded testing using Python and host-device communication</p>
</li>
<li><p>And finally, why security matters, especially in a connected world</p>
</li>
</ul>
<p>Whether you're preparing for embedded job interviews, building your own IoT projects, or just exploring how software drives real-world systems, this article gives you a launchpad for deeper learning.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Debug and Prevent Buffer Overflows in Embedded Systems ]]>
                </title>
                <description>
                    <![CDATA[ Buffer overflows are one of the most serious software bugs, especially in embedded systems, where hardware limitations and real-time execution make them hard to detect and fix. A buffer overflow happens when a program writes more data into a buffer t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-debug-and-prevent-buffer-overflows-in-embedded-systems/</link>
                <guid isPermaLink="false">67d84f228d156200bc7d3d8c</guid>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded ]]>
                    </category>
                
                    <category>
                        <![CDATA[ memory-management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Buffer Overfow ]]>
                    </category>
                
                    <category>
                        <![CDATA[ debugging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Firmware Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Code Quality ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learn to code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming basics ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Coding Best Practices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ clean code ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Soham Banerjee ]]>
                </dc:creator>
                <pubDate>Mon, 17 Mar 2025 16:34:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742229245130/858b21cc-443e-43ee-82ce-091438f6c5c0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Buffer overflows are one of the most serious software bugs, especially in embedded systems, where hardware limitations and real-time execution make them hard to detect and fix.</p>
<p>A buffer overflow happens when a program writes more data into a buffer than it was allocated, leading to memory corruption, crashes, or even security vulnerabilities. A buffer corruption occurs when unintended modifications overwrite unread data or modify memory in unexpected ways.</p>
<p>In safety-critical systems like cars, medical devices, and spacecraft, buffer overflows can cause life-threatening failures. Unlike simple software bugs, buffer overflows are unpredictable and depend on the state of the system, making them difficult to diagnose and debug.</p>
<p>To prevent these issues, it's important to understand how buffer overflows and corruptions occur, and how to detect and fix them.</p>
<h2 id="heading-article-scope">Article Scope</h2>
<p>In this article, you will learn:</p>
<ol>
<li><p>What buffers, buffer overflows, and corruptions are. I’ll give you a beginner-friendly explanation with real-world examples.</p>
</li>
<li><p>How to debug buffer overflows. You’ll learn how to use tools like GDB, LLDB, and memory maps to find memory corruption.</p>
</li>
<li><p>How to prevent buffer overflows. We’ll cover some best practices like input validation, safe memory handling, and defensive programming.</p>
</li>
</ol>
<p>I’ll also show you some hands-on code examples – simple C programs that demonstrate buffer overflow issues and how to fix them.</p>
<p>What this article doesn’t cover:</p>
<ol>
<li><p>Security exploits and hacking techniques. We’ll focus on preventing accidental overflows, not hacking-related buffer overflows.</p>
</li>
<li><p>Operating system-specific issues. This guide is for embedded systems, not general-purpose computers or servers.</p>
</li>
<li><p>Advanced RTOS memory management. While we discuss interrupt-driven overflows, we won’t dive deep into real-time operating system (RTOS) concepts.</p>
</li>
</ol>
<p>Now that you know what this article covers (and what it doesn’t), let’s go over the skills that will help you get the most out of it.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This article is designed for developers who have some experience with C programming and want to understand how to debug and prevent buffer overflows in embedded systems. Still, beginners can follow along, as I’ll explain key concepts in a clear and structured way.</p>
<p>Before reading, it helps if you know:</p>
<ol>
<li><p>Basic C programming.</p>
</li>
<li><p>How memory works – the difference between stack, heap, and global variables.</p>
</li>
<li><p>Basic debugging concepts – if you’ve used a debugger like GDB or LLDB, that’s a plus, but not required.</p>
</li>
<li><p>What embedded systems are – a basic idea of how microcontrollers store and manage memory.</p>
</li>
</ol>
<p>Even if you’re not familiar with these topics, this guide will walk you through them in an easy-to-understand way.</p>
<p>Before you dive into buffer overflows, debugging, and prevention, let’s take a step back and understand what a buffer is and why it’s important in embedded systems. Buffers play a crucial role in managing data flow between hardware and software but when handled incorrectly, they can lead to serious software failures.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-buffer-and-how-does-it-work">What is a Buffer, and How Does it Work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-a-buffer-overflow">What is a Buffer Overflow?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-causes-of-buffer-overflows-and-corruption">Common Causes of Buffer Overflows and Corruption</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-consequences-of-buffer-overflows">Consequences of Buffer Overflows</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-debug-buffer-overflows">How to Debug Buffer Overflows</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-prevent-buffer-overflows">How to Prevent Buffer Overflows</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-buffer-and-how-does-it-work">What is a Buffer, and How Does it Work?</h2>
<p>A buffer is a contiguous block of memory used to temporarily store data before it is processed. Buffers are commonly used in two scenarios:</p>
<ol>
<li><p>Data accumulation: When the system needs to collect a certain amount of data before processing.</p>
</li>
<li><p>Rate matching: When the data producer generates data faster than the data consumer can process it.</p>
</li>
</ol>
<p>Buffers are typically implemented as arrays in C, where elements are indexed from 0 to N-1 (where N is the buffer size).</p>
<p>Let’s look at an example of a buffer in a sensor system.</p>
<p>Consider a system with a sensor task that generates data at 400 Hz (400 samples per second or 1 sample every 2.5 ms). But the data processor (consumer) operates at only 100 Hz (100 samples per second or 1 sample every 10 ms). Since the consumer task is slower than the producer, we need a buffer to store incoming data until it is processed.</p>
<p>To determine the buffer size, we calculate:</p>
<p>Buffer Size = Time to consume 1 sample / Time to generate 1 sample = 10 ms/ 2.5 ms = 4</p>
<p>This means the buffer must hold at least 4 samples at a time to avoid data loss.</p>
<p>Once the buffer reaches capacity, there are several strategies to decide which data gets passed to the consumer task:</p>
<ol>
<li><p>Max/min sampling: Use the maximum or minimum value in the buffer.</p>
</li>
<li><p>Averaging: Compute the average of all values in the buffer.</p>
</li>
<li><p>Random access: Pick a sample from a specific location (for example, the most recent or the first).</p>
</li>
</ol>
<p>In real-world applications, it’s beneficial to use circular buffers or double buffering to prevent data corruption.</p>
<ul>
<li><p>Circular buffer approach: A circular buffer (also called a ring buffer) continuously wraps around when it reaches the end, ensuring old data is overwritten safely without exceeding memory boundaries. The buffer size should be multiplied by 2 (4 × 2 = 8) to hold 8 samples. This allows the consumer task to process 4 samples while the next 4 samples are being filled, preventing data overwrites.</p>
</li>
<li><p>Double buffer approach: Double buffering is useful when data loss is unacceptable. It allows continuous data capture while the processor is busy handling previous data. A second buffer of the same size is added. When the first buffer is full, the write pointer switches to the second buffer, allowing the consumer task to process data from the first buffer while the second buffer is being filled. This prevents data overwrites and ensures a continuous data flow.</p>
</li>
</ul>
<p>Buffers help manage data efficiently, but what happens when they are mismanaged? This is where buffer overflows and corruptions come into play.</p>
<h2 id="heading-what-is-a-buffer-overflow">What is a Buffer Overflow?</h2>
<p>A buffer overflow occurs when a program writes more data into a buffer than it was allocated, causing unintended memory corruption. This can lead to unpredictable behavior, ranging from minor bugs to critical system failures.</p>
<p>To understand buffer overflow, let's use a simple analogy. Imagine a jug with a tap near the bottom. The jug represents a buffer, while the tap controls how much liquid (data) is consumed.</p>
<p>The jug is designed to hold a fixed amount of liquid. As long as water flows into the jug at the same rate or slower than it flows out, everything works fine. But if water flows in faster than it flows out, the jug will eventually overflow.</p>
<p>Similarly, in software, if data enters a buffer faster than it is processed, it exceeds the allocated memory space, causing a buffer overflow. In the case of a circular buffer, this can cause the write pointer to wrap around and overwrite unread data, leading to buffer corruption.</p>
<h3 id="heading-buffer-overflows-in-software">Buffer Overflows in Software</h3>
<p>Unlike the jug, where water simply spills over, a buffer overflow in software overwrites adjacent memory locations. This can cause a variety of hard-to-diagnose issues, including:</p>
<ol>
<li><p>Corrupting other data stored nearby.</p>
</li>
<li><p>Altering program execution, leading to crashes.</p>
</li>
<li><p>Security vulnerabilities, where attackers exploit overflows to inject malicious code.</p>
</li>
</ol>
<p>When a buffer overflow occurs, data can overwrite variables, function pointers, or even return addresses, depending on where the buffer is allocated.</p>
<p>Buffer overflows can occur in different memory regions:</p>
<ol>
<li><p>Buffer overflows in global/static memory (.bss / .data sections)</p>
<ul>
<li><p>These occur when global or static variables exceed their allocated size.</p>
</li>
<li><p>The overflow can corrupt adjacent variables, leading to unexpected behavior in other modules.</p>
</li>
<li><p>Debugging is easier because memory addresses are fixed at compile time unless the compiler optimizes them. Map files provide a memory layout of variables during the compilation and linking.</p>
</li>
</ul>
</li>
<li><p>Stack-based buffer overflow (more predictable, easier to debug):</p>
<ul>
<li><p>Happens when a buffer is allocated in the stack (for example, local variables inside functions).</p>
</li>
<li><p>Overflowing the stack can affect adjacent local variables or return addresses, potentially crashing the program.</p>
</li>
<li><p>In embedded systems with small stack sizes, this often leads to a crash or execution of unintended code.</p>
</li>
</ul>
</li>
<li><p>Heap-based buffer overflow (harder to debug):</p>
<ul>
<li><p>Happens when a buffer is dynamically allocated in the heap (for example, using malloc() in C).</p>
</li>
<li><p>Overflowing a heap buffer can corrupt adjacent dynamically allocated objects or heap management structures.</p>
</li>
<li><p>Debugging is harder because heap memory is allocated dynamically at runtime, causing memory locations to vary.</p>
</li>
</ul>
</li>
</ol>
<h4 id="heading-buffer-overflow-vs-buffer-corruption">Buffer Overflow vs Buffer Corruption</h4>
<p>Buffer overflow and buffer corruption are of course related, but refer to different situations.</p>
<p>A buffer overflow happens when data is written beyond the allocated buffer size, leading to memory corruption, unpredictable behavior, or system crashes.</p>
<p>A buffer corruption happens when unintended data modifications result in unexpected software failures, even if the write remains within buffer boundaries.</p>
<p>Both issues typically result from poor write pointer management, lack of boundary checks, and unexpected system behavior.</p>
<p>Now that we've covered what a buffer overflow is and how it can overwrite memory, let’s take a closer look at how these issues affect embedded systems.</p>
<p>In the next section, we’ll explore how buffer overflows and corruption happen in real-world embedded systems and break down common causes, including pointer mismanagement and boundary violations.</p>
<h2 id="heading-common-causes-of-buffer-overflows-and-corruption">Common Causes of Buffer Overflows and Corruption</h2>
<p>Embedded systems use buffers to store data from sensors, communication interfaces (like UART (Universal Asynchronous Receiver-Transmitter), SPI (Serial Peripheral Interface), I2C (Inter-integrated Circuit), and real-time tasks. These buffers are often statically allocated to avoid memory fragmentation, and many implementations use circular (ring) buffers to efficiently handle continuous data streams.</p>
<p>Here are three common scenarios where buffer overflows or corruptions occur in embedded systems:</p>
<h3 id="heading-writing-data-larger-than-the-available-space">Writing Data Larger Than the Available Space</h3>
<p><strong>Issue</strong>: The software writes incoming data to the buffer without checking if there is enough space.</p>
<p><strong>Example</strong>: Imagine a 100-byte buffer to store sensor data. The buffer receives variable-sized packets. If an incoming packet is larger than the remaining space, it will overwrite adjacent memory, leading to corruption.</p>
<p>So why does this happen?</p>
<ul>
<li><p>Some embedded designs increment the write pointer after copying data, making it too late to prevent overflow.</p>
</li>
<li><p>Many low-level memory functions (memcpy, strcpy, etc.) do not check buffer boundaries, leading to unintended writes.</p>
</li>
<li><p>Without proper bound checking, a large write can exceed the buffer size and corrupt nearby memory.</p>
</li>
</ul>
<p>Here’s a code sample to demonstrate buffer overflow in a .bss / .data section:</p>
<pre><code class="lang-c">  <span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdint.h&gt;</span></span>
  <span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
  <span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string.h&gt;</span></span>

  <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> BUFFER_SIZE 300</span>

  <span class="hljs-keyword">static</span> <span class="hljs-keyword">uint16_t</span> sample_count = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">uint8_t</span> buffer[BUFFER_SIZE] = {<span class="hljs-number">0</span>};

  <span class="hljs-comment">// Function to simulate a buffer overflow scenario</span>
  <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">updateBufferWithData</span><span class="hljs-params">(<span class="hljs-keyword">uint8_t</span> *data, <span class="hljs-keyword">uint16_t</span> size)</span>
  </span>{
      <span class="hljs-comment">// Simulating a buffer overflow: No boundary check!</span>
      <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Attempting to write %d bytes at position %d...\n"</span>, size, sample_count);

      <span class="hljs-comment">// Deliberate buffer overflow for demonstration</span>
      <span class="hljs-keyword">if</span> (sample_count + size &gt; BUFFER_SIZE)
      {
          <span class="hljs-built_in">printf</span>(<span class="hljs-string">"WARNING: Buffer Overflow Occurred! Writing beyond allocated memory!\n"</span>);
      }

      <span class="hljs-comment">// Copy data (unsafe, can cause overflow)</span>
      <span class="hljs-built_in">memcpy</span>(&amp;buffer[sample_count], data, size);

      <span class="hljs-comment">// Increment sample count (incorrectly, leading to wraparound issues)</span>
      sample_count += size;
  }

  <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>
  </span>{   
      <span class="hljs-comment">// Save 1 byte to buffer</span>
      <span class="hljs-keyword">uint8_t</span> data_to_buffer = <span class="hljs-number">10</span>;
      updateBufferWithData(&amp;data_to_buffer, <span class="hljs-number">1</span>);

      <span class="hljs-comment">// Save an array of 20 bytes to buffer</span>
      <span class="hljs-keyword">uint8_t</span> data_to_buffer_1[<span class="hljs-number">20</span>] = {<span class="hljs-number">5</span>};
      updateBufferWithData(data_to_buffer_1, <span class="hljs-keyword">sizeof</span>(data_to_buffer_1));

      <span class="hljs-comment">// Intentional buffer overflow: Save an array of 50 x 8 bytes (400 bytes)</span>
      <span class="hljs-keyword">uint64_t</span> data_to_buffer_2[<span class="hljs-number">50</span>] = {<span class="hljs-number">7</span>};
      updateBufferWithData((<span class="hljs-keyword">uint8_t</span>*)data_to_buffer_2, <span class="hljs-keyword">sizeof</span>(data_to_buffer_2));

      <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
  }
</code></pre>
<h3 id="heading-interrupt-driven-overflows-real-time-systems">Interrupt-Driven Overflows (Real-time Systems)</h3>
<p><strong>Issue</strong>: The interrupt service routine (ISR) may write data faster than the main task can process, leading to buffer corruption or buffer overflow if the write pointer is not properly managed.</p>
<p><strong>Example</strong>: Imagine a sensor ISR that writes incoming data into a buffer every time a new reading arrives. Meanwhile, a low-priority processing task reads and processes the data.</p>
<p>What can go wrong?</p>
<ul>
<li><p>If the ISR triggers too frequently (due to a misbehaving sensor or high interrupt priority), the buffer may fill up faster than the processing task can keep up.</p>
</li>
<li><p>This can result in one of two failures:</p>
<ol>
<li><p>Buffer Corruption: The ISR overwrites unread data, leading to loss of information.</p>
</li>
<li><p>Buffer Overflow: The ISR exceeds buffer boundaries, causing memory corruption or system crashes.</p>
</li>
</ol>
</li>
</ul>
<p>So why does this happen?</p>
<ul>
<li><p>In real-time embedded systems, ISR execution preempts lower-priority tasks.</p>
</li>
<li><p>If the processing task doesn't not get enough CPU time, the buffer may become overwritten or overflow beyond its allocated scope.</p>
</li>
</ul>
<h3 id="heading-system-state-changes-amp-buffer-corruption">System State Changes &amp; Buffer Corruption</h3>
<p><strong>Issue</strong>: The system may unexpectedly reset, enter low-power mode, or changes operating state, leaving the buffer write pointers in an inconsistent state. This can result in buffer corruption (stale or incorrect data) or buffer overflow (writing past the buffer’s limits.</p>
<p><strong>Example Scenarios</strong>:</p>
<ol>
<li><p>Low-power wake-up issue (Buffer Overflow risk): Some embedded systems enter deep sleep to conserve energy. Upon waking up, if the buffer write pointer is not correctly reinitialized, it may point outside buffer boundaries, leading to buffer overflow and unintended memory corruption.</p>
</li>
<li><p>Unexpected mode transitions: If a sensor task is writing data and the system suddenly switches modes, the buffer states and pointers may not be cleaned up. The next time the sensor task runs, it may continue writing without clearing previous data. This can cause undefined behavior due to presence of stale data.</p>
</li>
</ol>
<p>Now that you understand how buffer overflows and corruptions happen, let’s examine their consequences in embedded systems ranging from incorrect sensor readings to complete system failures, making debugging and prevention critical.</p>
<h2 id="heading-consequences-of-buffer-overflows">Consequences of Buffer Overflows</h2>
<p>Buffer overflows can be catastrophic in embedded systems, leading to system crashes, data corruption, and unpredictable behavior. Unlike general-purpose computers, many embedded devices lack memory protection, making them particularly vulnerable to buffer overflows.</p>
<p>A buffer overflow can corrupt two critical types of memory:</p>
<h3 id="heading-1-data-variables-corruption">1. Data Variables Corruption</h3>
<p>A buffer overflow can overwrite data variables, corrupting the inputs for other software modules. This can cause unexpected behavior or even system crashes if critical parameters are modified.</p>
<p>For example, a buffer overflow could accidentally overwrite a sensor calibration value stored in memory. As a result, the system would start using incorrect sensor readings, leading to faulty operation and potentially unsafe conditions.</p>
<h3 id="heading-2-function-pointer-corruption">2. Function Pointer Corruption</h3>
<p>In embedded systems, function pointers are often used for interrupt handlers, callback functions, and RTOS task scheduling. If a buffer overflow corrupts a function pointer, the system may execute unintended instructions, leading to a crash or unexpected behavior.</p>
<p>As an example, a function pointer controlling motor speed regulation could be overwritten. Instead of executing the correct function, the system would jump to a random memory address, causing a system fault or erratic motor behavior.</p>
<p>Buffer overflows are among the hardest bugs to identify and fix because their effects depend on which data is corrupted and the values it contains. A buffer overflow can affect memory in different ways:</p>
<ul>
<li><p>If a buffer overflow corrupts unused memory, the system may seem fine during testing, making the issue harder to detect.</p>
</li>
<li><p>if a buffer overflow alters critical data variables, it can cause hidden logic errors that cause unpredictable behavior.</p>
</li>
<li><p>If a buffer overflow corrupts function pointers, it may crash immediately, making the problem easier to identify.</p>
</li>
</ul>
<p>During development, if tests focus only on detecting crashes, they may overlook silent memory corruption caused by a buffer overflow. In real-world deployments, new use cases not covered in testing can trigger previously undetected buffer overflow issues, leading to unpredictable failures.</p>
<p>Buffer overflows can cause a chain reaction, where one overflow leads to another overflow or buffer corruption, resulting in widespread system failures. So how does this happen?</p>
<ol>
<li><p>A buffer overflow corrupts a critical variable (for example, a timer interval).</p>
</li>
<li><p>The corrupted variable disrupts another module (for example, triggers the timer interrupt too frequently, causing it to push more data into a buffer than intended.).</p>
</li>
<li><p>This increased interrupt frequency forces a sensor task to write data faster than intended, eventually causing another buffer overflow or corruption by overwriting unread data.</p>
</li>
</ol>
<p>This chain reaction can spread across multiple software modules, making debugging nearly impossible. In real-word applications, buffer overflows in embedded systems can be life-threatening:</p>
<ul>
<li><p>In cars: A buffer overflow in an ECU (Electronic Control Unit) could cause brake failure or unintended acceleration.</p>
</li>
<li><p>In a spacecraft: A memory corruption issue could disable navigation systems, leading to mission failure.</p>
</li>
</ul>
<p>Now that we’ve seen how buffer overflows can corrupt memory, disrupt system behavior, and even cause critical failures, the next step is understanding how to detect and fix them before they lead to serious issues.</p>
<h2 id="heading-how-to-debug-buffer-overflows">How to Debug Buffer Overflows</h2>
<p>Debugging buffer overflows in embedded systems can be complex, as their effects range from immediate crashes to silent data corruption, making them difficult to trace. A buffer overflow can cause either:</p>
<ol>
<li><p>A system crash, which is easier to detect since it halts execution or forces a system reboot.</p>
</li>
<li><p>Unexpected behavior, which is much harder to debug as it requires tracing how corrupted data affects different modules.</p>
</li>
</ol>
<p>This section focuses on embedded system debugging techniques using memory map files, debuggers (GDB/LLDB), and a structured debugging approach. Let’s look into the debuggers and memory map files.</p>
<h3 id="heading-memory-map-file-map-file">Memory Map File (.map file)</h3>
<p>A memory map file is generated during the linking process. It provides a memory layout of global/static variables, function addresses, and heap/stack locations. It provides a memory layout of Flash and RAM, including:</p>
<ul>
<li><p>Text section (.text): Stores executable code.</p>
</li>
<li><p>Read-only section (.rodata): Stores constants and string literals.</p>
</li>
<li><p>BSS section (.bss): Stores uninitialized global and static variables.</p>
</li>
<li><p>Data section (.data): Stores initialized global and static variables.</p>
</li>
<li><p>Heap and stack locations, depending on the linker script.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739064875727/1e01992d-4d9d-42fb-b971-6f4e92452c22.png" alt="Figure 1: A visual of the memory layout" class="image--center mx-auto" width="1256" height="425" loading="lazy"></p>
<p>If a buffer overflow corrupts a global variable, the .map file can identify nearby variables that may also be affected, provided the compiler has not optimized the memory allocation. Similarly, if a function pointer is corrupted, the .map file can reveal where it was stored in memory.</p>
<h3 id="heading-debuggers-gdb-amp-lldb">Debuggers (GDB &amp; LLDB)</h3>
<p>Debugging tools like GDB (GNU Debugger) and LLDB (LLVM Debugger) allow:</p>
<ul>
<li><p>Controlling execution (breakpoints, stepping through code).</p>
</li>
<li><p>Inspecting variable values and memory addresses.</p>
</li>
<li><p>Getting backtraces (viewing function calls before a crash).</p>
</li>
<li><p>Extracting core dumps from microcontrollers for post-mortem analysis.</p>
</li>
</ul>
<p>If the system halts on a crash, a backtrace (bt command in GDB) can reveal which function was executing before failure. If the overflow affects a heap-allocated variable, GDB can inspect heap memory usage to detect corruption.</p>
<h3 id="heading-the-debugging-process">The Debugging Process</h3>
<p>Now, let’s go through a step-by-step debugging process to identify and fix buffer overflows. Once a crash or unexpected behavior occurs, follow these techniques to trace the root cause:</p>
<h4 id="heading-step-1-identify-the-misbehaving-module">Step 1: Identify the misbehaving module</h4>
<p>If the system crashes, use GDB or LLDB backtrace (bt command) to locate the last executed function. If the system behaves unexpectedly, determine which software module controls the affected functionality.</p>
<h4 id="heading-step-2-analyze-inputs-and-outputs-of-the-module">Step 2: Analyze inputs and outputs of the module</h4>
<p>Every function or module has inputs and outputs. Create a truth table listing expected outputs for all possible inputs. Check if the unexpected behavior matches any undefined input combination, which may indicate corruption.</p>
<h4 id="heading-step-3-locate-memory-corruption-using-address-analysis">Step 3: Locate memory corruption using address analysis</h4>
<p>If a variable shows incorrect values, determine its physical memory location. Depending on where the variable is stored:</p>
<ol>
<li><p>Global/static variables (.bss / .data): Look up the memory map file for nearby buffers.</p>
</li>
<li><p>Heap variables: Snapshot heap allocations using GDB.  </p>
<p> Here’s an example of using GDB to find corrupted variables:</p>
<pre><code class="lang-c"> (gdb) print &amp;my_variable  # Get memory address of the variable
 $<span class="hljs-number">1</span> = (<span class="hljs-keyword">int</span> *) <span class="hljs-number">0x20001000</span>
 (gdb) x/<span class="hljs-number">10</span>x <span class="hljs-number">0x20001000</span>   # Examine memory near <span class="hljs-keyword">this</span> address, Display <span class="hljs-number">10</span> memory words in hexadecimal format starting from <span class="hljs-number">0x20001000</span>
</code></pre>
</li>
</ol>
<h4 id="heading-step-4-identify-the-overflowing-buffer">Step 4: Identify the overflowing buffer</h4>
<p>If a buffer is located just before the corrupted variable, inspect its usage in the code. Review all possible code paths that write to the buffer. Check if any design limitations could cause an overflow under a specific use cases.</p>
<h4 id="heading-step-5-fix-the-root-cause">Step 5: Fix the root cause</h4>
<p>If the buffer overflow happened due to missing bounds checks, add proper input validation to prevent it. Buffer design should enforce strict memory limits. The module should implement strict boundary checks for all inputs and maintain a consistent state.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739065828677/74322607-5997-4275-87d0-b3d0acf54373.png" alt="Figure 2: Steps to debug a buffer overflow" class="image--center mx-auto" width="1105" height="325" loading="lazy"></p>
<p>In addition to GDB/LLDB, you can also use techniques like hardware tracing and fault injection to simulate buffer overflows and observe system behavior in real-time.</p>
<p>While debugging helps identify and fix buffer overflows, prevention is always the best approach. Let’s explore techniques that can help avoid buffer overflows altogether.</p>
<h2 id="heading-how-to-prevent-buffer-overflows">How to Prevent Buffer Overflows</h2>
<p>You can often prevent buffer overflows through good software design, defensive programming, hardware protections, and rigorous testing. Embedded systems, unlike general-purpose computers, often lack memory protection mechanisms, which means that buffer overflow prevention critical for system reliability and security.</p>
<p>Here are some key techniques to help prevent buffer overflows:</p>
<h3 id="heading-defensive-programming">Defensive Programming</h3>
<p>Defensive programming helps minimize buffer overflow risks by ensuring all inputs are validated and unexpected conditions are handled safely.</p>
<p>First, it’s crucial to validate input size before writing to a buffer. Always check the write index by adding the size of data to be written prior to writing data to make sure more data is not written than the available buffer space.</p>
<p>Then you’ll want to make sure you have proper error handling and fail-safe mechanisms in place. If an input is invalid, halt execution, log the error, or switch to a safe state. Also, functions should indicate success/failure with helpful error codes to prevent misuse.</p>
<p>Sample Code:</p>
<pre><code class="lang-c">   <span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdint.h&gt;</span></span>
   <span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string.h&gt;</span></span>
   <span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdbool.h&gt;</span></span>
   <span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>

   <span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> BUFFER_SIZE 300</span>

   <span class="hljs-keyword">static</span> <span class="hljs-keyword">uint16_t</span> sample_count = <span class="hljs-number">0</span>;
   <span class="hljs-keyword">static</span> <span class="hljs-keyword">uint8_t</span> buffer[BUFFER_SIZE] = {<span class="hljs-number">0</span>};

   <span class="hljs-keyword">typedef</span> <span class="hljs-keyword">enum</span>
   {
       SUCCESS = <span class="hljs-number">0</span>,
       NOT_ENOUGH_SPACE = <span class="hljs-number">1</span>,
       DATA_IS_INVALID = <span class="hljs-number">2</span>,
   } buffer_err_code_e;


   <span class="hljs-function">buffer_err_code_e <span class="hljs-title">updateBufferWithData</span><span class="hljs-params">(<span class="hljs-keyword">uint8_t</span> *data, <span class="hljs-keyword">uint16_t</span> size)</span>
   </span>{
       <span class="hljs-keyword">if</span> (data == <span class="hljs-literal">NULL</span> || size == <span class="hljs-number">0</span> || size &gt; BUFFER_SIZE)  
       {
           <span class="hljs-keyword">return</span> DATA_IS_INVALID; <span class="hljs-comment">// Invalid input size</span>
       }

       <span class="hljs-keyword">uint16_t</span> available_space = BUFFER_SIZE - sample_count;
       <span class="hljs-keyword">bool</span> can_write = (available_space &gt;= size) ? <span class="hljs-literal">true</span> : <span class="hljs-literal">false</span>;

       <span class="hljs-keyword">if</span> (!can_write)  
       {
           <span class="hljs-keyword">return</span> NOT_ENOUGH_SPACE;
       }

       <span class="hljs-comment">// Copy data safely</span>
       <span class="hljs-built_in">memcpy</span>(&amp;buffer[sample_count], data, size);
       sample_count += size;

       <span class="hljs-keyword">return</span> SUCCESS;
   }

   <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>
   </span>{   
       buffer_err_code_e ret;

       <span class="hljs-comment">// Save 1 byte to buffer</span>
       <span class="hljs-keyword">uint8_t</span> data_to_buffer = <span class="hljs-number">10</span>;
       ret = updateBufferWithData(&amp;data_to_buffer, <span class="hljs-keyword">sizeof</span>(data_to_buffer));
       <span class="hljs-keyword">if</span> (ret)  
       {
           <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Buffer update didn't succeed, Err:%d\n"</span>, ret);
       }

       <span class="hljs-comment">// Save an array of 20 bytes to buffer</span>
       <span class="hljs-keyword">uint8_t</span> data_to_buffer_1[<span class="hljs-number">20</span>] = {<span class="hljs-number">5</span>};
       ret = updateBufferWithData(data_to_buffer_1, <span class="hljs-keyword">sizeof</span>(data_to_buffer_1));
       <span class="hljs-keyword">if</span> (ret)  
       {
           <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Buffer update didn't succeed, Err:%d\n"</span>, ret);
       }

       <span class="hljs-comment">// Save an array of 50 x 8 bytes, Intentional buffer overflow</span>
       <span class="hljs-keyword">uint64_t</span> data_to_buffer_2[<span class="hljs-number">50</span>] = {<span class="hljs-number">7</span>};
       ret = updateBufferWithData((<span class="hljs-keyword">uint8_t</span>*)data_to_buffer_2, <span class="hljs-keyword">sizeof</span>(data_to_buffer_2));  
       <span class="hljs-keyword">if</span> (ret)  
       {
           <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Buffer update didn't succeed, Err:%d\n"</span>, ret);
       }

       <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
   }
</code></pre>
<h3 id="heading-choosing-the-right-buffer-design-and-size">Choosing the Right Buffer Design And Size</h3>
<p>Some buffer designs handle overflow better than others. Choosing the correct buffer type and size for the application reduces the risk of corruption.</p>
<ul>
<li><p>Circular Buffers (Ring Buffers) prevent out-of-bounds writes by wrapping around. They overwrite the oldest data instead of corrupting memory. These are useful for real-time streaming data (for example, UART, sensor readings). This approach is ideal for applications where data loss is unacceptable.</p>
</li>
<li><p>Ping-Pong Buffers (Double Buffers) use two buffers. One buffer fills up with data. Then, once it’s full, it switches to the second buffer while the first one is processed. This approach is beneficial for application that have strict requirements on no data loss. The buffer design should be based on the speed of write and read tasks.</p>
</li>
</ul>
<h3 id="heading-hardware-protection">Hardware Protection</h3>
<h4 id="heading-memory-protection-unit-mpu">Memory Protection Unit (MPU)</h4>
<p>An MPU (Memory Protection Unit) helps detect unauthorized memory accesses, including buffer overflows, by restricting which regions of memory can be written to. It prevents buffer overflows from modifying critical memory regions and triggers a MemManage Fault if a process attemps to write outside an allowed region.</p>
<p>But keep in mind that, an MPU does not prevent buffer overflows – it only detects and stops execution when they occur. Not all microcontrollers have an MPU, and some low-end MCUs lack hardware protection, making software-based safeguards even more critical.</p>
<p>Modern C compilers provide several flags to identify memory errors at compile-time:</p>
<ol>
<li><p>-Wall -Wextra: Enables useful warnings</p>
</li>
<li><p>-Warray-bounds: Detects out-of-bounds array access when the array size is known at compile-time</p>
</li>
<li><p>-Wstringop-overflow: Warns about possible overflows in string functions like memcpy and strcpy.</p>
</li>
</ol>
<h3 id="heading-testing-and-validation">Testing and Validation</h3>
<p>Testing helps detect buffer overflows before deployment, reducing the risk of field failures. Unit testing each function independently with valid inputs, boundary cases, and invalid inputs helps detect buffer-related issues early. Automated testing involves feeding random and invalid inputs into the system to uncover crashes and unexpected behavior. Static Analysis Tools like Coverity, Clang Static Analyzer help detect buffer overflows before runtime. Run real-world inputs on embedded hardware to detect issues.</p>
<p>Now that we've explored how to identify, debug, and prevent buffer overflows, it’s clear that these vulnerabilities pose a significant threat to embedded systems. From silent data corruption to catastrophic system failures, the consequences can be severe.</p>
<p>But with the right debugging tools, systematic analysis, and preventive techniques, you can effectively either prevent or mitigate buffer overflows in your systems.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Buffer overflows and corruption are major challenges in embedded systems, leading to crashes, unpredictable behavior, and security risks. Debugging these issues is difficult because their symptoms vary based on system state, requiring systematic analysis using memory map files, GDB/LLDB, and structured debugging approaches.</p>
<p>In this article, we explored:</p>
<ul>
<li><p>The causes and consequences of buffer overflows and corruptions</p>
</li>
<li><p>How to debug buffer overflows using memory analysis and debugging tools</p>
</li>
<li><p>Best practices for prevention</p>
</li>
</ul>
<p>Buffer overflow prevention requires a multi-layered approach:</p>
<ol>
<li><p>Follow a structured software design process to identify risks early.</p>
</li>
<li><p>Apply defensive programming principles to validate inputs and handle errors gracefully.</p>
</li>
<li><p>Use hardware-based protections like MPUs where available.</p>
</li>
<li><p>Enable compiler flags that help identify memory errors.</p>
</li>
<li><p>Test extensively, unit testing, automated testing, and code reviews help catch vulnerabilities early.</p>
</li>
</ol>
<p>By implementing these best practices, you can minimize the risk of buffer overflows in embedded systems, improving reliability and security.</p>
<p>In embedded systems, where reliability and safety are critical, preventing buffer overflows is not just a best practice, it is a necessity. A single buffer overflow can compromise an entire system. Defensive programming, rigorous testing, and hardware protections are essential for building secure and robust embedded applications.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Connect, Read, and Process Sensor Data on Microcontrollers – A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ In today’s world, computers are ubiquitous and generally serve two primary purposes. The first is general-purpose computing, where they handle a wide range of tasks, including running diverse applications and programs. Examples include laptops, deskt... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/connect-read-process-sensor-data-on-microcontrollers-for-beginners/</link>
                <guid isPermaLink="false">67d45997c9e7f2d42bb1c540</guid>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ microcontroller ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded software ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ADC ]]>
                    </category>
                
                    <category>
                        <![CDATA[ I2C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ real-time data processing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Signal Processing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ sensors ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Electronics ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hardware ]]>
                    </category>
                
                    <category>
                        <![CDATA[ electrical engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software architecture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MathJax ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Soham Banerjee ]]>
                </dc:creator>
                <pubDate>Fri, 14 Mar 2025 16:30:15 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741902732575/fd41a2d5-ed4f-445d-b186-936625837c8d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In today’s world, computers are ubiquitous and generally serve two primary purposes.</p>
<p>The first is general-purpose computing, where they handle a wide range of tasks, including running diverse applications and programs. Examples include laptops, desktops, servers, and supercomputers.</p>
<p>The second is embedded systems, which are specialized computers designed for specific functions. Commonly found in devices such as thermostats, refrigerators, cars, and other smart appliances, they rely on sensors to collect environmental data and execute their tasks efficiently.</p>
<h3 id="heading-the-role-of-sensors"><strong>The Role of Sensors</strong></h3>
<p>Sensors play a critical role in both types of computing. In embedded systems, sensors gather environmental data to help devices like autonomous vehicles, home appliances, and industrial machines perform tasks. In general-purpose computers, sensors primarily monitor internal conditions such as temperature and voltage, ensuring safe operation and preventing issues like overheating or electrical faults.</p>
<p>As Artificial Intelligence (AI) and the Internet of Things (IoT) evolve, sensors have become indispensable for gathering real-world data to support intelligent decision-making. Embedded systems leverage sensors to perceive their environment, transforming raw data into actionable insights that power automation and improve efficiency across industries.</p>
<p>This means that understanding sensor interfacing and designing robust sensor-driven software has become a vital skill for engineers and hobbyists alike.</p>
<p>Whether you're a beginner or experienced engineer, this guide will help you build a solid understanding of sensor interfacing software.</p>
<h2 id="heading-what-youll-learn-and-article-scope"><strong>What You’ll Learn and Article Scope</strong></h2>
<p>In this article, you’ll learn how to connect sensors to microcontrollers (MCUs) and design sensor software pipelines that turn raw data into meaningful, usable information. You’ll also explore practical techniques for processing sensor data accurately and efficiently in embedded systems.</p>
<p>Here’s a breakdown of what we’ll cover:</p>
<ul>
<li><p>What sensors are and how they work – An introduction to sensors, common types, and how sensor pipelines help process sensor data.</p>
</li>
<li><p>Key sensor characteristics – Important parameters like sensitivity, accuracy, precision, range, drift, and response time to help you choose the right sensor for your project.</p>
</li>
<li><p>How to interface sensors with microcontrollers – Hardware connections and communication protocols like SPI, I²C, and GPIO that allow microcontrollers to read sensor data.</p>
</li>
<li><p>Software architecture for sensor data – A high-level overview of the software pipeline that processes sensor data, including drivers, ADC support, scaling, calibration, and post-processing.</p>
</li>
<li><p>Detailed design of pipeline components – A closer look at each step in the pipeline, focusing on scaling raw data, calibrating sensors, and applying filters to clean up noisy signals.</p>
</li>
<li><p>Practical tips for power management – Best practices for handling power efficiently using low-power modes, FIFO buffers, and DMA when working with sensor data in embedded systems.</p>
</li>
</ul>
<p>By the end of this article, you’ll know how to design and implement a complete sensor data pipeline for an embedded system, from reading raw sensor data to preparing it for real-world use in intelligent, connected devices.</p>
<p><strong>Note</strong>: Advanced data processing, high-resolution ADCs, and hardware circuit design for sensors are outside the scope of this article.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>To get the most out of this article, you should have:</p>
<ol>
<li><p>Basic knowledge of microcontrollers: Understanding of common peripherals like ADCs (Analog-to-Digital Converters), SPI (Serial Peripheral Interface), I2C (Inter-Integrated Circuit) and GPIO (General Purpose Input/Output). If you’re new to these protocols, <a target="_blank" href="https://www.parlezvoustech.com/en/comparaison-protocoles-communication-i2c-spi-uart/">this article provides a great overview</a>.</p>
</li>
<li><p>Basic knowledge of electronics: Familiarity with circuits and signals, including analog and digital interfaces.</p>
</li>
<li><p>Programming in C: Familiarity in embedded software development, including driver development.</p>
</li>
<li><p>(Optional) Basic knowledge of sensors: Understanding different types of sensors (like temperature, pressure, motion) is helpful but not required.</p>
</li>
</ol>
<p>Also, this article assumes the following:</p>
<ul>
<li><p>You are working with a microcontroller equipped with the peripherals needed for sensor integration. The details of microcontroller peripherals can be found in a <a target="_blank" href="https://pdf.xab3.ro/manual/reference-manual-for-stm32f405415-stm32f407417-stm32f427437-and-stm32f429439-mcus-100">reference manual for example for an STM32F4</a> series microcontroller will have all the details :</p>
</li>
<li><p>You are familiar with compilers, debuggers, and IDEs used in embedded systems. Some common tools include:</p>
<ul>
<li><p>Compilers: <a target="_blank" href="https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads">GCC</a>, <a target="_blank" href="https://developer.arm.com/documentation/dui0773/l/Introducing-the-Toolchain/Toolchain-overview?lang=en">Clang</a>,</p>
</li>
<li><p>Debuggers: <a target="_blank" href="https://sourceware.org/gdb/">GDB</a>, <a target="_blank" href="https://lldb.llvm.org/use/tutorial.html">LLDB</a></p>
</li>
<li><p>IDEs: <a target="_blank" href="https://code.visualstudio.com">Visual Studio Code</a> (VSCode) is a popular choice, especially with extensions for embedded development and debugging.</p>
</li>
</ul>
</li>
<li><p>You aim to build reliable, sensor-driven embedded systems, capable of collecting and processing real-world data efficiently.</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-sensor-and-sensor-pipeline">What is a Sensor and Sensor Pipeline?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sensor-characteristics">Sensor Characteristics</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-interface-with-a-microcontroller">How to Interface with a Microcontroller</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-software-architecture">Software Architecture</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-high-level-overview-of-components">High-Level Overview of Components</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-accessing-data-from-the-sensor">Accessing Data from the Sensor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sensor-power-management">Sensor Power Management</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-detailed-design-of-components">Detailed Design of Components</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-sensor-driver">1. Sensor Driver</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-adc-support">2. ADC Support</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-scaling">3. Scaling</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-calibration">4. Calibration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-data-post-processing">5. Data Post-Processing</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-sensor-and-sensor-pipeline"><strong>What is a Sensor and Sensor Pipeline?</strong></h2>
<p>A sensor detects changes in physical properties such as temperature, pressure, or light and converts them into electrical signals that can be measured or interpreted. For example, a thermistor is a type of resistor whose resistance changes with temperature. As the temperature varies, the resistance of the thermistor changes, altering the voltage across it. The system then interprets this voltage change to determine the temperature.</p>
<p>To better understand sensors, consider the natural sensors in the human body: the eyes, ears, skin, nose, and tongue. These natural sensors constantly send signals about the environment to the brain for processing. Different regions of the brain interpret these signals and use the information to drive actions and responses. Just like the brain processes signals from natural sensors, a microcontroller processes signals from electronic sensors using a sensor pipeline.</p>
<p>Sensors come in many types, each designed to detect specific physical properties. Some sensors have a sensing element that changes its properties in response to conditions like heat, light, or pressure. Examples include thermistors, infrared receivers, and photodiodes.</p>
<p>For detecting movement, such as acceleration and rotation, MEMS (Microelectromechanical Systems) sensors—like accelerometers and gyroscopes—are widely used.</p>
<p>To measure distance, sensors like sonars, ultrasonic sensors, and radars are common. These are just a few examples of the many types of sensors available.</p>
<p>Beyond the types of physical properties they detect, sensors also differ in their levels of integration. Some sensors are raw sensors, consisting only of a sensing element and a transducer with simple leads for direct connection to an external circuit.</p>
<p>Others, known as smart sensors, include additional components such as an ADC (analog-to-digital converter) and onboard processing capabilities, enabling them to handle more of the data processing independently.</p>
<p>The choice between a raw sensor and a smart sensor depends on your application requirements, including factors like cost, size, and the processing load on the interfacing microcontroller.</p>
<p>Returning to our human analogy, consider how vision works as a sensor pipeline. When light enters our eyes, photoreceptor cells (rods and cones) in the retina act as sensing elements, converting the light into electrical signals. These signals travel via the optic nerve to the brain’s visual cortex, where they undergo processing to form a recognizable image. The brain then interprets this information and initiates a response, like smiling when you see a beautiful scenery.</p>
<p>Similarly, a sensor pipeline for an embedded system can be defined as shown in the picture below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738828676916/75137176-c9ba-432d-bf44-bb3da093e18d.png" alt="Figure 1: A Sensor Pipeline showing analogue to digital conversion, calibration, filtering, and then processing." class="image--center mx-auto" width="918" height="183" loading="lazy"></p>
<p>Each of these steps may have different requirements based on the application. Creating a requirements document for the sensor is helpful when selecting the appropriate sensor and configuring the pipeline.</p>
<h2 id="heading-sensor-characteristics"><strong>Sensor Characteristics</strong></h2>
<p>Before you dive into the blocks of the sensor pipeline, let’s review some important characteristics of a sensor.</p>
<h3 id="heading-sensitivity"><strong>Sensitivity</strong></h3>
<p>Sensitivity is the ability of a sensor to detect small changes in the physical property it’s designed to measure.</p>
<p>Sensitivity can vary based on factors like manufacturing processes, cost, and the design of the sensing element.</p>
<p>Sensors designed for a specific property often come in different sensitivity levels, allowing users to select an appropriate sensitivity based on the application requirements.</p>
<h3 id="heading-accuracy"><strong>Accuracy</strong></h3>
<p>Accuracy is the degree to which a sensor’s measurement matches the true value of the physical property it’s measuring. Testing a sensor’s accuracy typically requires comparing its readings to those of a reference instrument.</p>
<p>A sensor may have gain and offset errors—issues that calibration can help correct. Calibration adjusts for these systematic errors, which are often due to manufacturing tolerances or design factors.</p>
<p>Once calibrated, the sensor’s output can be verified against a reference to confirm its accuracy. The required level of accuracy should be determined based on the application’s needs.</p>
<h3 id="heading-precision"><strong>Precision</strong></h3>
<p>Precision refers to the consistency or repeatability of a sensor's measurements, regardless of how close those measurements are to the true value. It indicates the sensor's ability to produce the same output under identical conditions and how finely it can resolve and report values.</p>
<p>For example, if the true temperature of an object is 12.53°C:</p>
<ul>
<li><p>A precise sensor will consistently measure values like 12.52°C, 12.53°C, or 12.54°C, even if those values are slightly offset from the true temperature.</p>
</li>
<li><p>A highly accurate sensor, on the other hand, will measure values close to 12.53°C but may lack precision if those readings vary widely (e.g., 12.50°C, 12.53°C, and 12.56°C).</p>
</li>
</ul>
<p>For applications requiring exact measurements, a sensor with both high accuracy (closeness to the true value) and high precision (low variability) is essential. This is especially important in distinguishing small differences, such as between 12.5°C and 12.53°C.</p>
<p>In contrast, applications with less stringent requirements might use sensors with broader tolerances, such as ±1°C, which are sufficient for general monitoring purposes.</p>
<h3 id="heading-range"><strong>Range</strong></h3>
<p>The range of a sensor refers to the span between the maximum and minimum values of the physical property it can measure while maintaining its specified precision and accuracy. A sensor's operating range may extend beyond its measurement range, but the measurement range defines the limits within which the sensor reliably adheres to its specified sensitivity, accuracy, and response time.</p>
<h3 id="heading-drift"><strong>Drift</strong></h3>
<p>Drift is when a sensor's output changes over time due to conditions like temperature or humidity. Components within the sensor, including the sensing element, may be sensitive to these conditions, leading to gradual shifts in measurements.</p>
<p>For example, many components are affected by temperature and humidity changes, which can alter sensor readings. Also, sensors with internal oscillators may experience time-based drift, impacting accuracy.</p>
<p>Regular calibration with an accurate external reference (such as a precise clock) can help correct for drift and maintain reliable measurements. For certain applications, selecting a sensor with acceptable drift characteristics is crucial.</p>
<h3 id="heading-response-time"><strong>Response Time</strong></h3>
<p>Response time is the duration a sensor takes to detect and reflect a change in the measured physical property. For example, if the temperature rises by 5°C, the response time indicates how long the temperature sensor takes to reflect this change in its output.</p>
<p>Response time depends on the sensor’s design, manufacturing quality, and internal components, such as the ADC (Analog-to-Digital Converter), averaging circuits, and filters within the sensor pipeline.</p>
<p>All the parameters mentioned above are thoroughly documented in the sensor’s data-sheet. In practice, it’s a good idea to create a sensor requirements document for each specific application, detailing these key parameters as a baseline for sensor selection.</p>
<p>Now that you’ve examined the key characteristics of sensors, let’s explore how you can connect them to a microcontroller for real-world applications.</p>
<h2 id="heading-how-to-interface-with-a-microcontroller"><strong>How to Interface with a Microcontroller</strong></h2>
<h3 id="heading-choosing-a-communication-protocol">Choosing a Communication Protocol</h3>
<p>Another essential aspect of sensor requirements is specifying the communication interface between the sensor and the MCU or processor in the system. It’s important to understand how the sensor will be interfaced based on its output signal type and the available pins on the microcontroller.</p>
<p>For instance, certain sensors may connect directly to an analog or digital input pin on a microcontroller. A raw sensor, such as a temperature sensor, typically connects to an analog input pin, which is then read by the microcontroller’s internal ADC (Analog-to-Digital Converter).</p>
<p>In contrast, a digital-output sensor connects to a digital GPIO (General Purpose Input/Output) pin. For instance, speed sensors generate square waves with variable pulse widths to indicate speed. These signals are usually connected to a GPIO pin configured as an external interrupt or timer capture input, allowing the microcontroller to measure pulse width accurately.</p>
<p>A smart sensor, on the other hand, often supports communication protocols like SPI (Serial Peripheral Interface) or I2C (Inter-Integrated Circuit). These interfaces enable the microcontroller to configure the sensor, check its status, and retrieve data through register reads and writes.</p>
<p>Choosing the appropriate communication protocol for interfacing a sensor depends on the available pins in the system and the specific requirements of the application.</p>
<p><strong>Tip</strong>: When working with protocols like I²C or SPI, using tools such as <a target="_blank" href="https://www.saleae.com">Saleae</a> logic analyzers can greatly simplify debugging and validation. Logic analyzers capture and visualize communication signals, and tools like Saleae offer built-in protocol interpreters to help you decode sensor communication in real time. This can be especially helpful when troubleshooting configuration issues, timing problems, or communication errors during sensor interfacing.</p>
<p>Figure 2 below shows an example of a microcontroller connected to 4 sensors having different interfaces.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738828730915/25e62db6-a583-427a-bd77-c61c33990cdf.png" alt="Figure 2: A microcontroller interfacing with different sensors using different communication interfaces." class="image--center mx-auto" width="921" height="356" loading="lazy"></p>
<h3 id="heading-determining-power-requirements">Determining Power Requirements</h3>
<p>Power requirements are another key consideration when interfacing a sensor. Sensors may operate at different voltages (for example, 3.3V or 5V), so ensuring the microcontroller can accommodate these levels is essential. Level converters can bridge voltage mismatches, ensuring compatibility between the sensor and microcontroller voltage levels.</p>
<p>Timing and sampling requirements must also be evaluated, especially for sensors generating high-frequency data. Configuring external interrupts on GPIO pins can ensure timely data capture, while techniques like using DMA can streamline data transfer for sensors sampling at high frequencies without CPU involvement.</p>
<p>Now that you’ve learned about communication protocols and hardware connections, let’s focus on designing the software architecture that acquires, processes, and prepares sensor data for use. Designing effective software is crucial for obtaining clean, reliable data from the sensor.</p>
<h2 id="heading-software-architecture"><strong>Software Architecture</strong></h2>
<p>Now that we’ve chosen the sensor and communication protocol, let’s design the software architecture for the sensor pipeline. This software runs on the microcontroller connected to the sensor and processes raw data to make it clean and usable.</p>
<p>While application-level data processing is beyond the scope of this article, let’s focus on interfacing with the sensor and preparing the data for application use.</p>
<p>The sensor processing pipeline can be broken into the following components:</p>
<ol>
<li><p>Sensor Driver</p>
</li>
<li><p>Analog-to-Digital Conversion (ADC) Support</p>
</li>
<li><p>Scaling</p>
</li>
<li><p>Calibration</p>
</li>
<li><p>Data Post-Processing</p>
</li>
</ol>
<p>Let’s examine a high-level overview of these components for both smart and raw sensors.</p>
<h3 id="heading-high-level-overview-of-components"><strong>High-Level Overview of Components</strong></h3>
<ol>
<li><p><strong>Sensor Driver</strong></p>
<ol>
<li><p>Smart sensors: The driver configures the sensor, manages power, and handles read and write operations to the sensor registers over a communication protocol like SPI, I2C.</p>
</li>
<li><p>Raw sensors: The driver may only control GPIOs for power management, as raw sensors typically lack registers.</p>
</li>
</ol>
</li>
<li><p><strong>Analog-to-Digital Conversion (ADC) Support</strong></p>
<ol>
<li><p>Smart sensors: Include an onboard ADC, which is configured through the sensor driver.</p>
</li>
<li><p>Raw sensors: Requires an external ADC, an ADC driver implemented in software to configure the ADC, initiate conversions, and retrieve data.</p>
</li>
</ol>
</li>
<li><p><strong>Scaling</strong>: Scaling is necessary for both smart and raw sensors. It converts digital counts after the analog to digital conversion into meaningful physical quantities using formulas provided in the sensor data sheet. For example, a temperature sensor will use a formula to convert digital counts to degree Celsius.</p>
</li>
<li><p><strong>Calibration</strong>: Once the measured physical quantity is obtained, calibration adjusts the value by applying offsets, gains, or both to correct errors. This process ensures the sensor output aligns with reference values across its entire measurement range. A detailed discussion of the calibration process will follow in the next section.</p>
</li>
<li><p><strong>Data Post-Processing</strong>: Post-processing techniques, such as filtering are applied to improve data quality and reduce noise. Common filters such as low-pass or high-pass filters can remove unwanted frequency components.</p>
</li>
</ol>
<h3 id="heading-accessing-data-from-the-sensor"><strong>Accessing Data from the Sensor</strong></h3>
<p>The method of accessing data depends on the whether it’s a raw sensor or a smart sensor. Smart sensors will have onboard ADCs and FIFOs. Before delving into how data is accessed, it’s important to first understand sampling frequency.</p>
<h4 id="heading-sampling-frequency">Sampling Frequency:</h4>
<p>The frequency of taking a measurement from the sensor must follow the <a target="_blank" href="https://www.allaboutcircuits.com/technical-articles/nyquist-shannon-theorem-understanding-sampled-systems/">Nyquist-Shannon sampling theorem</a>. It states that the sampling rate must be twice the highest frequency component of the signal to be measured to accurately reconstruct the measured data.</p>
<p>The sampling frequency defines how often the sensor captures data, which affects how the data is accessed. Depending on whether the sensor is a raw sensor or a smart sensor, the approach to handling this sampled data varies.</p>
<p><strong>Smart Sensors:</strong></p>
<ol>
<li><p>Data register: The sensor writes sampled data directly into a register based on the set sample frequency updated during setup. The microcontroller reads this data register based on a data conversion completion interrupt.</p>
</li>
<li><p>FIFObBuffer: Some sensors include FIFO (First-In, First-Out) buffers to store multiple data points. When enabled, the FIFO updates at the configured sampling frequency and trigger interrupts when it becomes full or reaches a predefined level.<br> The benefits of FIFO include:</p>
<ol>
<li><p>Power efficiency: The MCU can process data in batches, reducing CPU overhead and allowing it to enter low-power mode during data collection.</p>
</li>
<li><p>Sampling and processing rate matching: FIFO buffers help reconcile differences between the sensor’s sampling rate and the MCU’s data processing rate.</p>
</li>
<li><p>For MCUs with Direct Memory Access (DMA), data transfer from the sensor to MCU memory can occur without CPU intervention, further reducing power consumption.</p>
</li>
</ol>
</li>
</ol>
<p><strong>Raw Sensors:</strong></p>
<p>For raw sensors, the MCU triggers ADC conversions at the sampling frequency, often using a timer interrupt. Data is read upon the ADC conversion complete interrupt, allowing the MCU to sleep during conversions and between samples to save power.</p>
<h3 id="heading-sensor-power-management"><strong>Sensor Power Management</strong></h3>
<p>Power management is critical for energy-sensitive applications. Strategies include:</p>
<ol>
<li><p>Low-power modes: Many sensors support low-power modes configurable through sensor registers.</p>
</li>
<li><p>GPIO-controlled power cycling (Duty-Cycling): For sensors without built-in low-power modes, the microcontroller can toggle the sensor’s power line using a GPIO pin, reducing power consumption further. Figure 3 below shows the diagram of a raw temperature sensor whose power is controlled using a GPIO from the MCU. For example, a temperature sensor in sleep mode can be activated only when temperature readings are required.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739042040654/1f2d4bbd-f15a-417a-9c79-3b93384e95bd.png" alt="Figure 3: Raw Temperature Sensor Interfacing a MCU" class="image--center mx-auto" width="549" height="557" loading="lazy"></p>
<p>The above techniques ensure efficient use of power while maintaining the required data sampling rate and sensor responsiveness.</p>
<p>With the high-level architecture in mind, we’ll now dive into the detailed design of each pipeline component.</p>
<h2 id="heading-detailed-design-of-components"><strong>Detailed Design of Components</strong></h2>
<p>In this section, you’ll delve into the key components of the sensor pipeline outlined in the Software Architecture section.</p>
<h3 id="heading-1-sensor-driver"><strong>1. Sensor Driver</strong></h3>
<p>The sensor driver is responsible for managing communication, configuration, power, and data acquisition for both smart and raw sensors.</p>
<h4 id="heading-smart-sensor-driver">Smart Sensor Driver:</h4>
<ol>
<li><p>Communication driver: Generic I2C or SPI drivers on the MCU can be adapted using wrapper functions to handle sensor-specific requirements, such as 1-byte, 2-byte, or 4-byte transfers.</p>
</li>
<li><p>Configuration: Typical tasks include setting the sampling rate, configuring interrupts, managing FIFO buffers, and, if needed, clock settings.</p>
</li>
<li><p>Power management: APIs should allow higher software layers to transition sensors between power modes by writing to specific registers or controlling GPIO lines for sensors without built-in power modes.</p>
</li>
</ol>
<h4 id="heading-raw-sensor-driver">Raw Sensor Driver:</h4>
<p>For raw sensors, the driver primarily manages power, often through GPIO-controlled toggling.</p>
<h3 id="heading-2-adc-support"><strong>2. ADC Support</strong></h3>
<p>ADC support is required only for raw sensors. In this article, we’re focusing on SAR ADCs, which are commonly embedded in microcontrollers.</p>
<h4 id="heading-how-sar-adcs-work">How SAR ADCs Work?</h4>
<p>A SAR ADC converts an analog signal to a digital value over multiple clock cycles, with the number of cycles equal to its bit resolution (for example, 10 cycles for a 10-bit ADC).</p>
<h4 id="heading-key-terms-related-to-adcs">Key terms related to ADCs:</h4>
<ol>
<li><p>Reference Voltage (VRef): Represents the maximum voltage the ADC can measure. Analog signals exceeding this limit must be scaled down.</p>
</li>
<li><p>Resolution: Determines the smallest detectable voltage change. For example, a 10-bit ADC with a 3.3V VRef has a resolution of 3.22 mV</p>
</li>
</ol>
<p>$$V_{\text{Res}} = V_{\text{Ref}} /2^{10}$$</p><p>The ADC result is stored in a data register, which can then be scaled to meaningful physical units.</p>
<h3 id="heading-3-scaling"><strong>3. Scaling</strong></h3>
<p>Scaling converts ADC counts into meaningful physical values, such as temperature (°C) or acceleration (g) depending on the sensor type. Sensor datasheets typically provide the necessary formulas or lookup tables.</p>
<p>For example, the method to convert a voltage measured by a raw temperature sensor to temperature value is shown below:</p>
<p>$$V_{\text{Measured}} = Counts_{\text{ADC}} / 2^{10} * V_{\text{Ref}} \quad \text{(Get V_Measured from ADC Counts)}$$</p><p>$$Temperature_{\text{Measured}} = V_{\text{Measured}} * T_{\text{C/mV}} \quad \text{(Get Temperature physical value)}$$</p><p>Similarly, a 3-axis accelerometer maps counts on the X, Y, and Z axes to acceleration values in g or milli-g.</p>
<h3 id="heading-4-calibration"><strong>4. Calibration</strong></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738829686302/bfa643dc-5e01-4b24-b885-b682acdb11cb.png" alt="Figure 4a: Calibration with gain &amp; offset | Figure 4b: Calibration with fixed offset" class="image--center mx-auto" width="1149" height="421" loading="lazy"></p>
<p>The figure above on the left (4a) is showing Calibration with gain and offset, while the figure above on the right (4b) is showing calibration with fixed offset.</p>
<p>$$x_{\text{calibrated}} = Gain * x_{\text{raw}} + Offset \quad \text{(Figure 4a - Linear Calibration)}$$</p><p>$$x_{\text{calibrated}} = x_{\text{raw}} + Offset \quad \text{(Figure 4b - Fixed offset Calibration)}$$</p><p>Calibration ensures the sensor’s output aligns with reference measurements, correcting for errors introduced by design, materials, or manufacturing.</p>
<h4 id="heading-types-of-errors">Types of Errors:</h4>
<ol>
<li><p>Offset error: A constant deviation of the sensor’s output from the true reference value, regardless of input magnitude.</p>
</li>
<li><p>Gain error: A proportional error where the sensor’s output scale deviates from the expected value, causing the output to increase or decrease incorrectly relative to the input.</p>
</li>
</ol>
<h4 id="heading-calibration-methods">Calibration Methods:</h4>
<ol>
<li><p>2/3-Point calibration: This type of calibration may involve either applying a fixed offset to the raw value or applying both gain and offset. Figure 4a illustrates an example of a gain/offset calibration, while Figure 4b depicts offset calibration. In both figures, the y-axis represents the reference value measured by an accurate instrument, while the x-axis represents the raw value measured by the sensor after ADC.</p>
</li>
<li><p>N-Point calibration: Involves multiple points for more complex, non-linear error correction.</p>
</li>
</ol>
<h4 id="heading-implementation">Implementation:</h4>
<ol>
<li><p>Calibration points shall cover the sensor’s entire measurement range for accuracy.</p>
</li>
<li><p>Parameters like gain and offset once estimated shall be stored in a non-volatile memory in the system for persistence to be used across power cycles.</p>
</li>
</ol>
<h3 id="heading-5-data-post-processing"><strong>5. Data Post-Processing</strong></h3>
<p>Post-processing covered in this section talks about removing noise and unwanted signal components, which improves data reliability.</p>
<h4 id="heading-filtering">Filtering</h4>
<p>Filtering is the process of removing unwanted frequency components from a signal to improve data quality. There are several different types of filters:</p>
<ul>
<li><p>Low-Pass Filters: Allows low-frequency signals to pass while attenuating high-frequency noise.</p>
</li>
<li><p>High-Pass Filters: Allows high-frequency signals to pass while attenuating low-frequency noise. (for example, gravitational acceleration in accelerometer data).</p>
</li>
<li><p>Band-Pass Filters: Retains only signals within a specific frequency range, removing both lower and higher frequencies outside the desired band.</p>
</li>
</ul>
<p>These filters are often implemented as FIR (Finite Impulse Response) or IIR (Infinite Impulse Response) filters. IIR filters are easy to implement and computationally efficient while FIR filters are computationally intensive but have better control over the frequency response.</p>
<p>Here, we will explore a simple low-pass filter known as the Exponential Moving Average (EMA), a type of IIR filter. A moving average filter is a mathematical technique that smooths short-term fluctuations while highlighting longer-term trends.</p>
<p>Unlike other moving average filters, EMA does not require maintaining a buffer, making it more memory-efficient. It is also more responsive to data changes while still providing smoothing, making it well-suited for real-time filtering. EMA assigns greater weight to recent data samples than older ones, allowing it to adapt quickly to changes in sensor readings.</p>
<p>EMA can be calculated like this:</p>
<p>$$EMA_{\text{t}} = \alpha * x_{\text{t}} + (1 - \alpha) * EMA_{\text{t - 1}}$$</p><p>$$\alpha = 2 / (N + 1) \quad \text{(Smoothening Factor, N - filter window size)}$$</p><p>$$EMA_{\text{t}} \quad \text{(Exponential Moving Average in current iteration)}$$</p><p>$$x_{\text{t}} \quad \text{(New Data Sample in Current Iteration)}$$</p><p>$$EMA_{\text{t - 1}} \quad \text{(Exponential Moving Average in the last iteration)}$$</p><p>Now that we understand the Exponential Moving Average (EMA) filter, here are two key factors to consider when tuning it for an application:</p>
<ul>
<li><p>Smoothing vs. Responsiveness: A higher smoothing factor (closer to 1, smaller filter window size) gives more weight to recent data, making the filter more responsive to changes but less effective at noise reduction. A lower smoothing factor (closer to 0, larger filter window size) provides better noise reduction but reacts more slowly to data changes.</p>
</li>
<li><p>Application-Specific Tuning: The smoothing factor should be chosen based on the sampling rate, sensor sensitivity, and application requirements. Real-time systems often require a balance between quick responsiveness and stable output.</p>
</li>
</ul>
<p>Here’s a code sample for EMA:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdint.h&gt;</span></span>

<span class="hljs-comment">// Exponential Moving Average (EMA) filter implementation</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> FILTER_WINDOW 5</span>

<span class="hljs-comment">// Function to calculate EMA</span>
<span class="hljs-function"><span class="hljs-keyword">float</span> <span class="hljs-title">calculateEMA</span><span class="hljs-params">(<span class="hljs-keyword">float</span> ema, <span class="hljs-keyword">float</span> new_value, <span class="hljs-keyword">float</span> alpha)</span> </span>{
    <span class="hljs-keyword">return</span> (alpha * new_value) + (<span class="hljs-number">1</span> - alpha) * ema;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">float</span> sensorReadings[] = {<span class="hljs-number">26.0</span>, <span class="hljs-number">27.5</span>, <span class="hljs-number">28.2</span>, <span class="hljs-number">27.0</span>, <span class="hljs-number">26.8</span>, <span class="hljs-number">26.5</span>, <span class="hljs-number">27.2</span>};
    <span class="hljs-keyword">int</span> numReadings = <span class="hljs-keyword">sizeof</span>(sensorReadings) / <span class="hljs-keyword">sizeof</span>(sensorReadings[<span class="hljs-number">0</span>]);

    <span class="hljs-keyword">float</span> alpha = <span class="hljs-number">2.0f</span> / (FILTER_WINDOW + <span class="hljs-number">1</span>); <span class="hljs-comment">// Standard EMA formula</span>
    <span class="hljs-keyword">float</span> ema = sensorReadings[<span class="hljs-number">0</span>];  <span class="hljs-comment">// Initialize EMA with the first reading</span>

    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"EMA Filtered Sensor Data:\n"</span>);

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; numReadings; i++) {
        ema = calculateEMA(ema, sensorReadings[i], alpha);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Reading %d: Raw = %.2f, EMA = %.2f\n"</span>, i + <span class="hljs-number">1</span>, sensorReadings[i], ema);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In summary, sensors are the backbone of modern smart devices, bridging the gap between the physical world and digital systems. From consumer electronics to industrial automation and medical devices, they enable devices to perceive and interact with their environments.</p>
<p>Understanding how sensors work, the components of their data pipeline, and their integration with microcontrollers is essential for engineers and hobbyists alike. By designing effective pipelines, developers can ensure accurate, clean, and reliable data, enabling systems to meet performance and power efficiency goals.</p>
<p>If you have questions or want to talk more about this topic, feel free to reach out on <a target="_blank" href="https://x.com/sohamstars">Twitter</a> or <a target="_blank" href="https://x.com/sohamstars">Lin</a><a target="_blank" href="https://www.linkedin.com/in/sohambanerjee2/">kedIn</a>. Always happy to connect.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Switch Case in Arduino – Control LEDs With the Switch Statement ]]>
                </title>
                <description>
                    <![CDATA[ You can use a switch case statement to execute different blocks of code based on the value of a variable. It offers a more direct and cleaner approach to handling multiple conditions. In this article, you'll learn how to control LEDs using a switch c... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-switch-case-in-arduino-control-leds/</link>
                <guid isPermaLink="false">670544332dc3e880b06ac749</guid>
                
                    <category>
                        <![CDATA[ arduino ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ihechikara Abba ]]>
                </dc:creator>
                <pubDate>Tue, 08 Oct 2024 14:39:47 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728178146204/a1c1a6af-b4ce-4fe4-a73d-8861d63cc01e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You can use a <code>switch case</code> statement to execute different blocks of code based on the value of a variable. It offers a more direct and cleaner approach to handling multiple conditions.</p>
<p>In this article, you'll learn how to control LEDs using a <code>switch case</code> statement in Arduino. You can also find the <code>switch case</code> statement in other programming languages, so this can serve as a practical example of how they work.</p>
<p>Here’s a demo of what you’ll be building:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728301474557/f67ddfe7-0cf1-47ee-9732-90f3a4b1649c.gif" alt="f67ddfe7-0cf1-47ee-9732-90f3a4b1649c" class="image--center mx-auto" width="298" height="168" loading="lazy"></p>
<p>You can watch the video version of this article here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/TAU_osZ6aGQ" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-hardware-components"><strong>Hardware Components</strong></h2>
<p>Here are the components you'll need to follow along:</p>
<ul>
<li><p>Arduino board (Uno).</p>
</li>
<li><p>Potentiometer.</p>
</li>
<li><p>Breadboard.</p>
</li>
<li><p>Three LEDs.</p>
</li>
<li><p>Resistors for the LEDs.</p>
</li>
<li><p>Jumper wires.</p>
</li>
</ul>
<h2 id="heading-how-to-use-a-switch-case-statement-in-arduino"><strong>How to Use a</strong> <code>Switch Case</code> Statement in Arduino</h2>
<p>Here's the syntax/structure of a <code>switch</code> statement:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">switch</span> (variable) {
  <span class="hljs-keyword">case</span> value1:
    <span class="hljs-comment">// code to be executed if variable == value1</span>
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> value2:
    <span class="hljs-comment">// code to be executed if variable == value2</span>
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">default</span>:
    <span class="hljs-comment">// code to be executed if variable doesn't match any case</span>
    <span class="hljs-keyword">break</span>;
}
</code></pre>
<p>Let's break it down:</p>
<ul>
<li><p><code>variable</code>: This denotes the variable being evaluated. The value of the variable determines how the code blocks will be executed.</p>
</li>
<li><p><code>case</code>: Each <code>case</code> represents a value that may match the variable being evaluated. If the <code>variable</code> and a <code>case</code> have the same value, the code for that case will be executed. You can have as many cases as you want.</p>
</li>
<li><p><code>break</code>: After a code block in a <code>case</code> has been executed, the <code>break</code> keyword terminates the code. That is, it stops the code from moving on to other cases because a match has already been found.</p>
</li>
<li><p><code>default</code>: In a situation where none of the cases match the <code>variable</code>, the code in the <code>default</code> block will be executed.</p>
</li>
</ul>
<p>Next, let's use a <code>switch</code> statement to control LEDs.</p>
<h3 id="heading-switch-case-in-arduino-example"><code>Switch Case</code> in Arduino Example</h3>
<h4 id="heading-circuit-diagram"><strong>Circuit Diagram</strong></h4>
<p>Here’s how to connect your components:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728177876221/e58910e9-f8be-430b-a220-cda2cc9b956a.png" alt="Circuit diagram showing potentiometer and LEDs connected to an Arduino Uno R3 board" class="image--center mx-auto" width="1920" height="814" loading="lazy"></p>
<p>The goal here is to decide which LED (or a combination of LEDs) comes on based on the value of a variable.</p>
<h4 id="heading-potentiometer-connection"><strong>Potentiometer Connection</strong></h4>
<ul>
<li><p>Connect the left terminal of the potentiometer to 5V.</p>
</li>
<li><p>Connect the right terminal to GND.</p>
</li>
<li><p>Connect the middle terminal to A0.</p>
</li>
</ul>
<h4 id="heading-led-connection"><strong>LED Connection</strong></h4>
<ul>
<li><p>For each LED, connect the shorter leg to GND.</p>
</li>
<li><p>Connect each longer leg to a digital pin. I recommend using pin 8 (for the green LED), 9 (for the yellow LED), and 10 (for the red LED) to match what we have in the circuit diagram. We'll also use these values in the code.</p>
</li>
</ul>
<p>Here's the full project code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> greenLED = <span class="hljs-number">8</span>;
<span class="hljs-keyword">int</span> yellowLED = <span class="hljs-number">9</span>;
<span class="hljs-keyword">int</span> redLED = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> potPin = A0; 
<span class="hljs-keyword">int</span> potValue;
<span class="hljs-keyword">int</span> mappedPotValue;
​
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  pinMode(greenLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  Serial.begin(<span class="hljs-number">9600</span>);
}
​
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  potValue = analogRead(potPin);
  mappedPotValue = <span class="hljs-built_in">map</span>(potValue, <span class="hljs-number">0</span>, <span class="hljs-number">1023</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>);
​
  <span class="hljs-keyword">switch</span> (mappedPotValue) {
    <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:
      digitalWrite(greenLED, LOW);
      digitalWrite(yellowLED, LOW);
      digitalWrite(redLED, LOW);
      Serial.println(mappedPotValue);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
      digitalWrite(greenLED, HIGH);
      digitalWrite(yellowLED, LOW);
      digitalWrite(redLED, LOW);
      Serial.println(mappedPotValue);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
      digitalWrite(greenLED, LOW);
      digitalWrite(yellowLED, HIGH);
      digitalWrite(redLED, LOW);
      Serial.println(mappedPotValue);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">3</span>:
      digitalWrite(greenLED, LOW);
      digitalWrite(yellowLED, LOW);
      digitalWrite(redLED, HIGH);
      Serial.println(mappedPotValue);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">4</span>:
      digitalWrite(greenLED, HIGH);
      digitalWrite(yellowLED, HIGH);
      digitalWrite(redLED, HIGH);
      Serial.println(mappedPotValue);
      delay(<span class="hljs-number">500</span>);
      digitalWrite(greenLED, LOW);
      digitalWrite(yellowLED, LOW);
      digitalWrite(redLED, LOW);
      Serial.println(mappedPotValue);
      delay(<span class="hljs-number">500</span>);
      <span class="hljs-keyword">break</span>;
  }
}
</code></pre>
<p>Let's break down the code.</p>
<h4 id="heading-variable-initialization"><strong>Variable Initialization</strong></h4>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> greenLED = <span class="hljs-number">8</span>;
<span class="hljs-keyword">int</span> yellowLED = <span class="hljs-number">9</span>;
<span class="hljs-keyword">int</span> redLED = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> potPin = A0; 
<span class="hljs-keyword">int</span> potValue;
<span class="hljs-keyword">int</span> mappedPotValue;
</code></pre>
<p>We started by initializing variables to correspond with the hardware connections.</p>
<p><code>greenLED</code>, <code>yellowLED</code>, and <code>redLED</code> have values of 8, 9, and 10, respectively. This matches the pins they were connected to on the Arduino board. Similarly, <code>potPin</code>, which is the variable for the potentiometer, has a value of A0.</p>
<p>You'll use the <code>potValue</code> variable to store the current value of the potentiometer. We also created a <code>mappedPotValue</code> variable to store the range of values needed for the LEDs in a minute.</p>
<h4 id="heading-pinmode-and-serial-monitor"><strong>pinMode and Serial Monitor</strong></h4>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  pinMode(greenLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  Serial.begin(<span class="hljs-number">9600</span>);
}
</code></pre>
<p>In the <code>setup()</code> function, we set the LEDs as output pins and initialized the serial monitor.</p>
<h4 id="heading-logic-for-switch-case-statement"><strong>Logic for</strong> <code>switch case</code> Statement</h4>
<p>First, we read the value of the potentiometer using the <code>analogRead()</code> function and stored it in the <code>potValue</code> variable:</p>
<pre><code class="lang-cpp">potValue = analogRead(potPin);
</code></pre>
<p>We then converted the values from the potentiometer to a range of 0 to 4 using the <code>map</code> function and stored them in the <code>mappedPotValue</code> variable:</p>
<pre><code class="lang-cpp">mappedPotValue = <span class="hljs-built_in">map</span>(potValue, <span class="hljs-number">0</span>, <span class="hljs-number">1023</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>);
</code></pre>
<p>Next, we created a <code>switch</code> statement—the value being evaluated is <code>mappedPotValue</code>. Recall that this is the variable where we stored the potentiometer values. So whenever you turn the potentiometer, the value changes and potentially matches a <code>case</code>:</p>
<pre><code class="lang-cpp">  <span class="hljs-keyword">switch</span> (mappedPotValue) {
    <span class="hljs-keyword">case</span> <span class="hljs-number">0</span>:
      digitalWrite(greenLED, LOW);
      digitalWrite(yellowLED, LOW);
      digitalWrite(redLED, LOW);
      Serial.println(mappedPotValue);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
      digitalWrite(greenLED, HIGH);
      digitalWrite(yellowLED, LOW);
      digitalWrite(redLED, LOW);
      Serial.println(mappedPotValue);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
      digitalWrite(greenLED, LOW);
      digitalWrite(yellowLED, HIGH);
      digitalWrite(redLED, LOW);
      Serial.println(mappedPotValue);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">3</span>:
      digitalWrite(greenLED, LOW);
      digitalWrite(yellowLED, LOW);
      digitalWrite(redLED, HIGH);
      Serial.println(mappedPotValue);
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">4</span>:
      digitalWrite(greenLED, HIGH);
      digitalWrite(yellowLED, HIGH);
      digitalWrite(redLED, HIGH);
      Serial.println(mappedPotValue);
      delay(<span class="hljs-number">500</span>);
      digitalWrite(greenLED, LOW);
      digitalWrite(yellowLED, LOW);
      digitalWrite(redLED, LOW);
      Serial.println(mappedPotValue);
      delay(<span class="hljs-number">500</span>);
      <span class="hljs-keyword">break</span>;
  }
</code></pre>
<p>We passed <code>mappedPotValue</code> as a parameter to <code>switch</code> since it's the variable being compared to different cases: <code>switch (mappedPotValue)</code>.</p>
<ul>
<li><p>For <code>case 0</code>, all the LEDs will be off.</p>
</li>
<li><p>For <code>case 1</code>, only the green LED comes on.</p>
</li>
<li><p>For <code>case 2</code>, only the yellow LED comes on.</p>
</li>
<li><p>For <code>case 3</code>, only the red LED comes on.</p>
</li>
<li><p>For <code>case 4</code>, all three LEDs will blink continuously.</p>
</li>
</ul>
<p>Using a <code>switch</code> statement, you've successfully controlled the behavior of LEDs based on the value of a potentiometer!</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In this article, you learned how to use a <code>switch case</code> statement in Arduino using a practical example.</p>
<p>You learned how to control different LEDs based on the value of a potentiometer. You achieved this by using different cases in a <code>switch</code> statement to match the potentiometer's current value and execute the corresponding code.</p>
<p><code>switch</code> statements can be used in different ways to make a project more dynamic. Some use cases in Arduino include:</p>
<ul>
<li><p>Managing and interpreting the different values, modes, and states of a component or sensor.</p>
</li>
<li><p>Performing actions based on specific commands. For example, rotating a robotic arm to a specific angle/direction.</p>
</li>
<li><p>Mapping button presses to user input, and so on.</p>
</li>
</ul>
<p>You can watch the video version of this project <a target="_blank" href="https://www.youtube.com/watch?v=TAU_osZ6aGQ">here</a>. The full project code is available on <a target="_blank" href="https://github.com/ihechikara/switch-case-arduino">GitHub</a>.</p>
<p>Check out <a target="_blank" href="https://ihechikara.com/">my blog</a> for articles about embedded systems, IoT, and web development.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Control the Brightness of an LED with a Potentiometer ]]>
                </title>
                <description>
                    <![CDATA[ Potentiometers are used in various electronic circuits and systems. You can use them in electronic devices to control volume, brightness, motor speed, voltage regulation, and so on. You've most likely used a potentiometer before with appliances like ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/control-led-with-potentiometer/</link>
                <guid isPermaLink="false">66b0a2863dc92ea6a5a091d9</guid>
                
                    <category>
                        <![CDATA[ arduino ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ihechikara Abba ]]>
                </dc:creator>
                <pubDate>Mon, 18 Mar 2024 19:48:37 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/cover-image-potentiometer.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Potentiometers are used in various electronic circuits and systems. You can use them in electronic devices to control volume, brightness, motor speed, voltage regulation, and so on.</p>
<p>You've most likely used a potentiometer before with appliances like radios, microwaves, blenders, electric fans, game controllers, and others.</p>
<p>They are generally used to provide or control different ranges of variable resistance in electronic circuits.</p>
<p>In this article, you'll learn the following:</p>
<ul>
<li>How to connect a potentiometer to an Arduino board.</li>
<li>How to get the values of a potentiometer.</li>
<li>How to control the brightness of an LED using a potentiometer.</li>
</ul>
<p>You can also watch the video version of this article here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/dwZCgzlYfoA" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-hardware-components">Hardware Components</h2>
<p>Here are the hardware components you'll need to follow along:</p>
<ul>
<li>Arduino board.</li>
<li>Potentiometer.</li>
<li>Breadboard.</li>
<li>LED.</li>
<li>1K Ohm resistor.</li>
<li>Jumper wires.</li>
</ul>
<h2 id="heading-how-to-connect-a-potentiometer-to-an-arduino-board">How to Connect a Potentiometer to an Arduino Board</h2>
<p>The potentiometer is made up of three terminals: two outer terminals and the middle terminal. Either of the outer terminals can be connected to either 5V or GND (ground). That is:</p>
<ul>
<li>If you connect the left outer terminal to 5V, you have to connect the right outer terminal to GND.</li>
<li>If you connect the left outer terminal to GND, you have to connect the right outer terminal to 5V.</li>
</ul>
<p>The middle terminal serves as the output terminal. We'll connect it to an analog pin. You can read the varying values of the potentiometer from the output terminal.</p>
<p>Here's the circuit diagram:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/03/circuit-diagram.png" alt="Image" width="600" height="400" loading="lazy">
<em>circuit diagram</em></p>
<p>Here's how the potentiometer was connected in the diagram above:</p>
<ul>
<li>The left outer terminal of the potentiometer was connected to GND.</li>
<li>The right outer terminal was connected to  5V.</li>
<li>The middle terminal (output terminal) was connected to analog pin A0 on the Uno board.</li>
</ul>
<p>Here's how the LED was connected:</p>
<ul>
<li>The shorter leg of the LED was connected to GND.</li>
<li>The longer leg was connected to digital pin 6 through a 1K Ohm resistor.</li>
</ul>
<p>Make sure to connect the LED to a digital pin with the ~ symbol. Such pins support pulse width modulation, which lets you send analog signals to digital pins.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> potPin = A0;
<span class="hljs-keyword">int</span> potValue = <span class="hljs-number">0</span>;
<span class="hljs-keyword">int</span> brightness = <span class="hljs-number">0</span>;
<span class="hljs-keyword">int</span> ledPin = <span class="hljs-number">6</span>;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
  pinMode(ledPin, OUTPUT);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  potValue = analogRead(potPin);
  brightness = (<span class="hljs-number">255.0</span>/<span class="hljs-number">1023.0</span>)*potValue;
  analogWrite(ledPin, brightness);
}
</code></pre>
<p>Let's break down the code.</p>
<h3 id="heading-initialize-variables">Initialize Variables</h3>
<p>We started by initializing our variables:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> potPin = A0;
<span class="hljs-keyword">int</span> potValue = <span class="hljs-number">0</span>;
<span class="hljs-keyword">int</span> brightness = <span class="hljs-number">0</span>;
<span class="hljs-keyword">int</span> ledPin = <span class="hljs-number">6</span>;
</code></pre>
<p>The <code>potPin</code> variable has a value of A0. This represents the A0 pin connected to the output pin of the potentiometer.</p>
<p>We then declared a <code>potValue</code> variable, which will be used to store the values from <code>potPin</code>.</p>
<p>The <code>brightness</code> variable will be used to control the brightness of the LED.</p>
<p>The LED pin was connected to digital pin 6 on the Uno board, so we initialized a <code>ledPin</code> variable with a value of 6: <code>int ledPin = 6;</code>.</p>
<h3 id="heading-serial-monitor-and-pinmode">Serial Monitor and pinMode</h3>
<p>Next, in the <code>loop()</code> function, we initialized the serial monitor and set the LED pin to serve as an output pin:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
  pinMode(ledPin, OUTPUT);
}
</code></pre>
<h3 id="heading-create-logic-to-control-led-brightness">Create Logic to Control LED Brightness</h3>
<p>In the <code>loop()</code> function, we have three lines of code:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  potValue = analogRead(potPin);
  brightness = (<span class="hljs-number">255.0</span>/<span class="hljs-number">1023.0</span>)*potValue;
  analogWrite(ledPin, brightness);
}
</code></pre>
<p>In the first line, we used the <code>analogRead()</code> function to read the value of <code>potPin</code>. The read values were assigned to the <code>potValue</code> variable.</p>
<p>At this point, if you print <code>potValue</code> to the serial monitor using <code>Serial.println(potValue);</code>, you'll get a range of values from 0 to 1023 when you tune the knob of the potentiometer.</p>
<p>For the <code>brightness</code> variable, we converted the values from the potentiometer to fall with the range of 0 to 255: <code>brightness = (255.0/1023.0)*potValue;</code>. This is because the <code>analogWrite()</code> function only accepts values within that range, and not the default 0 to 1023 that the potentiometer produces.</p>
<p>Lastly, we used the <code>analogWrite()</code> function to send values to the LED: <code>analogWrite(ledPin, brightness);</code>.</p>
<p>The <code>analogWrite()</code> function's first parameter is the <code>ledPin</code>, which denotes the pin where the values should be sent. The second parameter is <code>brightness</code>, which denotes a range of values to be sent to the LED (<code>ledPin</code>).</p>
<p>When you upload the code to your board, the LED should have varying levels of brightness as you tune the potentiometer.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, you learned how to control the brightness on an LED using a potentiometer. You also saw how to connect the components to digital and analog pins on an Arduino board.</p>
<p>Finally, you learned about how to make the components work together using code.</p>
<p>You can find the code for this project <a target="_blank" href="https://github.com/ihechikara/control-led-with-potentiometer/blob/main/pot.ino">here</a>. You can watch the video version <a target="_blank" href="https://youtu.be/dwZCgzlYfoA?si=k9W9eAEjcc4yLxqe">here</a>.</p>
<p>Check out <a target="_blank" href="https://ihechikara.com/">my blog</a> for articles about embedded systems, IoT, and web development.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Podcast: Hardware Engineering with Bruno Haid ]]>
                </title>
                <description>
                    <![CDATA[ On this week's episode of the freeCodeCamp Podcast, I interview hardware engineer and tech founder from Austria. We talk about growing up in the European countryside, his early passion for computers, and ultimately his move to San Francisco, where he... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/podcast-hardware-engineering-bruno-haid/</link>
                <guid isPermaLink="false">66b8d5148cd1c2aa053d499a</guid>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hardware ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Internet of Things ]]>
                    </category>
                
                    <category>
                        <![CDATA[ podcast ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Fri, 24 Nov 2023 21:17:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/signal-2023-11-24-141140_002_jpeg.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>On this week's episode of the freeCodeCamp Podcast, I interview hardware engineer and tech founder from Austria.</p>
<p>We talk about growing up in the European countryside, his early passion for computers, and ultimately his move to San Francisco, where he's founded several tech companies.</p>
<p>Bruno's super excited about embedded systems and custom hardware. He's building home appliances that incorporate open source software and open datasets.</p>
<p>We cover so many topics here. From Star Trek to the European Pirate Party.</p>
<p>I hope these weekly freeCodeCamp podcasts are firing you up about learning more about technology.</p>
<p>Be sure to follow The freeCodeCamp podcast in your favorite podcast app. And share this podcast with a friend. Let's inspire more folks to learn to code and build careers for themselves in tech.</p>
<p>You can listen to the podcast in Apple Podcasts, Spotify, or your favorite podcast app. You can also listen to the podcast below, right in your browser:</p>
<div class="embed-wrapper"><iframe src="https://play.libsyn.com/embed/episode/id/28783273/height/192/theme/modern/size/large/thumbnail/yes/custom-color/34345c/time-start/00:00:00/playlist-height/200/direction/backward/download/yes" height="192" width="100%" style="border:none" title="Embedded content" loading="lazy"></iframe></div>



<p>A couple interesting links from our discussion: </p>
<ul>
<li><a target="_blank" href="https://www.youtube.com/watch?v=PWeO5IkCssk">"Only Amiga" song from Comdex 1987</a></li>
<li><a target="_blank" href="https://www.youtube.com/watch?v=pWrioRji60A">Halt and Catch Fire TV Show trailer</a></li>
</ul>
<p>And here are some bonus photos from my time at Bruno's house in NYC:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Bruno_s_office_jpeg.png" alt="Image" width="600" height="400" loading="lazy">
<em>Bruno at work in his office</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/A_project_by_Bruno_jpeg.png" alt="Image" width="600" height="400" loading="lazy">
<em>A hardware project Bruno had prototyped in his bedroom. (This is the photo that I mentioned during the interview that I had snuck in to snap.)</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Arduino Handbook – Learn Microcontrollers for Embedded Systems ]]>
                </title>
                <description>
                    <![CDATA[ Arduino is an open-source platform that combines hardware and software in designing and building electronic projects. Arduino can be applied in a variety of projects like: Home automation. Internet of Things (IoT). Audio and music. Automated and rem... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-arduino-handbook/</link>
                <guid isPermaLink="false">66b0a3683ac4671a1e580305</guid>
                
                    <category>
                        <![CDATA[ arduino ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ihechikara Abba ]]>
                </dc:creator>
                <pubDate>Thu, 05 Oct 2023 13:15:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/The-Arduino-Handbook-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Arduino is an open-source platform that combines hardware and software in designing and building electronic projects.</p>
<p>Arduino can be applied in a variety of projects like:</p>
<ul>
<li>Home automation.</li>
<li>Internet of Things (IoT).</li>
<li>Audio and music.</li>
<li>Automated and remote controlled systems.</li>
<li>Automation in agriculture.</li>
<li>Electronic prototyping.</li>
<li>Wearable devices, and so much more.</li>
</ul>
<p>The hardware part of Arduino comprises Arduino boards, input and output devices (including digital and analog pins, and sensors and actuators), shields, breadboards, jumper wires, and so on. These components can be combined together to create dynamic and interactive projects. </p>
<p>The software is made up of the development tools used to write, debug, compile, and upload code to Arduino boards. Most 0f the software tools can be found in the Arduino IDE (Integrated Development Environment).</p>
<p>This handbook will help you understand how Arduino works. You'll learn about the Arduino boards, the components that make up a board, and how to connect devices to them.</p>
<p>We'll talk about input and output peripherals which help the microcontroller (the brain of the Arduino board) process information coming from the physical environment, and send output based on programmed logic. </p>
<p>You'll learn about the Arduino IDE, how to code using the Arduino programming language, and how to use sensors, actuators, and other components to build projects as you learn. </p>
<p>You'll also learn about serial communication, which helps Arduino boards communicate with other computers.</p>
<p>This handbook is written for makers (students, artists, hobbyists, programmers) who are beginners.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Although it would be helpful, you don't need prior programming knowledge to use this handbook. You will learn the basics of Arduino programming from scratch. This can also serve as your introduction to programming.</p>
<p>To make it beginner friendly, we won't talk about some aspects of electronics like current and voltage, resistance, circuits (series and parallel), and most basic electronic/electrical laws and requirements for students in STEM (Science, Technology, Engineering, Mathematics) fields.</p>
<p>Whether you have knowledge of these concepts or not, you can learn about Arduino using this handbook.</p>
<p>If you know how to use a breadboard and a resistor, then that's all the knowledge on electronics you'll need to follow along. </p>
<p>In summary, this handbook is for everyone. You don't need an engineering degree to become an Arduino maker!</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-chapter-1-getting-started-with-arduino">Chapter 1: Getting Started with Arduino</a></li>
<li><a class="post-section-overview" href="#heading-chapter-2-basics-of-arduino-programming">Chapter 2: Basics of Arduino Programming</a></li>
<li><a class="post-section-overview" href="#heading-chapter-3-how-to-use-digital-pins-in-arduino">Chapter 3: How to use Digital Pins in Arduino</a></li>
<li><a class="post-section-overview" href="#heading-chapter-4-how-to-use-analog-pins-in-arduino">Chapter 4: How to use Analog Pins in Arduino</a></li>
<li><a class="post-section-overview" href="#heading-chapter-5-how-to-use-sensors-and-actuators-in-arduino">Chapter 5: How to use Sensors and Actuators in Arduino</a></li>
<li><a class="post-section-overview" href="#heading-chapter-6-how-to-use-the-serial-monitor-in-arduino-2">Chapter 6: How to use the Serial Monitor in Arduino</a></li>
<li><a class="post-section-overview" href="#heading-chapter-7-how-to-use-displays-in-arduino">Chapter 7: How to use Displays in Arduino</a></li>
</ul>
<h1 id="heading-chapter-1-getting-started-with-arduino">Chapter 1: Getting Started with Arduino</h1>
<p>The Arduino development and design process comprises both hardware and software. So knowing how they work together is important for building the right foundation for your journey.</p>
<p>In this chapter, you’ll learn about the different components that make up the Arduino Uno board. You'll also learn how to install the Arduino IDE and set up your development environment.</p>
<p>At the time of writing, a new Uno board was released — the Arduino Uno R4. This handbook will make use of the Uno R3 board, but you can follow along with either of them. The R4 board comes in two variants — Arduino Uno R4 WiFi and Arduino Uno R4 Minima — with cool additional features that you can read about <a target="_blank" href="https://blog.arduino.cc/2023/06/26/uno-r4-the-new-dimension-of-making/?_gl=1*18ccx2k*_ga*MTkzMTc3MDUxNC4xNjc5NjU4Mzkz*_ga_NEXN8H46L5*MTY4Nzk0Njg3Mi40LjEuMTY4Nzk0ODE3MS4wLjAuMA..">here</a>.</p>
<h2 id="heading-components-of-the-arduino-uno-r3-board">Components of the Arduino Uno R3 Board</h2>
<p>There are many types of Arduino boards like Arduino Nano, Arduino Uno, Arduino Mega, Arduino Leonardo, and so on.</p>
<p>These boards have some common features — they all have digital and output pins, they’re programmable, and they all have a microcontroller.</p>
<p>But there are also some differences. Each board varies in size and shape, and usually has more or fewer components when compared to other boards.</p>
<p>The common boards you’ll come across as a beginner are the Nano, Uno, and Mega boards. The most commonly used is the Uno board, which we’ll use for this handbook.</p>
<p>Here are some of the components you'll find on the Uno R3 board:</p>
<ul>
<li>A power port.</li>
<li>USB connector.</li>
<li>Microcontroller (ATmega328).</li>
<li>Analog pins.</li>
<li>Digital pins.</li>
<li>Reset button.</li>
<li>TX and RX indicators.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/UnoR3.png" alt="Image" width="600" height="400" loading="lazy">
<em>Arduino Uno R3 board (https://store.arduino.cc/products/arduino-uno-rev3)</em></p>
<p>You’ll make use of most of the components listed above as you progress through this handbook.</p>
<h2 id="heading-how-to-install-and-set-up-the-arduino-ide">How to Install and Set Up the Arduino IDE</h2>
<p>You can use the Arduino IDE to program Arduino boards. That is, you write the code in the IDE, then upload it to the board.</p>
<p>In this section, you’ll learn how to set up the IDE, and create your first Arduino program (also called an Arduino sketch).</p>
<p>You can download the latest version of the Arduino IDE on the <a target="_blank" href="https://www.arduino.cc/en/software">Arduino software download page</a>. You can download the IDE for different operating systems — Windows, MacOS, and Linux.</p>
<p>The installation process is similar for the operating systems listed above. Here’s how to install it on a Windows machine:</p>
<h3 id="heading-step-1-download-the-arduino-ide">Step #1 – Download the Arduino IDE</h3>
<p>The first step is to download the IDE from the <a target="_blank" href="https://www.arduino.cc/en/software">Arduino software download page</a>. You should see a section of the page similar to the image below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/arduino-download-page-options.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>On the right side of the image above are different download options for specific operating systems. Make sure you download the option that suits your operating system.</p>
<p>I'll use the ZIP file option for Windows. If you decide to download an installer instead, then you can follow the installation steps after clicking the installation file.</p>
<h3 id="heading-step-2-unzip-the-downloaded-file">Step #2 – Unzip the Downloaded File</h3>
<p>Go on and unzip the downloaded file. This gives you access to all the resources needed to run the Arduino IDE.</p>
<p>After unzipping the file, you should see files like these:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/arduino-ide-unzipped.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing the files you should see</em></p>
<p>To launch the Arduino IDE, click on the file that says “Arduino IDE”.</p>
<h3 id="heading-step-3-overview-of-the-arduino-ide">Step #3 – Overview of the Arduino IDE</h3>
<p>Now that you’ve downloaded and installed the Arduino IDE, the next part is to get familiar with the development environment. In the next section, you’ll learn how to upload code to an Arduino Uno board using the IDE.</p>
<p>Before that, let’s have a look at some options you’ll find in the Arduino IDE. At the top left corner of the IDE are five options — File, Edit, Sketch, Tools, Help:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/ide.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot showing these options (File, Edit, Sketch, Tools, Help)</em></p>
<p>The “File” option lets you do different things like creating a new sketch (we’ll talk about sketches in the next section), opening an existing sketch, Arduino practice examples for beginners, keyboard shortcuts, save options, and so on.</p>
<p>The “Edit” option gives you access to text formatting options like copy, paste, cut, comment/uncomment code, font size options, text search options, and so on.</p>
<p>You can use the “Sketch” option to verify and compile code, upload code to Arduino boards, optimize code, and add libraries.</p>
<p>You can use the “Tools” option to manage libraries, format code, access the serial monitor and plotter, select an Arduino board and port to upload code to, choose a processor, and so on.</p>
<p>The “Help” option provides resources for troubleshooting, information on IDE updates, guides on “getting started”, and so on.</p>
<p>Next, let’s look at some other parts and functionalities in the IDE that you’ll find useful. The image below, from the <a target="_blank" href="https://docs.arduino.cc/software/ide-v2/tutorials/getting-started-ide-v2">Arduino documentation</a>, highlights them perfectly:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/arduino-ide-icons.png" alt="Image" width="600" height="400" loading="lazy">
<em><a target="_blank" href="https://docs.arduino.cc/software/ide-v2/tutorials/getting-started-ide-v2">https://docs.arduino.cc/software/ide-v2/tutorials/getting-started-ide-v2</a></em></p>
<ul>
<li><strong>Verify/Upload</strong>: You can use these options to compile and upload code to Arduino boards. You’ll get error messages if the code doesn't compile as expected.</li>
<li><strong>Select Board &amp; Port</strong>: You can use this option to select a port and port number to upload your code too. The current version of the Arduino IDE automatically detects both boards and ports.</li>
<li><strong>Sketchbook</strong>: This gives you access to all the sketches created in your computer. You can also access sketches saved on Arduino Cloud (mostly used for creating IoT projects).</li>
<li><strong>Boards Manager</strong>: The Arduino IDE comes with support for different boards. As you progress through your journey, you’ll make use of different boards and some of them may not be supported by the IDE. The board manager tab lets you install and manage packages required to use these boards.</li>
<li><strong>Library Manager</strong>: You can use libraries to extend certain functionalities in code. Through the library manager, you can install numerous libraries that’ll help simplify the development process for you.</li>
<li><strong>Debugger</strong>: This is used for real time testing and debugging of Arduino programs.</li>
<li><strong>Search</strong>: You can use the search tool to find specific keywords in your code.</li>
<li><strong>Open Serial Monitor</strong>: You can use the serial monitor to communicate with Arduino boards, debug and test code, visualize data from your boards, interact with user input, and so on. We’ll look at the serial monitor in depth in a different chapter.</li>
<li><strong>Open Serial Plotter</strong>: The serial plotter is mostly used for real-time visualization of numerical data.</li>
</ul>
<h2 id="heading-what-is-an-arduino-sketch">What Is an Arduino Sketch?</h2>
<p>We mentioned the term “sketch” a couple of times in the last section, but what is it? A sketch is a program written with the Arduino programming language. It’s another way of referring to a code file written for Arduino projects.</p>
<p>The Arduino programming language is built upon the C/C++ language so they both share similar syntax and structure. You may come across resources that refer to Arduino code as “embedded C” or “embedded C++”.</p>
<h2 id="heading-how-to-upload-code-to-an-arduino-board">How to Upload Code to an Arduino Board</h2>
<p>To upload code to an Arduino board, you'll need both hardware and software. The hardware is the board which is the Uno board in our case, and the software is the Arduino sketch in the IDE.</p>
<p>Here are the steps:</p>
<h3 id="heading-step-1-connect-the-arduino-board">Step #1 – Connect the Arduino Board</h3>
<p>Connect the Arduino board to your computer using the USB cable. Without this step, you can't go further.</p>
<h3 id="heading-step-2-create-a-sketch">Step #2 – Create a Sketch</h3>
<p>Now it's time to launch the IDE and write some code. </p>
<p>Here's a code example that makes an LED blink:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> ledPin = <span class="hljs-number">13</span>;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  pinMode(ledPin, OUTPUT);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  digitalWrite(ledPin, HIGH);
  delay(<span class="hljs-number">1000</span>);
  digitalWrite(ledPin, LOW);
  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>Don't worry if you don't understand the code — we'll cover everything as we go further.</p>
<h3 id="heading-step-3-select-the-board-and-port">Step #3 – Select the Board and Port</h3>
<p>You can select the board to upload your code to from the IDE. Here's an image showing what that looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/port.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-4-verify-the-code">Step #4 – Verify the Code</h3>
<p>You can use the verify button to compile the code and check for errors. If errors exist, you'll get an error message to show you the possible cause.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/verify.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing the verify button</em></p>
<h3 id="heading-step-5-upload-the-code">Step #5 – Upload the Code</h3>
<p>You can upload the code using the upload button (the button after the verify button).</p>
<p>If there are no errors in your code, these steps will help upload code to your board. If you've uploaded the code above, you should have the built-in LED (it is connected to pin 13 by design) on the Uno board blinking.</p>
<p>In the next chapter, you'll learn the basics of the Arduino programming language.</p>
<h1 id="heading-chapter-2-basics-of-arduino-programming">Chapter 2: Basics of Arduino Programming</h1>
<p>Before we dive into creating our own sketches and tinkering, you have to understand the logic that make these boards work as expected. To do that, you’ll have to know how to code using the Arduino programming language.</p>
<p>As discussed in the last chapter, the Arduino language is built upon C/C++. You’ll begin this chapter by learning the basics of programming. This will prepare you for every other chapter that involves writing code.</p>
<p>I’ve created this chapter with beginners in mind. If you’ve never written code before then this can serve as a starting point for you. This doesn’t mean you’ll learn how to code in C or C++. You’ll be learning how to write Arduino code which shares similar syntax with those languages.</p>
<p>At the end of this chapter, you should be able to understand and write Arduino code.</p>
<h2 id="heading-variables-and-data-types-in-arduino">Variables and Data Types in Arduino</h2>
<p>Variables and data types are used in most programming languages to store and manipulate data. You can think of variables as containers or storage units. Data types, like the name implies, are the type of data stored in variables.</p>
<p>In Arduino programming, you must specify the data type of a variable before using it. That is:</p>
<pre><code>dataType variableName = variableValue
</code></pre><p>There are different types of data types in Arduino, and we’ll discuss each one along with code examples.</p>
<h3 id="heading-int-data-type-in-arduino"><code>int</code> Data Type in Arduino</h3>
<p>The <code>int</code> data type is used to store integer values. The Uno board has a 16-bit integer capacity so it can store values that fall within the range of -32,768 to 32,767.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> redLED = <span class="hljs-number">6</span>;
</code></pre>
<p>In the code above, we created an integer variable called <code>redLED</code> with a value of 6.</p>
<p>The <code>int</code> data type can also store negative integers:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> redLED = <span class="hljs-number">-6</span>;
</code></pre>
<h3 id="heading-long-data-type-in-arduino"><code>long</code> Data Type in Arduino</h3>
<p>The <code>long</code> data type is similar to <code>int</code> but has a wider range of integer values. It has a 32-bit integer limit which falls within the range of -2,147,483,648 to 2,147,483,647.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">long</span> largeNumber = <span class="hljs-number">6000</span>;
</code></pre>
<h3 id="heading-float-data-type-in-arduino"><code>float</code> Data Type in Arduino</h3>
<p>The <code>float</code> data type can be used to store numbers with decimals. Float variables can store values up to 3.4028235E+38 and values as low as -3.4028235E+38.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">float</span> num = <span class="hljs-number">10.5</span>;
</code></pre>
<p>Although the <code>float</code> data type is mainly used for decimal numbers, it can also accept whole numbers (integers without decimals). But it'll always return a float value. So if you store 10 in a <code>float</code>, it'll return 10.00.</p>
<h3 id="heading-string-data-type-in-arduino"><code>String</code> Data Type in Arduino</h3>
<p>You can use the <code>String</code> data type to store and manipulate text. You'll work with strings occasionally to display information in the form of text when building projects.</p>
<p>Here's a code example:</p>
<pre><code class="lang-c++">String greeting = <span class="hljs-string">"Hello World!"</span>;
</code></pre>
<p>The value of strings are nested within double quotation marks as can be seen in the code above.</p>
<p>Note that when declaring a string, the "S" should always be in uppercase.</p>
<h3 id="heading-char-data-type-in-arduino"><code>char</code> Data Type in Arduino</h3>
<p>The <code>char</code>  data type stores single characters.</p>
<p>Here's an example:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">char</span> alphabet = <span class="hljs-string">'A'</span>;
</code></pre>
<p>This is different from the <code>String</code> data type that can store multiple characters.</p>
<p>There are two main differences between <code>char</code> and <code>String</code>:</p>
<ul>
<li><code>char</code> uses single quotes ('A') while <code>string</code> uses double quotes ("Arduino").</li>
<li><code>char</code> stores single characters while <code>string</code> stores multiple characters.</li>
</ul>
<p><code>char</code> can also accept integer values equivalent to the <a target="_blank" href="https://www.asciitable.com/">ASCII</a> value of letters:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">char</span> charValue = <span class="hljs-number">65</span>;
</code></pre>
<p>In the code above, we initialized a <code>char</code> variable with the value of 65. When printed to the serial monitor (we'll talk about the serial monitor in <a class="post-section-overview" href="#heading-chapter-6-how-to-use-the-serial-monitor-in-arduino-2">Chapter 6: How to use the Serial Monitor in Arduino</a>), A will be returned.</p>
<p>A is returned because 65 has an ASCII character of A.</p>
<h3 id="heading-bool-and-boolean-data-types-in-arduino"><code>bool</code> and <code>boolean</code> Data Types in Arduino</h3>
<p>You can use both <code>bool</code> and <code>boolean</code> to store/denote boolean values of either <code>true</code> or <code>false</code>.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">bool</span> roomIsCold = <span class="hljs-literal">false</span>;
</code></pre>
<p>Boolean values are mostly used with logical and comparison operators, and conditional statements (you'll learn about these later in this chapter) to manipulate and control different outcomes in an Arduino program.</p>
<h3 id="heading-byte-data-type-in-arduino"><code>byte</code> Data Type in Arduino</h3>
<p>The <code>byte</code> data type has an 8-bit unsigned integer limit that ranges from 0 to 255. Unsigned means that it can't store negative values.</p>
<pre><code class="lang-c++">byte sensorValue = <span class="hljs-number">200</span>;
</code></pre>
<p>The <code>byte</code> data type isn't the only data type that can be unsigned. You can also use the <code>unsigned int</code>, <code>unsigned long</code>, and <code>unsigned char</code> data types which all have their respective positive integer ranges.</p>
<h2 id="heading-operators-in-arduino">Operators in Arduino</h2>
<p>Operators are symbols or characters that can be used to perform certain operations on operands. An operand is simply any value(s) an operator acts on.</p>
<p>There are different categories of operators in Arduino like:</p>
<h3 id="heading-arithmetic-operators">Arithmetic Operators</h3>
<p>Arithmetic operators are used to perform mathematical operations like addition, subtraction, division, multiplication, and so on. Here are some arithmetic operators you should know:</p>
<h4 id="heading-addition-operator">Addition(+) Operator</h4>
<p>The addition operator, denoted by the <code>+</code> symbol, adds two operands together:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> a = <span class="hljs-number">5</span>;
<span class="hljs-keyword">int</span> b = <span class="hljs-number">10</span>;

<span class="hljs-comment">// we use addition operator to add a and b below</span>
<span class="hljs-keyword">int</span> c = a + b;

Serial.print(c);
<span class="hljs-comment">// 15</span>
</code></pre>
<h4 id="heading-subtraction-operator">Subtraction(-) Operator</h4>
<p>The subtraction operator subtracts the value of one operand from another operand. It is denoted by the <code>-</code> symbol:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> a = <span class="hljs-number">5</span>;
<span class="hljs-keyword">int</span> b = <span class="hljs-number">10</span>;

<span class="hljs-comment">// we use subtraction operator to subtract b from a below</span>
<span class="hljs-keyword">int</span> c = b - a;

Serial.print(c);
<span class="hljs-comment">// 5</span>
</code></pre>
<h4 id="heading-multiplication-operator">Multiplication (*) Operator</h4>
<p>You can use the multiplication operator (<code>*</code>) to multiply two operands:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> a = <span class="hljs-number">5</span>;
<span class="hljs-keyword">int</span> b = <span class="hljs-number">10</span>;

<span class="hljs-comment">// we use multiplication operator to multiply a by b below</span>
<span class="hljs-keyword">int</span> c = a * b;

Serial.print(c);
<span class="hljs-comment">// 50</span>
</code></pre>
<h4 id="heading-division-operator">Division(/) Operator</h4>
<p>The division operator divides one operand by another:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> a = <span class="hljs-number">5</span>;
<span class="hljs-keyword">int</span> b = <span class="hljs-number">10</span>;

<span class="hljs-comment">// we use division operator to divide b by a below</span>
<span class="hljs-keyword">int</span> c = b / a;

Serial.print(c);
<span class="hljs-comment">// 2</span>
</code></pre>
<h4 id="heading-modulus-operator">Modulus (%) Operator</h4>
<p>The modulus operator returns the remainder of a division between two operands:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> a = <span class="hljs-number">5</span>;
<span class="hljs-keyword">int</span> b = <span class="hljs-number">10</span>;

<span class="hljs-comment">// we use division operator to divide b by a below</span>
<span class="hljs-keyword">int</span> c = b % a;

Serial.print(c);
<span class="hljs-comment">// 0</span>
</code></pre>
<h4 id="heading-increment-operator">Increment (++) Operator</h4>
<p>The increment operator increases the value of a variable by 1:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> num = <span class="hljs-number">5</span>;
num++;

Serial.print(num);
<span class="hljs-comment">// 6</span>
</code></pre>
<h4 id="heading-decrement-operator">Decrement (--) Operator</h4>
<p>The decrement operator decreases the value of a variable by 1:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> num = <span class="hljs-number">5</span>;
num--;

Serial.print(num);
<span class="hljs-comment">// 4</span>
</code></pre>
<h3 id="heading-assignment-operators">Assignment Operators</h3>
<p>Assignment operators are mainly used to assign values to variables. You can also use them to update the value of variables. </p>
<p>The assignment (<code>=</code>) operator is used for assigning and updating variables. The <code>=</code> operator should not be confused for "equal to" — they aren't the same. We'll talk about the equal to (<code>==</code>) operator in the Comparison Operators section.</p>
<p>Here's an example that shows how to use the assignment operator:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> age = <span class="hljs-number">1</span>;
</code></pre>
<p>In the code above, we created a variable called <code>age</code>, and then assigned a value of 1 to it using the <code>=</code> operator.</p>
<p>But this isn't the only way to assign or update the value of variables when using the <code>=</code> operator. You can also use compound assignment operators.</p>
<h4 id="heading-compound-assignment-operators">Compound Assignment Operators</h4>
<p>Compound assignment operators let you combine arithmetic operators and the <code>=</code> operator. This method provides a shorter way of writing code. Here is an example:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">5</span>;
x += <span class="hljs-number">5</span>;

Serial.print(x)
<span class="hljs-comment">// 10</span>
</code></pre>
<p>In the code above, we created an <code>x</code> variable and assigned a value of 5 to it. But on the second line, you'd see that we combined the arithmetic addition (<code>+</code>) operator and the <code>=</code> operator to assign a new value to <code>x</code>:</p>
<pre><code class="lang-c++">x += <span class="hljs-number">5</span>;
</code></pre>
<p>The line of code above is the same as this:</p>
<pre><code class="lang-c++">x = x + <span class="hljs-number">5</span>;
</code></pre>
<p>So compound operators combine two operators and let you do the same thing in a shorter way. There's nothing wrong with either method.</p>
<p>Here are other compound operator examples:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> a = <span class="hljs-number">10</span>;
a -= <span class="hljs-number">5</span>; <span class="hljs-comment">// Equivalent to a = a - 5 (a becomes 5)</span>

<span class="hljs-keyword">int</span> b = <span class="hljs-number">10</span>;
b *= <span class="hljs-number">5</span>; <span class="hljs-comment">// Equivalent to b = b * 5 (b becomes 50)</span>

<span class="hljs-keyword">int</span> c = <span class="hljs-number">10</span>;
c /= <span class="hljs-number">5</span>; <span class="hljs-comment">// Equivalent to c = c / 5 (c becomes 2)</span>

<span class="hljs-keyword">int</span> d = <span class="hljs-number">10</span>;
d %= <span class="hljs-number">5</span>; <span class="hljs-comment">// Equivalent to d = d % 3 (d becomes 0)</span>
</code></pre>
<h3 id="heading-comparison-operators">Comparison Operators</h3>
<p>You can use comparison operators to compare two values/operands. Comparison operators return either <code>true</code> (1) or <code>false</code> (0) depending on the relationship between operands.</p>
<p>Comparison operators can help you make decisions based on their return values. You'll see them a lot when we start building projects.</p>
<p>Here are the comparison operators you'll come across occasionally:</p>
<h4 id="heading-equal-to-operator">Equal To (==) Operator</h4>
<p>This operator compares the values of two variables. If the values are the same, it returns <code>true</code>. If the values are not the same, it returns <code>false</code>. Here's an example:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> y = <span class="hljs-number">5</span>; 

Serial.print(x == y)
<span class="hljs-comment">// returns 0</span>
</code></pre>
<p>The return value of <code>x == y</code> in the code above is 0 (<code>false</code>) because the two variables are not the same. Remember that the <code>==</code> operator returns 1 (<code>true</code>) only when both variables have the same value.</p>
<h4 id="heading-not-equal-operator">Not Equal (!=) Operator</h4>
<p>The not equal operator checks whether two values have different values. It's does the opposite of the <code>==</code> operator. That means it'll return 1 (<code>true</code>) if both values are not of the same value and 0 (<code>false</code>) if both values are the same.</p>
<p>Here's an example:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> y = <span class="hljs-number">5</span>; 

Serial.print(x != y)
<span class="hljs-comment">// returns 1</span>
</code></pre>
<h4 id="heading-greater-than-gt-operator">Greater Than (&gt;) Operator</h4>
<p>The greater than (<code>&gt;</code>) operator checks if the operand on the left is greater than the operand on the right. If the left operand is greater, it returns 1. If the left operand is smaller, it returns 0.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> y = <span class="hljs-number">5</span>; 

Serial.print(x &gt; y)
<span class="hljs-comment">// returns 1</span>
</code></pre>
<h4 id="heading-less-than-lt-operator">Less Than (&lt;) Operator</h4>
<p>The less than (<code>&lt;</code>) operator checks if the operand on the left is less than the operand on the left. If the left operand is smaller, it returns 1. If the left operand is greater, it returns 0.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> y = <span class="hljs-number">5</span>; 

Serial.print(x &lt; y)
<span class="hljs-comment">// returns 0</span>
</code></pre>
<h4 id="heading-greater-than-or-equal-to-gt-operator">Greater Than or Equal To (&gt;=) Operator</h4>
<p>Just like the name, the <code>&gt;=</code> operator checks if the operand on the left is either greater than or equal to the operand on the right. It returns 1 if the left operand is greater than or equal to the right operand, and 0 if it isn't.</p>
<p>"Or" implies that either of the conditions can be used. If the left operand is not greater than the right operand but is equal to the right operand, you'll still get a value of 1.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> y = <span class="hljs-number">5</span>; 

Serial.print(x &gt;= y)
<span class="hljs-comment">// returns 1</span>
</code></pre>
<h4 id="heading-less-than-or-equal-to-lt-operator">Less Than or Equal To (&lt;=) Operator</h4>
<p>The <code>&lt;=</code> operator checks if the left operand is either less than or equal to the right operand. If the left operand is less than or equal to the right operand, it returns 1, and returns 0 if the left operand is neither less than nor equal to the right operand.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> y = <span class="hljs-number">5</span>; 

Serial.print(x &lt;= y)
<span class="hljs-comment">// returns 0</span>
</code></pre>
<h3 id="heading-ia"> </h3>
<p>Logical Operators</p>
<p>Logical operators are used in most programming languages to evaluate and determine the relationship between variables. </p>
<p>Here are the three logical operators you should know for Arduino programming:</p>
<h4 id="heading-logical-and-ampamp-operator">Logical AND (<code>&amp;&amp;</code>) Operator</h4>
<p>The logical AND (<code>&amp;&amp;</code>) operator returns 1 if both statements are true.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;

Serial.print((x &gt; <span class="hljs-number">5</span>) &amp;&amp; (x &gt; <span class="hljs-number">3</span>));
<span class="hljs-comment">// returns 1</span>
</code></pre>
<p>The expression above — <code>(x &gt; 5) &amp;&amp; (x &gt; 3)</code> — returns 1 because both statements are true. That is, <code>x &gt; 3</code> and <code>x &gt; 3</code>. If either or both of those statements were false, then we'd have a return value of 0.</p>
<h4 id="heading-logical-or-operator">Logical OR (||) Operator</h4>
<p>The logical OR (<code>||</code>) operator returns 1 if one of both statements is true.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;

Serial.print((x &gt; <span class="hljs-number">5</span>) &amp;&amp; (x &gt; <span class="hljs-number">15</span>));
<span class="hljs-comment">// returns 1</span>
</code></pre>
<p>The code above returns 1 although one of the statements is false. This is because the <code>||</code> operator returns 1 if either or both statements are true.</p>
<h4 id="heading-logical-not-operator">Logical NOT (!) Operator</h4>
<p>The NOT (<code>!</code>) operator negates or reverses the value of its operand. If the operand statement is true, it returns false, and returns false if the operand is true.</p>
<p>Here's an example:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;

Serial.print(!(x &gt; <span class="hljs-number">5</span>));
<span class="hljs-comment">// returns 0</span>
</code></pre>
<p>The code above returns 0, but why? <code>x &gt; 5</code> is true so the expected result is 1.</p>
<p>We got 0 because the <code>!</code> operator reversed the return value of the operand from 0 to 1.</p>
<h2 id="heading-conditional-statements-in-arduino">Conditional Statements in Arduino</h2>
<p>You can use conditional statements to make decisions or execute code based on specific conditions. You can combine conditional statements and logic (like operators in the last section) to control how code is executed.</p>
<p>Let's take a look at some conditional statements and how to use them:</p>
<h3 id="heading-if-statement"><code>if</code> Statement</h3>
<p>The <code>if</code> statement is used to execute code if a condition is <code>true</code>. Here's what the syntax looks like:</p>
<pre><code><span class="hljs-keyword">if</span> (condition) {
    <span class="hljs-comment">// code to be executed if condition is true</span>
}
</code></pre><p>In the syntax above, <code>condition</code> denotes a specified logic. If the condition is <code>true</code> then the code in the curly brackets will be executed. Here's an example:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> x = <span class="hljs-number">5</span>;
<span class="hljs-keyword">if</span> (x &lt; <span class="hljs-number">10</span>) {
  Serial.print(<span class="hljs-string">"x is less than 10"</span>);
}

<span class="hljs-comment">// x is less than 10</span>
</code></pre>
<p>In the code above, we gave a condition— <code>x &lt; 10</code> — and a block of code within curly brackets that prints "x is less than 10". The code in the curly brackets will only run if the condition is true.</p>
<p>This is the same as saying "if x is less than 10 then print 'x is less than 10' to the serial monitor". Since x is less than 10, the condition evaluates as <code>true</code> and we get the message printed out.</p>
<p>But what if the condition is <code>false</code>? The code in the curly brackets won't run, so we'll need a different type of logic to handle situations like that. We can do this using the <code>else</code> statement.</p>
<h3 id="heading-else-statement"><code>else</code> Statement</h3>
<p>The <code>else</code> statement is used to execute code if a condition is <code>false</code>.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> score = <span class="hljs-number">20</span>;
<span class="hljs-keyword">if</span> (score &gt; <span class="hljs-number">50</span> ) {
  Serial.print(<span class="hljs-string">"You passed the exam!"</span>);
} <span class="hljs-keyword">else</span> {
  Serial.print(<span class="hljs-string">"You have to rewrite the exam!"</span>);
}

<span class="hljs-comment">// You have to rewrite the exam</span>
</code></pre>
<p>In the code above, the condition given is <code>false</code>. So the code for the <code>else</code> statement will be executed because the <code>score</code> variable is not greater than 50.</p>
<p>Remember: the code for the <code>else</code> statement only runs when the condition is <code>false</code>. If the condition is <code>true</code> then the code for the <code>if</code> statement will be executed.</p>
<h3 id="heading-else-if-statement"><code>else if</code> Statement</h3>
<p>You can use the <code>else if</code> statement to define multiple conditions to be checked. Here's the syntax:</p>
<pre><code><span class="hljs-keyword">if</span> (condition1) {
    <span class="hljs-comment">// code to be executed if condition1 is true</span>
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (condition2){
    <span class="hljs-comment">// code to be executed if condition2 is true</span>
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// code to be executed if condition1 and condition2 are false</span>
}
</code></pre><p>In the syntax above, there are two conditions (you can create more than two conditions). If <code>condition1</code> is <code>true</code>, then code in the curly bracket for <code>condition1</code> will be executed.</p>
<p>If condition1 is <code>false</code>, then <code>condition2</code> will be evaluated. If <code>condition2</code> is <code>true</code>, its block of code will be executed.</p>
<p>If both <code>condition1</code> and <code>condition2</code> are <code>false</code>, the <code>else</code> statement's code will be executed.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> score = <span class="hljs-number">80</span>;
<span class="hljs-keyword">if</span> (score &gt; <span class="hljs-number">50</span> ) {
  Serial.print(<span class="hljs-string">"You passed the exam!"</span>);
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (score &lt; <span class="hljs-number">50</span>) {
  Serial.print(<span class="hljs-string">"You have to rewrite the exam!"</span>);
} <span class="hljs-keyword">else</span> {
  Serial.print(<span class="hljs-string">"No records for your exam score found!"</span>);
}

<span class="hljs-comment">// You passed the exam!</span>
</code></pre>
<h3 id="heading-switch-case-statement"><code>switch-case</code> Statement</h3>
<p>In the last section, we saw how to create multiple conditions using <code>else if</code> statements. Your code might become hard to read if you have many conditions. We can clean it up and make the code more readable using <code>switch</code> statements.</p>
<p>Here's what the syntax looks like:</p>
<pre><code><span class="hljs-keyword">switch</span> (expression) {
    <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
        <span class="hljs-comment">// Code to be executed if expression equals case 1</span>
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
        <span class="hljs-comment">// Code to be executed if expression equals case 2</span>
        <span class="hljs-keyword">break</span>;
     <span class="hljs-keyword">case</span> <span class="hljs-number">3</span>:
        <span class="hljs-comment">// Code to be executed if expression equals case 3</span>
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">default</span>:
        <span class="hljs-comment">// Code to be executed if expression doesn't match any case</span>
        <span class="hljs-keyword">break</span>;
}
</code></pre><p>Let's break the syntax down:</p>
<ul>
<li>The <code>expression</code> is compared to the value of each <code>case</code>.</li>
<li>When a <code>case</code> matches the <code>expression</code>, the code for that case is executed.</li>
<li>The <code>break</code> keyword stops the <code>switch</code> statement's iteration once a match for the <code>expression</code> has been found.</li>
<li>The code for the <code>default</code> keyword is executed if none of the cases match the <code>expression</code>.</li>
</ul>
<p>Here's an example:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> day = <span class="hljs-number">2</span>;

<span class="hljs-keyword">switch</span> (day) {
  <span class="hljs-keyword">case</span> <span class="hljs-number">1</span>:
    Serial.print(<span class="hljs-string">"Monday"</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> <span class="hljs-number">2</span>:
    Serial.print(<span class="hljs-string">"Tuesday"</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> <span class="hljs-number">3</span>:
    Serial.print(<span class="hljs-string">"Wednesday"</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> <span class="hljs-number">4</span>:
    Serial.print(<span class="hljs-string">"Thursday"</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> <span class="hljs-number">5</span>:
    Serial.print(<span class="hljs-string">"Friday"</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> <span class="hljs-number">6</span>:
    Serial.print(<span class="hljs-string">"Saturday"</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">case</span> <span class="hljs-number">7</span>:
    Serial.print(<span class="hljs-string">"Sunday"</span>);
    <span class="hljs-keyword">break</span>;
  <span class="hljs-keyword">default</span>:
    Serial.print(<span class="hljs-string">"Number out of range"</span>);
  }

<span class="hljs-comment">//  Tuesday</span>
</code></pre>
<p>The code above prints "Tuesday" because the <code>expression</code> which has a value of 2 matches <code>case 2</code>.</p>
<h2 id="heading-loops-in-arduino">Loops in Arduino</h2>
<p>You can use loops to execute code repeatedly until a certain condition is met. You can also use loops to iterate over a collection of data and execute code on all elements of the collection.</p>
<p>There are different type of loops you can use in Arduino like the <code>for loop</code>, <code>while loop</code>, and <code>do-while</code> loop. Let's take a look at their syntax along with some practical examples:</p>
<h3 id="heading-for-loop"><code>for</code> loop</h3>
<p>You can use the <code>for loop</code> to iterate through a collection or execute code until a certain condition is met. It is commonly used when you know the number of times the loop is supposed to run.</p>
<p>Here's the syntax:</p>
<pre><code><span class="hljs-keyword">for</span> (initialization; condition; increment/decrement) {
   <span class="hljs-comment">// code to be executed</span>
}
</code></pre><p>There are three important keywords in the syntax above:</p>
<ul>
<li><strong>initialization</strong> denotes an initial variable (usually an integer) which specifies the starting point of the loop.</li>
<li><strong>condition</strong> is used to control the number of times the loop is expected to run for. The loop stops when the condition is <code>false</code>.</li>
<li><strong>increment/decrement</strong> increases/decreases the value of the initial variable after every iteration.</li>
</ul>
<p>Here's an example to help you understand the keywords:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">11</span>; i++){
  Serial.println(i);
}

<span class="hljs-comment">// 0</span>
<span class="hljs-comment">// 1</span>
<span class="hljs-comment">// 2</span>
<span class="hljs-comment">// 3</span>
<span class="hljs-comment">// 4</span>
<span class="hljs-comment">// 5</span>
<span class="hljs-comment">// 6</span>
<span class="hljs-comment">// 7</span>
<span class="hljs-comment">// 8</span>
<span class="hljs-comment">// 9</span>
<span class="hljs-comment">// 10</span>
</code></pre>
<p>In the loop above, we created an initial variable called <code>1</code> with a value of 0.</p>
<p>The condition stated <code>i &lt; 11</code> which implies that the loop will continue to run as long as <code>i</code> is less than 11.</p>
<p>Using the increment operator <code>i++</code>, we increased the value of <code>i</code> by 1 every time the loop ran.</p>
<p>Lastly, we printed the value of <code>i</code> at every iteration. In the serial monitor, you'll notice the numbers from 0 to 10 printed out. This is because after the number 10, <code>i</code> is no longer less than 11 (the condition given), so the loop terminates.</p>
<h3 id="heading-while-loop"><code>while</code> loop</h3>
<p>The <code>while</code> loop works just like the <code>for</code> loop — it executes code as long as the given condition is <code>true</code>. But its often used when the number of times the loop is supposed to run is unknown.</p>
<p>Here's the syntax:</p>
<pre><code><span class="hljs-keyword">while</span> (condition) {
    <span class="hljs-comment">// Code to be executed</span>
}
</code></pre><p>In the syntax above, the code will continue to run until the <code>condition</code> becomes <code>false</code>.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">while</span> (i &lt; <span class="hljs-number">11</span>) {
  Serial.println(i);
  i++;
}

<span class="hljs-comment">// 0</span>
<span class="hljs-comment">// 1</span>
<span class="hljs-comment">// 2</span>
<span class="hljs-comment">// 3</span>
<span class="hljs-comment">// 4</span>
<span class="hljs-comment">// 5</span>
<span class="hljs-comment">// 6</span>
<span class="hljs-comment">// 7</span>
<span class="hljs-comment">// 8</span>
<span class="hljs-comment">// 9</span>
<span class="hljs-comment">// 10</span>
</code></pre>
<h3 id="heading-do-while-loop"><code>do-while</code> Loop</h3>
<p>The <code>do-while</code> loop is just like the <code>while</code> loop, but it executes its code block first before checking the validity of the condition given. That is, at the beginning of the loop, the code in curly brackets will run first even if the condition is <code>false</code>. After that, it starts checking if the condition is <code>true</code> or <code>false</code>, just like a normal loop.</p>
<p>In summary, the code for a <code>do-while</code> loop will run at least once, irrespective of the condition given. Here's an example:</p>
<pre><code><span class="hljs-keyword">do</span> {
  <span class="hljs-comment">// code block to be executed</span>
}
<span class="hljs-keyword">while</span> (condition);
</code></pre><p>Here's the code example:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;

<span class="hljs-keyword">do</span> {
  Serial.println(i);
  i++;
} <span class="hljs-keyword">while</span> ( i &lt; <span class="hljs-number">11</span>);

<span class="hljs-comment">// 0</span>
<span class="hljs-comment">// 1</span>
<span class="hljs-comment">// 2</span>
<span class="hljs-comment">// 3</span>
<span class="hljs-comment">// 4</span>
<span class="hljs-comment">// 5</span>
<span class="hljs-comment">// 6</span>
<span class="hljs-comment">// 7</span>
<span class="hljs-comment">// 8</span>
<span class="hljs-comment">// 9</span>
<span class="hljs-comment">// 10</span>
</code></pre>
<h2 id="heading-arrays-in-arduino">Arrays in Arduino</h2>
<p>You can use arrays in Arduino to store multiple variables of the same data type in a single variable. Each element stored in an array can be accessed using its index number.</p>
<h3 id="heading-array-declaration">Array Declaration</h3>
<p>Declaring an array simply means to create one. You can do that in Arduino using the syntax below:</p>
<pre><code>dataType arrayName[arraySize]
</code></pre><p>In the syntax above:</p>
<ul>
<li><code>dataType</code> represents the data types that'll be stored in the array. For instance, if the data type is <code>int</code>, then only integers can be stored in the array.</li>
<li><code>arrayName</code> denotes the name of the array.</li>
<li><code>arraySize</code> denotes the number of elements that can be stored in the array.</li>
</ul>
<p>Here's an array declaration code example:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> ages[<span class="hljs-number">4</span>];
</code></pre>
<p>In the code above, we created an array called <code>ages</code>. The array can only store four elements with an <code>int</code> data type.</p>
<h3 id="heading-array-initialization">Array Initialization</h3>
<p>To initialize an array means to assign values to the array. In the last section, we created an array called <code>ages</code>. Now, let's assign some elements to it:</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> ages[<span class="hljs-number">4</span>] = {<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">6</span>, <span class="hljs-number">8</span>};
</code></pre>
<p>You can see from the example above that there are only four elements in the array — 2, 4, 6, 8. Assigning a fifth element would throw an error because we specified that the array can only have for integer elements: <code>int ages[4];</code>.</p>
<p>You can access the elements of the array using their index number. Indexes start at zero (0) – so the first element has an index of 0, the second element has an index of 1, the third element has an index of 2, and so on.</p>
<pre><code class="lang-c++"><span class="hljs-keyword">int</span> ages[<span class="hljs-number">4</span>] = {<span class="hljs-number">2</span>, <span class="hljs-number">4</span>, <span class="hljs-number">6</span>, <span class="hljs-number">8</span>};

Serial.print(ages[<span class="hljs-number">0</span>]);
<span class="hljs-comment">// 2</span>
</code></pre>
<p>As can be seen above, we accessed the first element using the array name and the index of the element in square brackets: <code>ages[0]</code>.</p>
<p>You can also assign and reassign values to a particular element using its index:</p>
<pre><code class="lang-c++">ages[<span class="hljs-number">0</span>] = <span class="hljs-number">10</span>;
</code></pre>
<p>Note that you can declare and initialize an array at the same time. I only divided them into separate sections to help you understand what each term means.</p>
<h2 id="heading-functions-in-arduino">Functions in Arduino</h2>
<p>In the last chapter, we discussed some built-in functions in Arduino that can be used to carry out a variety of tasks related to Arduino hardware and software components. All we did was write the function name and pass in parameters where necessary and we got the desired outcome.</p>
<p>For instance, the <code>digitalWrite()</code> function writes values to digital pins using two parameters (the pin number and the value to be sent to the pin). Under the hood, some code logic handles that operation.</p>
<p>Let's assume that the logic required to send values to digital pins was up to a hundred lines of code. Without functions, you'll have to write those hundred lines every time you wanted to send values to digital pins.</p>
<p>Functions prevent you from having to reinvent the wheel. They also help you break your code down into smaller, more readable and manageable parts.</p>
<p>Just like how built-in functions can be reused to perform a particular task repeatedly, you can also create your own functions for specific functionalities, and that's exactly what you'll learn in this chapter.</p>
<h3 id="heading-how-to-declare-functions-in-arduino">How to Declare Functions in Arduino</h3>
<p>There are four main parts of a function in Arduino:</p>
<ul>
<li>The type of data the function returns.</li>
<li>The name of the function.</li>
<li>The function parameter(s) which is optional.</li>
<li>The body of the function.</li>
</ul>
<p>Here's what that looks like:</p>
<pre><code>dataType functionName(optionalParameters) {
    <span class="hljs-comment">// body of the function</span>
}
</code></pre><p>So from the syntax above, <code>dataType</code> is the data type the function returns. It can be <code>int</code>, <code>String</code>, and so on. A function that has no <code>return</code> statement uses the <code>void</code> type as its data type.</p>
<p>The <code>functionName</code> is the name given to the function. The name is used to call the function to execute the logic defined in the body of the function. You'd see words like "call", "fire", and "invoke" associated with functions. They all mean the same thing — to execute the function's logic.</p>
<p><code>optionalParameters</code> are variables that you define when creating a function. They enable functions to accept external data which can be used within the function body. Function parameters are defined along with their data types. You'll understand this when we look at some examples.</p>
<p>The body of the function is where all the logic goes to. What the function does when it is invoked is written in the body.</p>
<p>Now that we've seen the different parts of a function, let's create some functions!</p>
<h3 id="heading-how-to-declare-a-function-with-the-void-type">How to Declare a Function with the <code>void</code> Type</h3>
<p>In the last chapter, we discussed the <code>void Setup()</code> and <code>void loop()</code> functions. They are two built-in functions that you'll use in every Arduino sketch. These functions are defined using the <code>void</code> keyword because they return nothing</p>
<p>Here's what the syntax looks like for functions that use the <code>void</code> type:</p>
<pre><code><span class="hljs-keyword">void</span> functionName(optionalParameters) {
    <span class="hljs-comment">// code logic</span>
}
</code></pre><p>In the syntax above, <code>functionName</code> denotes the name of the function. We can use that name to call the function in order to execute the code defined in the function.</p>
<p><code>optionalParameters</code> are used to pass external data to the function while the code logic that runs when the function is called is written between the curly brackets.</p>
<p>Here's an example:</p>
<pre><code class="lang-c++"><span class="hljs-comment">// function declaration</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">printName</span><span class="hljs-params">(String userName)</span> </span>{
  Serial.println(<span class="hljs-string">"Hello "</span> + userName);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  printName(<span class="hljs-string">"Ihechikara"</span>); <span class="hljs-comment">// function call</span>
  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>In the code above, we created a function called <code>printName</code> which accepts a string parameter called <code>userName</code>. The function's task is to print "Hello " along with whatever the parameter value is.</p>
<p>In the <code>void loop()</code>, we called the function and passed a parameter to it: <code>printName("Ihechikara")</code>. In the serial monitor, you'll see "Hello Ihechikara" printed.</p>
<p>To call a function, all you have to do is write the name of the function with parenthesis: <code>printName()</code>. Remember to pass in parameters when required: <code>printName("Ihechikara")</code>.</p>
<p>Using a parameter that has the wrong data type will result in an error. For instance, we defined a string parameter in our example. Using an integer will raise an error because the function expects a string.</p>
<h3 id="heading-how-to-declare-a-function-with-a-return-data-type">How to Declare a Function with a Return Data Type</h3>
<p>In this section, I'll use the <code>int</code> data type to show you how functions declared without the <code>void</code> type are used. The logic here is the same with other functions that use the <code>return</code> statement.</p>
<pre><code class="lang-c++"><span class="hljs-comment">// function declaration</span>
<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">addNums</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{

  <span class="hljs-keyword">int</span> result = a + b;
  <span class="hljs-keyword">return</span> result;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  Serial.println(addNums(<span class="hljs-number">2</span>, <span class="hljs-number">10</span>)); <span class="hljs-comment">// function call</span>
  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>In the code above, we declared a function with the <code>int</code> type: <code>int addNums(int a, int b) {...}</code>. This implies that the function is expected to return an integer value.</p>
<p>The function's logic adds the value of the two parameters (<code>a</code> and <code>b</code>) and returns their sum. We used the <code>return</code> statement to return the sum of the parameters.</p>
<p>We can now say that the task of the <code>addNums</code> function is to return the sum of two given parameters. This can be seen when we used the function in the <code>void loop()</code>:</p>
<pre><code class="lang-c++">Serial.println(addNums(<span class="hljs-number">2</span>, <span class="hljs-number">10</span>));
</code></pre>
<p>We called the function with two parameters and got their sum printed out in the serial monitor.</p>
<h3 id="heading-what-you-should-know-about-the-return-statement">What You Should Know About the <code>return</code> Statement</h3>
<p>In the last two sections, we saw how to use functions in two different ways — functions that use the <code>return</code> statement and functions that don't use it.</p>
<p>But what if you used the <code>return</code> statement in a <code>void</code> function? Would that break the code? The answer is no. I'll explain why.</p>
<p>The main use of the <code>return</code> keyword is to return a value from the function, and then terminate the function. Consider the example below:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">addNums</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{

  <span class="hljs-keyword">int</span> result = a + b;
  Serial.println(result);
  <span class="hljs-keyword">return</span> result;

  <span class="hljs-comment">// This part will be ignored</span>
  Serial.println(<span class="hljs-string">"Hello World"</span>);
}
</code></pre>
<p>The function above takes in two parameters — a and b — and returns their sum. You'll notice that we printed "Hello World" after the <code>return</code> statement. The part of the code that comes after the <code>return</code> statement will not be executed because the function terminates/stops its operation once it sees a <code>return</code> statement.</p>
<p>So you should always remember that anything that comes after the <code>return</code> statement will be ignored.</p>
<p>You can use the <code>return</code> statement in <code>void</code> functions but it is a convention not to. We simply use the <code>void</code> keyword to define functions that have no use for the <code>return</code> statement.</p>
<h2 id="heading-commonly-used-built-in-functions-in-arduino-sketch">Commonly Used Built-in Functions in Arduino Sketch</h2>
<p>In this section, we’ll discuss some of the commonly used built-in functions you’ll come across when writing or reading Arduino code. We'll make use of them in most of the upcoming chapters of this handbook.</p>
<p>We'll begin with the two main parts of an Arduino sketch — the <code>setup()</code> and <code>loop()</code> functions.</p>
<h3 id="heading-setup-and-loop-functions-in-arduino"><code>setup()</code> and <code>loop()</code> Functions in Arduino</h3>
<p>You can use the <code>setup()</code> function to configure analog and digital pins, initialize variables, and do other setup functionalities. The <code>setup()</code> function is executed once — when the board is powered on or reset.</p>
<p>The <code>loop()</code> function runs continuously. This part of the sketch is where you write all the code logic. You can use the <code>loop()</code> function to give the Arduino board instructions on different components and sensors.</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-comment">// put your setup code here, to run once:</span>

}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-comment">// put your main code here, to run repeatedly:</span>

}
</code></pre>
<h3 id="heading-pinmode-function-in-arduino"><code>pinMode()</code> Function in Arduino</h3>
<p>The <code>pinMode()</code> function is used to configure pins as input or output pins. It can also be used to configure a resistor to act as either a pull-up or pull-down resistor. You'll understand more about this function in the Sensors and Actuators chapter.</p>
<h4 id="heading-syntax">Syntax</h4>
<pre><code>pinMode(pin, mode)
</code></pre><ul>
<li><code>pin</code> denotes the pin number on an Arduino board.</li>
<li><code>mode</code> denotes the configuration of the <code>pin</code> which can be INPUT, OUTPUT, or INPUT_PULLUP.</li>
</ul>
<h3 id="heading-digitalread-function-in-arduino"><code>digitalRead()</code> Function in Arduino</h3>
<p>You can use the <code>digitalRead()</code> function to read the state of digital pins. It returns either 0 (<code>LOW</code>) or 1 (<code>HIGH</code>).</p>
<h4 id="heading-syntax-1">Syntax</h4>
<pre><code>digitalRead(pin)
</code></pre><p>In the code above, <code>pin</code> denotes the pin number on an Arduino board.</p>
<h3 id="heading-digitalwrite-function-in-arduino"><code>digitalWrite()</code> Function in Arduino</h3>
<p>The <code>digitalWrite()</code> function assigns or writes values (either <code>HIGH</code> or <code>LOW</code>) to digital pins.</p>
<h4 id="heading-syntax-2">Syntax</h4>
<pre><code>digitalWrite(pin, value)
</code></pre><ul>
<li><code>pin</code> denotes the pin number on an Arduino board.</li>
<li><code>value</code> denotes the value to be assigned to <code>pin</code>. Can be <code>HIGH</code> or <code>LOW</code>.</li>
</ul>
<h3 id="heading-analogread-function-in-arduino"><code>analogRead()</code> Function in Arduino</h3>
<p>The <code>analogRead()</code> function reads values from analog pins and returns values that fall within the range of 0 and 1023.</p>
<h4 id="heading-syntax-3">Syntax</h4>
<pre><code>analogRead(pin)
</code></pre><p>In the code above, <code>pin</code> denotes the pin number on an Arduino board.</p>
<h3 id="heading-analogwrite-function-in-arduino"><code>analogWrite()</code> Function in Arduino</h3>
<p>This function writes or assigns an analog value to a pin.</p>
<h4 id="heading-syntax-4">Syntax</h4>
<pre><code>analogWrite(pin, value)
</code></pre><ul>
<li><code>pin</code> denotes the pin number on an Arduino board.</li>
<li><code>value</code> denotes the value to be assigned to <code>pin</code>. Range from 0 to 255.</li>
</ul>
<h2 id="heading-serial-functions-in-arduino">Serial Functions in Arduino</h2>
<p>Serial communication enables an Arduino board to communicate with the computer and other devices using the built-in serial monitor. Here are some of the commonly used functions:</p>
<h3 id="heading-serialbegin"><code>Serial.begin()</code></h3>
<p>The <code>Serial.begin()</code> function initializes serial communication. It is the first function you use when working with the serial monitor. The function takes in a baud rate as its parameter.</p>
<p>In this case, baud rate represents the rate or speed of data transfer in serial communication.</p>
<p>Here's the syntax:</p>
<pre><code>Serial.begin(baudRate)
</code></pre><h3 id="heading-serialprint-and-serialprintln"><code>Serial.print()</code> and <code>Serial.println()</code></h3>
<p>You can use the <code>print()</code> and <code>println()</code> functions to print data to the serial monitor.</p>
<pre><code class="lang-cpp">print(val)
println(val)
</code></pre>
<p>In the code above, <code>val</code> denotes the value to be printed.</p>
<p>We'll talk more about serial communication in <a class="post-section-overview" href="#heading-chapter-6-how-to-use-the-serial-monitor-in-arduino-2">Chapter 6: How to use the Serial Monitor in Arduino</a>.</p>
<h3 id="heading-delay-function-in-arduino"><code>delay()</code> Function in Arduino</h3>
<p>You can use the <code>delay()</code> function to pause the Arduino program for a specified amount of time. Here's what the syntax looks like:</p>
<pre><code>delay(ms)
</code></pre><p>In the code above, <code>ms</code> denotes the specified time in milliseconds.</p>
<h1 id="heading-chapter-3-how-to-use-digital-pins-in-arduino">Chapter 3: How to Use Digital Pins in Arduino</h1>
<p>Digital pins are used to send and receive digital signals in two states — <code>HIGH</code> and <code>LOW</code>. The digital pins on a Arduino board can be configured as either input or output pins.</p>
<p>These states can also be represented using numbers (1 for <code>HIGH</code> and 0 for <code>LOW</code>), or in volts (V) (5V for <code>HIGH</code> and 0V for <code>LOW</code>).</p>
<p>The number and arrangement of pins differ in various Arduino boards, but they serve the same purpose. So if you understand how to use them in this chapter, you won't have a problem working with them in other boards.</p>
<p>The Uno board has 14 digital pins numbered from 0 to 13. Although each pin can be configured to serve as either a digital input or output pin, some of them come with extra functionalities like:</p>
<ul>
<li>Pins 0 (RX) and 1 (TX) enable the Arduino board to communicate serially. RX receives while TX sends.</li>
<li>Pins that have the tilde (~) symbol beside them support PWM (Pulse Width Modulation) signals. This means that you can use these pins like analog pins (to receive analog values).</li>
<li>Pins 2 and 3 can be used for interrupt-based functionalities.</li>
</ul>
<h2 id="heading-how-to-configure-digital-pins-as-input-or-output-pins">How to Configure Digital Pins as Input or Output Pins</h2>
<p>When a digital pin is configured as an INPUT pin, it serves as a point for receiving information from components. This way you get data from sensors, electronic components, and so on.</p>
<p>You can use the <code>pinMode()</code> function to configure a pin to serve as either an INPUT or OUTPUT pin. Note that the pins on an Uno board are set to INPUT by default so if you don't specify prior to using them, they'll serve as input pins.</p>
<p>In this section, you'll learn how to use digital pins as both input and output pins. You'll start by learning about them individually, and then see how to combine input and output signals to build a mini project.</p>
<p>We'll start with INPUT.</p>
<h3 id="heading-digital-pins-as-input">Digital Pins as INPUT</h3>
<p>The information or signal sent to input pins can be read using the <code>digitalRead()</code> function. In this section, you'll learn how to configure and read signals from a digital input pin using different built-in functions.</p>
<p>We'll use the following hardware components:</p>
<ul>
<li>Arduino Uno.</li>
<li>Breadboard.</li>
<li>Pushbutton.</li>
<li>Jumper wires.</li>
</ul>
<p>Here's the diagram for the connection:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/circuit-diagram-digital-input-pushbtn.png" alt="Image" width="600" height="400" loading="lazy">
<em>Configuration diagram – INPUT</em></p>
<p>Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> pushBtn = <span class="hljs-number">7</span>;
<span class="hljs-keyword">int</span> push_btn_state;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  pinMode(pushBtn, INPUT_PULLUP);
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
  push_btn_state = digitalRead(pushBtn);
  Serial.println(push_btn_state);
  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>Let's break the code down.</p>
<p>We started by creating two integer variables — <code>pushBtn</code> and <code>push_btn_state</code>:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> pushBtn = <span class="hljs-number">7</span>;
<span class="hljs-keyword">int</span> push_btn_state;
</code></pre>
<p><code>pushBtn</code> was assigned a value of 7. We used this value to denote pin 7 on the Arduino board. We declared the <code>push_btn_state</code> variable but didn't assign any value to it because we'll use it to store the value of the push button later.</p>
<p>In our <code>setup()</code> function, we configured the push button to act as an input pin using the <code>pinMode()</code> function:</p>
<pre><code class="lang-cpp">pinMode(pushBtn, INPUT_PULLUP);
</code></pre>
<p>The function took in two parameters — the <code>pushBtn</code> variable which denoted pin 7, and <code>INPUT_PULLUP</code> which sets the pin as an input with a pull-up resistor.</p>
<p>We also initialized the serial monitor using <code>Serial.begin(9600)</code>.</p>
<p>At this point, we've configured the Arduino software and hardware to recognize pin 7 as an input pin.</p>
<p>Next, we used the <code>digitalRead()</code> function to read signals coming from pin 7. Remember the <code>push_btn_state</code> variable we created? That's where we stored the signal:</p>
<pre><code class="lang-cpp">push_btn_state = digitalRead(pushBtn);
</code></pre>
<p>After that, we printed the value being read from pin 7 to the serial monitor using <code>Serial.println(push_btn_state);</code>.</p>
<p>When you open the serial monitor, you'll see 1 being printed out repeatedly. This is the initial state of the pushbutton using a pull-up resistor. When you press down the pushbutton, the value will become 0. When you release the button, the value will become 1.</p>
<p>0 denotes <code>LOW</code> while 1 denotes <code>HIGH</code>. With this example, you should understand how to configure, read, and display signals from an input pin.</p>
<h3 id="heading-digital-pins-as-output">Digital Pins as OUTPUT</h3>
<p>The main use of an output pin is to send out signals. Since we're working with digital output, we can send either <code>HIGH</code> (5V) or <code>LOW</code> (0V) signals. We can do this for digital pins using the <code>digitalWrite()</code> function.</p>
<p>In this section, we'll use a LED (Light Emitting Diode) to demonstrate how to configure and send signals to components.</p>
<p>Here are the components we'll use:</p>
<ul>
<li>Arduino Uno.</li>
<li>Red LED.</li>
<li>1k Ohm resistor.</li>
<li>Jumper wires.</li>
<li>Breadboard.</li>
</ul>
<p>Here's the circuit diagram:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/circuit-diagram-digital-output-led.png" alt="Image" width="600" height="400" loading="lazy">
<em>Configuration diagram – OUTPUT</em></p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> RedLED = <span class="hljs-number">8</span>;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  pinMode(RedLED, OUTPUT);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
  digitalWrite(RedLED, HIGH);
  delay(<span class="hljs-number">1000</span>);
  digitalWrite(RedLED, LOW);
  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>In the code above, we configured the red LED, which is connected to pin 8 on the Uno board, as an output pin using the <code>pinMode()</code> function.</p>
<p>We then used the <code>digitalWrite()</code> function to send signals to the pin:</p>
<pre><code class="lang-cpp">  digitalWrite(RedLED, HIGH);
  delay(<span class="hljs-number">1000</span>);
  digitalWrite(RedLED, LOW);
  delay(<span class="hljs-number">1000</span>);
</code></pre>
<p>With the <code>HIGH</code> parameter, we send 5V to the pin which makes the LED come on. With <code>LOW</code>, we send 0V which turns the LED off. So the LED comes on and off continuously with a delay of 1000 milliseconds. This example is commonly known as the "BLINK" example.</p>
<h2 id="heading-digital-io-project">Digital I/O Project</h2>
<p>Now that we've understood how to send and receive digital signals using Arduino, let's combine both of them to build an interactive project.</p>
<p>The idea is to control an LED using a pushbutton. When you press the button, the LED goes off, and comes back on when you release the button.</p>
<p>We'll combine the components used in the previous examples:</p>
<ul>
<li>Arduino Uno.</li>
<li>Red LED.</li>
<li>1k Ohm resistor.</li>
<li>Pushbutton.</li>
<li>Jumper wires.</li>
<li>Breadboard.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/digital-IO-project.png" alt="digital-IO-project" width="600" height="400" loading="lazy">
<em>Configuration diagram - INPUT and OUTPUT project</em></p>
<p>Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> pushBtn = <span class="hljs-number">7</span>;
<span class="hljs-keyword">int</span> push_btn_state;
<span class="hljs-keyword">int</span> RedLED = <span class="hljs-number">8</span>;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  pinMode(pushBtn, INPUT_PULLUP);
  pinMode(RedLED, OUTPUT);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
  push_btn_state = digitalRead(pushBtn);

  <span class="hljs-keyword">if</span> (push_btn_state == <span class="hljs-number">1</span>) {
    digitalWrite(RedLED, HIGH);
  } <span class="hljs-keyword">else</span> {
    digitalWrite(RedLED, LOW);
  }
}
</code></pre>
<p>Here's a breakdown of the code above:</p>
<p>First, we connected the pushbutton to pin 7 and the red LED to pin 8.</p>
<p>Then we configured both pins — pin 7 as input, and pin 8 as output.</p>
<p>Next, we read the value of the pushbutton using the <code>digitalRead()</code> function and stored the value in a variable called <code>push_btn_state</code>.</p>
<p>Using an <code>if</code> statement, we checked for the state of the pushbutton. When <code>push_btn_state</code> is not being pushed down, it has a value of 1 (<code>HIGH</code>). We send 5V to the LED using <code>digitalWrite()</code>.</p>
<p>When <code>push_btn_state</code> is pushed down, it has a value of 0, and we send 0V to the LED which turns it off.</p>
<h1 id="heading-chapter-4-how-to-use-analog-pins-in-arduino">Chapter 4: How to Use Analog Pins in Arduino</h1>
<p>Analog pins can be used to receive and send voltage values from/to different sensors and components. Unlike digital signals that fall within two states of 0 (<code>LOW</code>) and 1 (<code>HIGH</code>), analog values have a wider range of values from 0 to 1023.</p>
<p>The Uno board has six analog pins — A0, A1, A2, A3, A4, and A5. These pins are INPUT pins by default.</p>
<p>Similar to digital pins, there are in-built functions for receiving and sending analog voltage signals. You can use the <code>analogRead()</code> function to read/receive analog values from pins while the <code>analogWrite()</code> function can be used to write to specific pins.</p>
<p>Note that the <code>analogWrite()</code> function doesn't write or send analog values to analog pins. It sends analog voltage values (that get converted to digital signals) to digital pins that support PWM (Pulse Width Modulation).</p>
<p>You can find PWM digital pins on the Arduino board by the (~) symbol beside them. On the Uno board, there are pins 3, 5, 6, 9, 10, and 11.</p>
<p>In the project for this section, you'll learn how to adjust the brightness of an LED using a potentiometer. We'll use the potentiometer as an input component, and send its voltage to an LED to increase/decrease the LED's brightness.</p>
<p>Here are the components we'll use:</p>
<ul>
<li>Arduino Uno.</li>
<li>Yellow LED.</li>
<li>Potentiometer.</li>
<li>1k Ohm resistor.</li>
<li>Jumper wires.</li>
<li>Breadboard.</li>
</ul>
<p>Here's the circuit diagram:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/analog-IO.png" alt="Image" width="600" height="400" loading="lazy">
<em>Configuration diagram</em></p>
<p>Here's the code for the circuit:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> potentiometer = A0;
<span class="hljs-keyword">int</span> pot_value = <span class="hljs-number">0</span>;
<span class="hljs-keyword">float</span> pot_in_PWM;
<span class="hljs-keyword">int</span> yelowLED = <span class="hljs-number">6</span>;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  pinMode(potentiometer, INPUT);
  pinMode(yelowLED, OUTPUT);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
  pot_value = analogRead(potentiometer);
  pot_in_PWM = pot_value * (<span class="hljs-number">255.0</span> / <span class="hljs-number">1023.0</span>);

  analogWrite(yelowLED, pot_in_PWM);
}
</code></pre>
<p>In the code above, we set the potentiometer (connected to analog pin A0) as an input component. The yellow LED (connected to digital pin 6) was set as an output component. The LED is connected to a PWM pin because we'll be sending analog values to it.</p>
<p>Using the <code>analogRead()</code> function, we got the value of the potentiometer and stored it in a variable called <code>pot_value</code>. The potentiometer returns values from 0 to 1023.</p>
<p>We then converted the values returned by the potentiometer to a range of 0 to 255 which corresponds with PWM values. This is because the <code>analogWrite()</code> function sends PWM values that fall within that range (0 to 255). The new range of values was stored in a variable called <code>pot_in_PWM</code>.</p>
<p>Lastly, we used the <code>analogRead()</code> function to send PWM values to the LED: <code>analogWrite(yelowLED, pot_in_PWM)</code>.</p>
<p>When you verify and upload the code to your Arduino board, you'll be able to control the brightness of the LED by turning the potentiometer's knob.</p>
<h1 id="heading-chapter-5-how-to-use-sensors-and-actuators-in-arduino">Chapter 5: How to Use Sensors and Actuators in Arduino</h1>
<p>Sensors and actuators play a crucial role in developing projects using Arduino. They help microcontrollers get information about changes in the physical environment, and make decisions based on that information.</p>
<p>The goal of this chapter is to help you understand the difference between sensors and actuators, and how to use them. Of course, there are numerous sensors and actuators, but we'll focus on just a few. You should be able to explore other sensors on your own at the end of this chapter.</p>
<p>We'll first talk about what sensors are, types of sensors, and how to work with them using code. We'll then do the same for actuators, and conclude by looking at an example that uses both sensors and actuators in a project.</p>
<p>Let's get started!</p>
<h2 id="heading-what-are-sensors-in-arduino">What are Sensors in Arduino?</h2>
<p>A sensor is a device that listens for or detects changes in the environment. Sensors convert the information they detect in the physical environment to electrical signals which are then sent to the microcontroller (the brain of the board).</p>
<p>You can look at sensors like the human sense organs — we use them to gather information about our environment. Each sense organ (like eyes and ears) in the human body has a specific function and mode of operation that's different from other organs.</p>
<p>In the same manner, sensors in Arduino have their specific functions and use cases. For example, we have sensors that can measure and detect temperature, motion, moisture, and so on.</p>
<h3 id="heading-types-of-sensors-in-arduino">Types of Sensors in Arduino</h3>
<p>Here are some commonly used sensors you'll come across when working with Arduino:</p>
<ul>
<li><strong>Temperature sensor</strong>: Measures the temperature and humidity of the environment.</li>
<li><strong>Light-Dependent Resistor (LDR)</strong>: Measures/senses the intensity of light.</li>
<li><strong>Ultrasonic sensor</strong>: Measures the distance of an object from the sensor.</li>
<li><strong>Motion sensor</strong>: Generally detects motion by sensing changes in infrared energy/radiation.</li>
<li><strong>Soil moisture sensor</strong>: Measures the moisture level of the soil.</li>
<li><strong>Water sensor</strong>: Measures water level/detects water, and so on.</li>
</ul>
<p>There are other types of sensors that you can use with Arduino, but we'll just focus on two: the LDR (light-dependent resistor) and ultrasonic sensor. </p>
<p>With the examples in this section, you'll be able to explore how other sensors work on your own.</p>
<h3 id="heading-how-to-use-the-light-dependent-resistor-ldr-in-arduino">How to use the Light-Dependent Resistor (LDR) in Arduino</h3>
<p>The light-dependent resistor (LDR), also known as a photoresistor, is an electronic component that has varying levels of resistance depending on the intensity of light.</p>
<p>Basically, you can use this sensor to sense light. You can do cool things with it like creating an automated lighting system that turns on electric lights in your house when it's dark, or a weather station that tracks and monitors sunlight, and so on.</p>
<p>A LDR usually looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/ldr.png" alt="Image" width="600" height="400" loading="lazy">
<em>Diagram of an LDR</em></p>
<p>Here's what the circuit connection looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/ldr-circuit.png" alt="Image" width="600" height="400" loading="lazy">
<em>Configuration diagram</em></p>
<p>In the circuit above, one leg of the LDR is connected to 5V (volts). The other leg is connected to a 1K Ohm resistor — one end of the resistor is connected to ground (GND) while the other end is connected to an analog pin (A1 in our circuit).</p>
<p>Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> ldrPin = A1;
<span class="hljs-keyword">int</span> ldrValue;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  pinMode(ldrPin, INPUT);
  Serial.begin(<span class="hljs-number">9600</span>); 
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  ldrValue = analogRead(ldrPin);
  Serial.println(ldrValue);
  delay(<span class="hljs-number">1000</span>); 
}
</code></pre>
<p>In the code above, we created a variable called <code>ldrpin</code> with a value of <code>A1</code>. This denotes the connection made in the circuit where one leg of the LDR is connected to the analog pin. This pin will help us know the value of the sensor.</p>
<p>We then created an <code>ldrValue</code> variable which will be used to store the value of the sensor.</p>
<p>In the <code>setup()</code> function, we set the LDR as an INPUT pin. We also initialized the serial monitor.</p>
<p>Next, we read the value of the LDR using the <code>analogRead</code> function, and stored the value in the <code>ldrValue</code> variable:</p>
<pre><code class="lang-cpp">ldrValue = analogRead(ldrPin);
</code></pre>
<p>Lastly, we printed the read value to the serial monitor with a delay of 1000 milliseconds (one second).</p>
<p>At this point, if you increase the exposure of light on the LDR, the value increases. If you decrease the light intensity or cover the sensor to block off light, the value will decrease or become zero, respectively.</p>
<h2 id="heading-how-to-use-the-ultrasonic-sensor-in-arduino">How to Use the Ultrasonic Sensor in Arduino</h2>
<p>The ultrasonic sensor is generally used to measure the distance of objects. You can use this sensor in a lot of applications like:</p>
<ul>
<li>Home automation (you can perform certain actions when the presence of an object/human is sensed, like turning on lights in a dark room).</li>
<li>Automated doors.</li>
<li>Security systems.</li>
<li>Measurement of distance, and so on.</li>
</ul>
<p>Just like other sensors, there are various types. In this example, we'll use the HC-SR04 ultrasonic sensor. Don't worry, the working principle is the same. So if you understand how to use this one, you can configure and use other types of ultrasonic sensors you come across.</p>
<p>Here's what the circuit diagram looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/ultra-sensor.png" alt="Image" width="600" height="400" loading="lazy">
<em>Configuration diagram (from https://howtomechatronics.com/tutorials/arduino/ultrasonic-sensor-hc-sr04/)</em></p>
<p>The sensor has four pins — VCC, Trig, Echo, and GND.</p>
<ul>
<li>The VCC pin is connected to 5V on the Uno board</li>
<li>The Trig pin is connected to digital pin 9.</li>
<li>The Echo pin is connected to digital pin 10.</li>
<li>The GND pin is connected to GND on the Uno board.</li>
</ul>
<p>The trig pin is used to "trigger" the ultrasonic sensor while the echo pin is used to distance of objects based on the amount of time it takes for ultrasonic waves/signals to bounce back from an object.</p>
<p>Here's a code example:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> trigPin = <span class="hljs-number">9</span>; 
<span class="hljs-keyword">int</span> echoPin = <span class="hljs-number">10</span>;  

<span class="hljs-keyword">long</span> duration, distance;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(<span class="hljs-number">10</span>);
  digitalWrite(trigPin, LOW);

  duration = pulseIn(echoPin, HIGH);

  distance = (duration / <span class="hljs-number">2</span>) * <span class="hljs-number">0.0343</span>;

  Serial.print(<span class="hljs-string">"Distance: "</span>);
  Serial.print(distance);
  Serial.println(<span class="hljs-string">" cm"</span>);

  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>In the code above, the trig pin denotes digital pin 9, and the echo pin denotes digital pin 10.</p>
<p>We declared two variables — duration and distance — to be used to store their respective values.</p>
<p>We triggered the sensor by sending the <code>HIGH</code> signal to the trigger pin for 10 microseconds. Without this, the sensor might not work:</p>
<pre><code class="lang-cpp">  digitalWrite(trigPin, HIGH);
  delayMicroseconds(<span class="hljs-number">10</span>);
  digitalWrite(trigPin, LOW);
</code></pre>
<p>We measured and stored the duration of the ultrasonic pulse/signals using the <code>pulseIn(echoPin, HIGH)</code> function and stored it in the <code>duration</code> variable.</p>
<p>We then calculated the duration in centimeters and stored it in the <code>distance</code> variable.</p>
<p>Lastly, we printed the distance to the serial monitor with a delay of 1000 milliseconds.</p>
<p>At this point, you can place an object closer to or farther away from the sensor and see the value of the distance of the object change in the serial monitor.</p>
<h2 id="heading-what-are-actuators-in-arduino">What are Actuators in Arduino?</h2>
<p>Actuators in Arduino are components that convert electrical signals into physical/mechanical motion.</p>
<p>Here are some actuators:</p>
<ul>
<li>LED (Light Emitting Diode): Used as light/visual indicators.</li>
<li>Buzzer: Used to produce sound.</li>
<li>Relay modules: Used to control high voltage devices.</li>
<li>LCD (Liquid Crystal Display): Used as visual display for text, images, sensor data, and so on. We'll dedicate a separate chapter to displays.</li>
<li>Servo motors: Used to control angular or rotational motion (an example is the movement of a robotic arm).</li>
</ul>
<p>We'll focus on the buzzer and LED in this section.</p>
<h3 id="heading-how-to-use-leds-in-arduino">How to use LEDs in Arduino</h3>
<p>LEDs are usually the first components you learn about in Arduino. There are easy to connect and use.</p>
<p>Here's what an LED looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/redLED.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here's the circuit connection:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/led-circuit.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the circuit above, the longer leg of the LED (the anode or positive leg) is connected to digital pin 7. </p>
<p>The shorter leg (cathode) is connected to ground.</p>
<p>Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> redLED = <span class="hljs-number">7</span>;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  pinMode(redLED, OUTPUT);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  digitalWrite(redLED, HIGH);
  delay(<span class="hljs-number">1000</span>);
  digitalWrite(redLED, LOW);
  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>The code above will make the LED blink once the code has been uploaded.</p>
<h3 id="heading-how-to-use-a-buzzer-in-arduino">How to use a Buzzer in Arduino</h3>
<p>You can use buzzers to produce sound. There are generally two types of buzzers: active and passive buzzers.</p>
<p>Active buzzers usually have a predefined type of sound that they produce when voltage is supplied to them. The sound cannot be modified. Active buzzers have an internal circuit that triggers the sound production.</p>
<p>Passive buzzers are a bit more flexible when it comes to producing sound because they rely on external signals. This means that you can determine the frequency (or frequencies) of the sound produced by the buzzer.</p>
<p>Buzzers can be used in different applications like:</p>
<ul>
<li>Alarm systems.</li>
<li>Sound indictor for home appliances.</li>
<li>Doorbells.</li>
<li>Communication devices to denote the start/end of a signal communication, and so on.</li>
</ul>
<p>We'll work with the passive buzzer because of its flexibility. This is what it looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/buzzer.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The buzzer has a positive and negative terminal. The positive is connected to a digital pin while the negative is connected to ground.</p>
<p>Here's the circuit diagram:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/buzzer-circuit.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In this example, we'll use the buzzer to produce the DO, RE, MI, FA, SOL, LA, TI music notes sound.</p>
<p>Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> buzzerPin = <span class="hljs-number">7</span>;

<span class="hljs-keyword">int</span> notes[] = {<span class="hljs-number">262</span>, <span class="hljs-number">294</span>, <span class="hljs-number">330</span>, <span class="hljs-number">349</span>, <span class="hljs-number">392</span>, <span class="hljs-number">440</span>, <span class="hljs-number">494</span>};

<span class="hljs-keyword">int</span> noteDurations[] = {<span class="hljs-number">400</span>, <span class="hljs-number">400</span>, <span class="hljs-number">400</span>, <span class="hljs-number">400</span>, <span class="hljs-number">400</span>, <span class="hljs-number">400</span>, <span class="hljs-number">400</span>};

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  pinMode(buzzerPin, OUTPUT);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">8</span>; i++) {
    tone(buzzerPin, notes[i], noteDurations[i]);
    delay(noteDurations[i] + <span class="hljs-number">50</span>);
    noTone(buzzerPin);
  }

  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>In the code above, we used the <code>buzzerPin</code> variable to denote pin 7 on the Uno board.</p>
<p>We then created an array called <code>notes[]</code> which stores the respective frequency of each note, and another array called <code>noteDurations</code> which stores the duration of each note as they're being produced by the buzzer.</p>
<p>In the <code>void loop()</code> function, we looped through each note a played them using the <code>tone()</code> function. The function takes in three parameters — the pin connected to the buzzer, the frequency, and the duration.</p>
<p>We used the <code>noTone()</code> function to stop the generation of sound. Lastly, we added a delay of 1000 milliseconds which is the amount of time it'll take to play the notes from the start after the last note has been played.</p>
<p>If you've followed along up to this point, then you should have the DO, RE, MI, FA, SOL, LA, TI music notes playing through the buzzer.</p>
<h2 id="heading-arduino-sensor-and-actuator-example">Arduino Sensor and Actuator Example</h2>
<p>Now that you understand what sensors and actuators are and how to use them, let's use them together in one project.</p>
<p>In many embedded systems, sensors and actuators work together to get a specific task/functionality done. Here's how that works:</p>
<ul>
<li>A sensor detects/senses changes in the environment, and sends signals to the microcontroller to notify it of the detected changes.</li>
<li>The microcontroller processes the signals from the sensor. Depending on the existing logic (defined in the code), it sends signals to the actuator.</li>
<li>The actuator converts the signal from the microcontroller to physical/mechanical motion.</li>
</ul>
<p>Let's demonstrate these using an example. The idea here is to turn on an LED in a dark environment and turn it off when there's enough light.</p>
<p>The sensor will be the LDR sensor, the microcontroller will be the Arduino Uno board/software, and the actuator will be the LED.</p>
<p>Here's the circuit diagram:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/ldr-led-circuit.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> redLED = <span class="hljs-number">7</span>;

<span class="hljs-keyword">int</span> ldrPin = A1;
<span class="hljs-keyword">int</span> ldrValue;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  pinMode(ldrPin, INPUT);
  pinMode(redLED, OUTPUT);
  Serial.begin(<span class="hljs-number">9600</span>); 
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  ldrValue = analogRead(ldrPin);

  <span class="hljs-keyword">if</span> (ldrValue &gt; <span class="hljs-number">200</span>) {
    digitalWrite(redLED, LOW);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (ldrValue &lt; <span class="hljs-number">200</span>) {
    digitalWrite(redLED, HIGH);
  }

  Serial.println(ldrValue);
  delay(<span class="hljs-number">1000</span>); 
}
</code></pre>
<p>As usual, we created variables to represent the Arduino pins connected to the LED and LDR. We then set the LDR as INPUT and the LED as output in the <code>setup()</code> function.</p>
<p>We read the value of the LDR using the <code>analogRead()</code> function and stored it in the <code>ldrValue</code> variable.</p>
<p>Next, we used the an <code>if</code> statement to check the value of the LDR.</p>
<p>If the value is greater than 200, we turn off the LED. If the value is less than 200, we turn on the LED.</p>
<h1 id="heading-chapter-6-how-to-use-the-serial-monitor-in-arduino">Chapter 6: How to Use the Serial Monitor in Arduino</h1>
<p>The serial monitor is a useful tool for every Arduino builder. You can use it for a variety of tasks like:</p>
<ul>
<li>Debugging and testing code/components.</li>
<li>Serial communication between the Arduino board and the computer.</li>
<li>Display sensor and component data and readings.</li>
</ul>
<p>In this chapter, you'll learn how to initialize and use the serial monitor using the Arduino IDE. You'll learn about different built-in functions that can be used to send and receive data between the Arduino board and the computer.</p>
<p>Lastly, you'll build a project that uses values sent from the serial monitor to power specific LEDs connected to the Arduino board.</p>
<h2 id="heading-how-to-initialize-the-serial-monitor-with-serialbegin">How to Initialize the Serial Monitor With <code>Serial.begin()</code></h2>
<p>You can use the <code>Serial.begin()</code> function to initialize the serial monitor. It takes in the baud rate as its parameter. Here's what the syntax looks like:</p>
<pre><code>Serial.begin(baudRate)
</code></pre><p>The baud rate is the speed of data transfer between the Arduino board and the computer or any other device communicating with the Arduino board through the serial monitor.</p>
<p>The most commonly used baud rate is 9600, but you'll also come across resources that make use of 115200, 57600, and 38400, and so on. Whichever baud rate you specify in the <code>Serial.begin()</code> function should always match the baud rate seen in the serial monitor window.</p>
<p>For example, we can initialize the serial monitor in the IDE using the <code>Serial.begin()</code> function like this:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);

}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-comment">// put your main code here, to run repeatedly:</span>

}
</code></pre>
<p>In the code above, using the <code>Serial.begin()</code> function, we initialized the serial monitor in the <code>setup()</code> function with a baud rate of 9600.</p>
<p>At this point, nothing is happening. You can verify that the baud rates match by opening the serial monitor window. Once you have it open, you should have the serial monitor appear at the base of the IDE. You'll see the baud rate of the serial monitor within the window, usually written like "9600 baud".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/baud-rate.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing baud rate</em></p>
<p>If you're using an older version of the IDE, the serial monitor may pop up as a separate window. The functionality is still the same.</p>
<p>Now that we've initialized the serial monitor, let's see how to send and receive data with it.</p>
<h2 id="heading-how-to-send-data-with-serial-monitor">How to Send Data with Serial Monitor</h2>
<p>You can use different built-in functions reserved for serial communication in Arduino. We won't discuss all the built-in serial functions in Arduino – we'll just look at some you'll use/come across regularly. You can see more functions <a target="_blank" href="https://www.arduino.cc/reference/en/language/functions/communication/serial/">here</a>.</p>
<h3 id="heading-print-and-println-functions"><code>print()</code> and  <code>println()</code> Functions</h3>
<p>The <code>print()</code> and <code>println()</code> functions both print data to the serial monitor. The difference between the two is that <code>print()</code> prints data on the same line while <code>println()</code> prints each data on a new line.</p>
<p>Here are some examples:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  Serial.print(<span class="hljs-string">"Hello"</span>); 
  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>In the code above, we used the <code>print()</code> function to print "Hello" to the serial monitor repeatedly with a delay of 1000 milliseconds. Here's what the output looks like in the serial monitor:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/print.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here's another example using the <code>println()</code> function:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  Serial.println(<span class="hljs-string">"Hi"</span>);
  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>Here's the output in the serial monitor:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/println.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-receive-data-with-serial-monitor">How to Receive Data with Serial Monitor</h2>
<p>We'll discuss four functions in this section — <code>available()</code>, <code>readString()</code>, <code>parseInt()</code>, and <code>parseFloat()</code>. You can read up on other serial functions <a target="_blank" href="https://www.arduino.cc/reference/en/language/functions/communication/serial/">here</a>.</p>
<h3 id="heading-available-function"><code>available()</code> Function</h3>
<p>The <code>Serial.available()</code> function checks the number of characters in the serial port. It is mostly use to run code only when data is available in the serial monitor.</p>
<p>Here's a code example:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> userInput;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
  <span class="hljs-keyword">if</span> (Serial.available() &gt; <span class="hljs-number">0</span>) {
    userInput = Serial.parseInt();

    Serial.println(userInput);
  }

}
</code></pre>
<p>In the code above, the code block in the <code>if</code> statement will not run until the number of characters in the serial monitor is greater than 0.</p>
<p>When you initialize the serial monitor, the number of readable characters will be zero because there is no data available yet. The <code>Serial.available()</code> function checks and returns the number of characters available in the serial monitor. So it is used to create logic that only lets code be executed when data is available.</p>
<h3 id="heading-readstring-function"><code>readString()</code> Function</h3>
<p>You can use the <code>readString()</code> function to read characters from the serial monitor. It returns a string object so whatever values/characters you input in the serial monitor will be seen as string values when using the <code>readString()</code> function.</p>
<pre><code class="lang-cpp">String userInput;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
  <span class="hljs-keyword">if</span> (Serial.available() &gt; <span class="hljs-number">0</span>) {
    userInput = Serial.readString();

    Serial.println(userInput);
  }

}
</code></pre>
<h3 id="heading-parseint-function"><code>parseInt()</code> Function</h3>
<p>The <code>parseInt()</code> function returns valid integer values from incoming serial data. Non integer values will be returned as 0.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> userInput;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
  <span class="hljs-keyword">if</span> (Serial.available() &gt; <span class="hljs-number">0</span>) {
    userInput = Serial.parseInt();

    Serial.println(userInput);
  }

}
</code></pre>
<h3 id="heading-parsefloat-function"><code>parseFloat()</code> Function</h3>
<p>The <code>parseFloat()</code> function returns valid floating point numbers from incoming serial data.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">float</span> userInput;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  Serial.begin(<span class="hljs-number">9600</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
  <span class="hljs-keyword">if</span> (Serial.available() &gt; <span class="hljs-number">0</span>) {
    userInput = Serial.parseFloat();

    Serial.println(userInput);
  }

}
</code></pre>
<h2 id="heading-serial-monitor-project">Serial Monitor Project</h2>
<p>In this section, you'll build a project that uses string values from the serial monitor to turn on LEDs. We'll make use of some of the serial functions discussed in previous sections.</p>
<p>Here's the circuit diagram:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/serial-communication-project.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the circuit diagram above, we have three LEDs. The red LED is connected to pin 6, the blue LED to pin 5, and the yellow LED to pin 4.</p>
<p>Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> redLED = <span class="hljs-number">6</span>;
<span class="hljs-keyword">int</span> blueLED = <span class="hljs-number">5</span>;
<span class="hljs-keyword">int</span> yellowLED = <span class="hljs-number">4</span>;

String userInput;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span></span>{
  pinMode(redLED, OUTPUT);
  pinMode(blueLED, OUTPUT);
  pinMode(yellowLED, OUTPUT);
  Serial.begin(<span class="hljs-number">9600</span>);

  Serial.println(<span class="hljs-string">"Choose an LED to turn on from the list below:"</span>);
  Serial.println(<span class="hljs-string">"red"</span>);
  Serial.println(<span class="hljs-string">"blue"</span>);
  Serial.println(<span class="hljs-string">"yellow"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{

  <span class="hljs-keyword">if</span> (Serial.available() &gt; <span class="hljs-number">0</span>) {

    userInput = Serial.readString();

    <span class="hljs-keyword">if</span> (userInput == <span class="hljs-string">"red"</span>) {
      digitalWrite(redLED, HIGH);
      digitalWrite(blueLED, LOW);
      digitalWrite(yellowLED, LOW);
    } 

    <span class="hljs-keyword">if</span> (userInput == <span class="hljs-string">"blue"</span>) {
      digitalWrite(redLED, LOW);
      digitalWrite(blueLED, HIGH);
      digitalWrite(yellowLED, LOW);
    }

    <span class="hljs-keyword">if</span> (userInput == <span class="hljs-string">"yellow"</span>) {
      digitalWrite(redLED, LOW);
      digitalWrite(blueLED, LOW);
      digitalWrite(yellowLED, HIGH);
    }

  }

}
</code></pre>
<p>Let's break down the code above.</p>
<p>Just like the connection in the circuit diagram, we initialized the three LEDs with their respective pin numbers. We also created a string variable called <code>userInput</code> that will be used to store the data from the serial monitor:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> redLED = <span class="hljs-number">6</span>;
<span class="hljs-keyword">int</span> blueLED = <span class="hljs-number">5</span>;
<span class="hljs-keyword">int</span> yellowLED = <span class="hljs-number">4</span>;
</code></pre>
<p>We then configured the three LEDs as output pins using the <code>pinMode()</code> function:</p>
<pre><code class="lang-cpp">pinMode(redLED, OUTPUT);
pinMode(blueLED, OUTPUT);
pinMode(yellowLED, OUTPUT);
</code></pre>
<p>We initialized the serial monitor with a baud rate of 9600, and printed some strings to give users a hint at the expected values to be used in the serial monitor:</p>
<pre><code class="lang-cpp">Serial.begin(<span class="hljs-number">9600</span>);

Serial.println(<span class="hljs-string">"Choose an LED to turn on from the list below:"</span>);
Serial.println(<span class="hljs-string">"red"</span>);
Serial.println(<span class="hljs-string">"blue"</span>);
Serial.println(<span class="hljs-string">"yellow"</span>);
</code></pre>
<p>At this point, you'll have an output like this in the serial monitor:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/project-output.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Using the <code>Serial.available()</code> function, we check for availability of serial data before running the code in the <code>if</code> statement:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">if</span> (Serial.available() &gt; <span class="hljs-number">0</span>) {
    ...
}
</code></pre>
<p>The next thing we did was to read and return the incoming data as string values, and store the data in the <code>userInput</code> variable. We did that using the <code>readString()</code> function:</p>
<pre><code class="lang-cpp">userInput = Serial.readString();
</code></pre>
<p>Lastly, we used <code>if</code> statements to check which LED color value the user has typed in/sent through the serial monitor, then turn on/off the respective LEDs:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">if</span> (userInput == <span class="hljs-string">"red"</span>) {
  digitalWrite(redLED, HIGH);
  digitalWrite(blueLED, LOW);
  digitalWrite(yellowLED, LOW);
} 

<span class="hljs-keyword">if</span> (userInput == <span class="hljs-string">"blue"</span>) {
  digitalWrite(redLED, LOW);
  digitalWrite(blueLED, HIGH);
  digitalWrite(yellowLED, LOW);
}

<span class="hljs-keyword">if</span> (userInput == <span class="hljs-string">"yellow"</span>) {
  digitalWrite(redLED, LOW);
  digitalWrite(blueLED, LOW);
  digitalWrite(yellowLED, HIGH);
}
</code></pre>
<p>If you type and send red through the serial monitor, the red LED comes on while the others goes off. The same logic applies to sending blue or yellow through the serial monitor.</p>
<h1 id="heading-chapter-7-how-to-use-displays-in-arduino">Chapter 7: How to Use Displays in Arduino</h1>
<p>You can use display components in Arduino to represent data visually in different formats like text, images, and so on.</p>
<p>Displays provide an alternative to showing data other than the serial monitor. You can use them to display sensor values, instructions to users, user input, and so on.</p>
<p>There are various types of display components when it comes to building with Arduino but we'll focus on the LCD (Liquid Crystal Display).</p>
<p>To be more specific, we'll use the 16 x 2 LCD (HD44780). This is what it looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/lcd-component.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From the image above, there are sixteen pins with different values. We'll use most of these pins to make connections to the Uno board which will enable us use and control the component.</p>
<p>There are generally two ways of connecting the LCD — using the built-in library or using a third-party library.</p>
<p>With the built-in <code>LiquidCrystal</code> library, you'll make use of about 12 pins on the LCD component. When it comes to using a third-party library like <code>LiquidCrystal_I2C</code>, you'll make use of just 2 pins.</p>
<p>We'll be using the first method — this will help you understand how the pins work. Now let's talk about those pins.</p>
<h2 id="heading-what-are-the-pins-in-an-lcd-used-for">What are the pins in an LCD used for?</h2>
<p>The pins control functionalities like memory management, data transfer, and power on an LCD. Let's talk about them in detail:</p>
<ul>
<li>The first pin from the left is the GND pin, which is the LCD's ground pin. It is connected to GND on the Arduino board. In some LCD modules, it may be written as VSS.</li>
<li>The VCC is the power pin which is connected to 5V on the Arduino board. In some LCD modules, it may be written as VDD.</li>
<li>The V0 pin is used to adjust the contrast of the LCD. It is connected to a potentiometer. Tuning the potentiometer changes the contrast of the data being displayed.</li>
<li>The RS (Register Select) pin can be used to control the LCD's memory. It is usually connected to a digital pin.</li>
<li>The RW (Read/Write) pin controls whether data is written or read to the LCD. It is usually connected to ground (GND) which sets the LCD to write mode — this enables you to send and display data with the LCD.</li>
<li>The E (Enable) pin "enables" data transfer between the microcontroller (in our case, the Uno board) and the LCD. It is usually connected to a digital pin.</li>
<li>The D0 to D7 (can also be written as DB0 to DB7) are the data pins. There are used to send data to the LCD in bits. In most cases, the D4 to D7 pins are used. The main difference is that data is sent in 4-bits with the D0 to D3 pins, while data is sent in 8-bits with the D4 to D7 pins. There are usually connected to digital pins.</li>
<li>The LED pins are used to control the backlight of the LCD. In some LCD modules, they may be written as BLA and BLK, or A and K. The first LED pin is connected to 5V using a resistor while the second LED pin is connected to ground (GND).</li>
</ul>
<h2 id="heading-example-1-how-to-connect-and-use-an-lcd-with-arduino">Example #1 – How to Connect and Use an LCD with Arduino</h2>
<p>In the last section, we talked about the meaning of the pins on an LCD. In this section, you'll see a practical example on how to connect them in a circuit, and write code to display data on the LCD.</p>
<p>We'll use the following components:</p>
<ul>
<li>Arduino Uno.</li>
<li>16 x 2 LCD.</li>
<li>Potentiometer.</li>
<li>Jumper wires.</li>
<li>Resistors.</li>
<li>Breadboard.</li>
</ul>
<p>Here's what the circuit diagram looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/lcd.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In the circuit above, we made the following connections from the LCD to the Uno board:</p>
<ul>
<li>The LCD's GND pin was connected to GND on the Uno board.</li>
<li>The LCD's VCC pin was connected to 5V on the Uno board.</li>
<li>The V0 pin was connected to the potentiometer.</li>
<li>The RS pin was connected to digital pin 4 on the Uno board.</li>
<li>The RW pin was connected to GND on the Uno board.</li>
<li>The LCD's D4 was connected to digital pin 6 on the Uno board.</li>
<li>The LCD's D5 was connected to digital pin 7 on the Uno board.</li>
<li>The LCD's D6 was connected to digital pin 8 on the Uno board.</li>
<li>The LCD's D7 was connected to digital pin 9 on the Uno board.</li>
<li>The first LED pin was connected to 5V.</li>
<li>The second LED pin was connected to GND.</li>
</ul>
<p>For the potentiometer, one of the outer legs (either the left or right) was connected to 5V, the other outer leg was connected to GND. Then the middle leg of the potentiometer was connected to the V0 pin on the LCD. This will enable you to control the contrast of the LCD.</p>
<p>Next, we'll write some code!</p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;LiquidCrystal.h&gt;</span></span>

<span class="hljs-function">LiquidCrystal <span class="hljs-title">lcd</span><span class="hljs-params">(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>)</span></span>;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  lcd.begin(<span class="hljs-number">16</span>, <span class="hljs-number">2</span>);
  lcd.print(<span class="hljs-string">"freeCodeCamp!"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{

}
</code></pre>
<p>In the code above, we first included/imported the built-in <code>LiquidCrystal</code> library which can be used to interact with an LCD using Arduino code: <code>#include &lt;LiquidCrystal.h&gt;</code>.</p>
<p>We then initialized the <code>lcd</code> object with the required pin numbers: <code>LiquidCrystal lcd(4, 5, 6, 7, 8, 9)</code>. The first number denotes the RS pin. The second number denotes the E pin. The last four numbers denote the data pins (D4 to D7).</p>
<p>Next, we initialized the number of columns and rows using the <code>lcd.begin()</code> function: <code>lcd.begin(16, 2)</code>. We have 16 columns and 2 rows.</p>
<p>The <code>lcd.print</code> function prints data to the LCD. If you've made all the connections right, you should see "freeCodeCamp" displayed on the LCD.</p>
<p>In the next examples, we'll work with user input and sensor data, and also see other <code>lcd</code> functions.</p>
<h2 id="heading-example-2-how-to-display-user-input-with-lcd-in-arduino">Example #2 – How to Display User Input with LCD in Arduino</h2>
<p>In this example, we'll accept input from the user using the serial monitor, and display the input along with a welcome message on the LCD screen.</p>
<p>We'll make use of the same circuit in the previous example. Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;LiquidCrystal.h&gt;</span></span>
<span class="hljs-function">LiquidCrystal <span class="hljs-title">lcd</span><span class="hljs-params">(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>)</span></span>;

String userInput;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
  lcd.begin(<span class="hljs-number">16</span>, <span class="hljs-number">2</span>);

  lcd.print(<span class="hljs-string">"Input name"</span>);

  lcd.setCursor(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-keyword">if</span> (Serial.available() &gt; <span class="hljs-number">0</span>) {

    userInput = Serial.readString();
    lcd.print(<span class="hljs-string">"Welcome "</span> + userInput);
  }
}
</code></pre>
<p>In the code above, we created a string variable called <code>userInput</code> which will be used to store the value of the user's input.</p>
<p>We then initialized the serial monitor — <code>Serial.begin(9600);</code></p>
<p>Next, we set the rows and columns of the LCD using the <code>lcd.begin</code> function.</p>
<p>Using the <code>lcd.print</code> function, we printed "Input name" on the LCD screen.</p>
<p>This was followed by the <code>lcd.setCursor(0, 1)</code> function which has two parameters — 0 and 1. 0 represents first column on the left while 1 represents the second row. So the cursor will be set to the second line of the LCD.</p>
<p>We set the cursor to be on the second line because we already have some text ("input name") on the first line so it would be more ideal in this case to print the message to the user on the next line. I'll show you an alternative way to do this after this example.</p>
<p>In the <code>void loop()</code> function, we used the <code>Serial.readString()</code> function to read the value of user's input and stored it in the <code>userInput</code> variable.</p>
<p>Lastly, we printed a welcome message and the user's name in the LCD. This will be printed on the second line because that's where we set the cursor to be.</p>
<p>Play around with the code and see what happens as you adjust the parameters of the <code>lcd.setCursor()</code> function. This will help you understand it better.</p>
<h3 id="heading-example-2-alternative">Example #2 Alternative</h3>
<p>An alternative way to print the message to the user is by using the same line where the initial message was displayed. We can clear whatever is written on the first row of the LCD and display a different value.</p>
<p>Here's how:</p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;LiquidCrystal.h&gt;</span></span>
<span class="hljs-function">LiquidCrystal <span class="hljs-title">lcd</span><span class="hljs-params">(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>)</span></span>;

String userInput;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  Serial.begin(<span class="hljs-number">9600</span>);
  lcd.begin(<span class="hljs-number">16</span>, <span class="hljs-number">2</span>);

  lcd.print(<span class="hljs-string">"Input name"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  <span class="hljs-keyword">if</span> (Serial.available() &gt; <span class="hljs-number">0</span>) {
    lcd.clear();
    userInput = Serial.readString();
    lcd.print(<span class="hljs-string">"Welcome "</span> + userInput);
  }
}
</code></pre>
<p>In this example, we used the <code>lcd.clear()</code> function to clear the initial request message and display/print the welcome message to the user. The rest of the logic is the same.</p>
<p>So when you type in a name in the serial monitor an hit enter, the request message ("Input name") will be cleared while the welcome message will be displayed with the name you inputted.</p>
<h2 id="heading-example-3-how-to-display-sensor-data-with-lcd-in-arduino">Example #3 – How to Display Sensor Data with LCD in Arduino</h2>
<p>In this example, we'll display data from a sensor on the LCD. We'll use the temperature sensor. The logic here is to sense the temperature of a room and display the varying values on the LCD.</p>
<p>Here are the components we'll use:</p>
<ul>
<li>Arduino Uno.</li>
<li>16 x 2 LCD.</li>
<li>Light-Dependent Resistor (LDR).</li>
<li>Potentiometer.</li>
<li>Jumper wires.</li>
<li>Resistors.</li>
<li>Breadboard.</li>
</ul>
<p>Here's the circuit diagram:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/lcd-ldr.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The circuit diagram is almost the same as the ones in the previous examples, except that we added an LDR to measure the intensity of light. Using the LDR, we'll read, store, and display the value of light intensity on the LCD.</p>
<p>Here's the code:</p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;LiquidCrystal.h&gt;</span></span>
<span class="hljs-function">LiquidCrystal <span class="hljs-title">lcd</span><span class="hljs-params">(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>)</span></span>;

<span class="hljs-keyword">int</span> ldrPin = A1;
<span class="hljs-keyword">int</span> ldrValue;

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
  pinMode(ldrPin, INPUT);
  lcd.begin(<span class="hljs-number">16</span>, <span class="hljs-number">2</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
  ldrValue = analogRead(ldrPin);

  lcd.clear();
  lcd.setCursor(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
  lcd.print(<span class="hljs-string">"LDR value:"</span>);

  lcd.setCursor(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>);
  lcd.print(ldrValue);

  delay(<span class="hljs-number">1000</span>);
}
</code></pre>
<p>Let's break down the code.</p>
<p>We started by including the built-in <code>LiquidCrystal</code> library and initialized the respective pins for working with the LCD (refer to the first example if you do not understand how the connection and initialization works).</p>
<p>We then created two variables: <code>ldrPin</code> which denoted the analog pin connected to the LDR, and <code>ldrValue</code> to store the readings from the LDR.</p>
<p>In <code>void setup()</code>, we configured the LDR as an input pin and set the columns and rows of the LCD.</p>
<p>Next, we read the value of the LDR using the <code>analogRead</code> function and stored that value in the <code>ldrValue</code> variable.</p>
<p>Using the <code>lcd.clear()</code> function, we clear the display after each reading. This helps in making the reading appear more accurately. If you remove the function, then some values will still be on display and make your results appear inaccurately (I spent about thirty minutes figuring this out 😂).</p>
<p>After that, we set the cursor on the first row and column of the LCD: <code>lcd.setCursor(0, 0)</code>, and displayed a message ("LDR value:").</p>
<p>Using the <code>lcd.setCursor(0, 1)</code> function, we moved the cursor down to the second row, and used <code>lcd.print(ldrValue)</code> to print/display the value of the LDR on the LCD.</p>
<p>Lastly, we added a delay of 1000 milliseconds.</p>
<p>Now you can see the value of light intensity in your environment displayed on the LCD. </p>
<p>You can use a flashlight to increase and decrease the exposure of light on the LDR, and then observe the values change on the LCD. Pretty cool!</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Congratulations! We've come to the end of this handbook. You now have enough knowledge to take on bigger projects. </p>
<p>And that should be your next goal — applying these concepts to projects that aid you and the people around you. There is no limit to what you can create. You can start by watching a couple of videos to see the type of projects people build, then you can come up with yours.</p>
<p>This handbook covered the necessary parts of Arduino (both hardware and software) that you'll need as a beginner to kickstart your journey. </p>
<p>The best way to improve and retain what you've learned is by practicing and building.</p>
<p>Happy tinkering!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Embedded Rust Programming on Raspberry Pi Zero W ]]>
                </title>
                <description>
                    <![CDATA[ Embedded programming in Rust requires a whole new knowledge base. Using a Raspberry Pi Zero W, you can quickly get up and running with embedded Rust.  Starting with an embedded "Hello World" equivalent, and advancing to a text-to-morse-code translato... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/embedded-rust-programming-on-raspberry-pi-zero-w/</link>
                <guid isPermaLink="false">66ace42a106bc8d5a4eb36f6</guid>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Raspberry Pi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Rust ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Shaun Hamilton ]]>
                </dc:creator>
                <pubDate>Thu, 09 Jun 2022 15:37:48 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/06/rpi-rust.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Embedded programming in Rust requires a whole new knowledge base. Using a Raspberry Pi Zero W, you can quickly get up and running with embedded Rust. </p>
<p>Starting with an embedded <em>"Hello World"</em> equivalent, and advancing to a text-to-morse-code translator, this article will walk you through the process.</p>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-set-up-the-pi">How to Set Up the Pi</a><ul>
<li><a class="post-section-overview" href="#heading-format-the-sd-card">Format the SD Card</a></li>
<li><a class="post-section-overview" href="#heading-flash-the-distribution">Flash the Distribution</a><ul>
<li><a class="post-section-overview" href="#heading-configure-wifi-and-ssh">Configure Wifi and SSH</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-complete-the-circuit">Complete the Circuit</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-cross-compilation">How to Set Up Cross Compilation</a><ul>
<li><a class="post-section-overview" href="#heading-install-the-target">Install the Target</a></li>
<li><a class="post-section-overview" href="#heading-specify-the-linker">Specify the Linker</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-program-an-embedded-hello-world">How to Program an Embedded "Hello World"</a><ul>
<li><a class="post-section-overview" href="#heading-successfully-exit-the-program">Successfully Exit the Program</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-cross-compile-the-program">How to Cross Compile the Program</a></li>
<li><a class="post-section-overview" href="#heading-how-to-transfer-the-binary-to-the-pi">How to Transfer the Binary to the Pi</a></li>
<li><a class="post-section-overview" href="#heading-how-to-ssh-into-the-pi">How to SSH into the Pi</a><ul>
<li><a class="post-section-overview" href="#heading-run-the-program">Run the Program</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-how-to-code-a-text-to-morse-code-translator">How to Code a Text-to-Morse-Code Translator</a></li>
<li><a class="post-section-overview" href="#heading-appendix">Appendix</a><ul>
<li><a class="post-section-overview" href="#heading-targets-1">Targets</a></li>
</ul>
</li>
</ul>
<h2 id="heading-how-to-set-up-the-pi">How to Set Up the Pi</h2>
<h3 id="heading-format-the-sd-card">Format the SD Card</h3>
<p>Use the Raspberry Pi Imager which can be downloaded from the <a target="_blank" href="https://www.raspberrypi.com/software/">Raspberry Pi Software Webpage</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/rpi-imager.png" alt="rpi-imager" width="600" height="400" loading="lazy"></p>
<h3 id="heading-flash-the-distribution">Flash the Distribution</h3>
<p>A distribution I'd suggest is <a target="_blank" href="https://www.raspberrypi.com/software/operating-systems/">Raspberry Pi OS Lite</a>. This is a <em>headless</em> distribution, which means it does not come with a GUI.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/rpi-imager-os.png" alt="rpi-imager-os" width="600" height="400" loading="lazy"></p>
<h4 id="heading-configure-wifi-and-ssh">Configure Wifi and SSH</h4>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/rpi-imager-ssh.png" alt="rpi-imager-ssh" width="600" height="400" loading="lazy"></p>
<p>Once that is done, you can insert the SD card into the Raspberry Pi, and power it up.</p>
<h3 id="heading-complete-the-circuit">Complete the Circuit</h3>
<p><strong>Circuit Diagram</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/rpi-circuit.png" alt="rpi-circuit" width="600" height="400" loading="lazy"></p>
<p><strong>Pi Pinout</strong></p>
<p>Connect negative to ground, and positive to BCM pin 17 as shown below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/rpi-pinout.png" alt="rpi-pinout" width="600" height="400" loading="lazy"></p>
<p>The pinout can be seen here: https://pinout.xyz/</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/IMG_3418-1-.JPG" alt="IMG_3418-1-" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-set-up-cross-compilation">How to Set Up Cross Compilation</h2>
<h3 id="heading-install-the-target">Install the Target</h3>
<p>Use <code>rustup</code> to install the necessary target for your Raspberry Pi:</p>
<pre><code class="lang-bash">my-pc$ rustup add target arm-unknown-linux-gnueabihf
</code></pre>
<p><a class="post-section-overview" href="#heading-targets-1">Appendix</a> for more information about targets in Rust.</p>
<h3 id="heading-specify-the-linker">Specify the Linker</h3>
<p>Download the <code>raspberrypi/tools</code> repository into a directory named <code>rpi_tools</code>:</p>
<pre><code class="lang-bash">my-pc:~ $ git <span class="hljs-built_in">clone</span> https://github.com/raspberrypi/tools <span class="hljs-variable">$HOME</span>/rpi_tools
</code></pre>
<p>Edit the <code>~/.cargo/config</code> file using your favourite text editor:</p>
<pre><code class="lang-bash">my_pc:~ $ sudo nano ~/.cargo/config
</code></pre>
<p>Tell Cargo to use a specific linker version for your target:</p>
<pre><code class="lang-conf">[target.arm-unknown-linux-gnueabihf]
linker = "/rpi_tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc"
</code></pre>
<h2 id="heading-how-to-program-an-embedded-hello-world">How to Program an Embedded "Hello World"</h2>
<p>Start by creating a new Rust project, and opening the <code>main.rs</code> file in your favourite text editor:</p>
<pre><code class="lang-bash">my-pc:~ $ cargo new blink
my-pc:~ $ <span class="hljs-built_in">cd</span> blink
my-pc:~/blink $ nano src/main.rs
</code></pre>
<p>Import the <code>rust_gpiozero</code> crate, and program an LED to alternate between on and off every second:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> rust_gpiozero::*;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// Create a new LED attached to Pin 17</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> led = LED::new(<span class="hljs-number">17</span>);

    led.blink(<span class="hljs-number">1.0</span>, <span class="hljs-number">1.0</span>);

    led.wait();
}
</code></pre>
<p>Be sure to add the dependency to the <code>Cargo.toml</code> file:</p>
<pre><code class="lang-toml"><span class="hljs-section">[dependencies]</span>
<span class="hljs-attr">rust-gpiozero</span> = <span class="hljs-string">"0.2.1"</span>
</code></pre>
<h3 id="heading-successfully-exit-the-program">Successfully Exit the Program</h3>
<p>Since <code>rustc 1.61.0</code> <sup>[<a target="_blank" href="https://doc.rust-lang.org/stable/std/process/struct.ExitCode.html">1</a>]</sup>, you can use the <code>std::process::ExitCode</code> struct to specify the status code returned to the process' parent:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::process::ExitCode;
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -&gt; ExitCode {
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">if</span> error {
      <span class="hljs-keyword">return</span> ExitCode::from(<span class="hljs-number">1</span>);
    }
    ExitCode::SUCCESS
}
</code></pre>
<p>Otherwise, you can simply return a <code>Result</code>:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -&gt; <span class="hljs-built_in">Result</span>&lt;(), std::io::Error&gt; {
  <span class="hljs-comment">// ...</span>
  <span class="hljs-literal">Ok</span>(())
}
</code></pre>
<h2 id="heading-how-to-cross-compile-the-program">How to Cross Compile the Program</h2>
<p>Build a release of your program, targeting the required architecture:</p>
<pre><code class="lang-bash">my-pc:~/blink $ cargo build --release --target=arm-unknown-linux-gnueabihf
</code></pre>
<h2 id="heading-how-to-transfer-the-binary-to-the-pi">How to Transfer the Binary to the Pi</h2>
<p>Use <code>scp</code> to transfer the compiled binary from your host computer to the Raspberry Pi over SSH:</p>
<pre><code class="lang-bash">my-pc:~/blink $ scp target/arm-unknown-linux-gnueabihf/release/blink pi@192.168.1.199:~/blink
</code></pre>
<p><strong>Note:</strong> The local IP of your Pi will likely be different.</p>
<h2 id="heading-how-to-ssh-into-the-pi">How to SSH into the Pi</h2>
<p>SSH and log in to the Raspberry Pi via its local IP address:</p>
<pre><code class="lang-bash">my-pc:~ $ ssh pi@192.168.1.199
</code></pre>
<h3 id="heading-run-the-program">Run the Program</h3>
<p>From the Raspberry Pi, run the compiled binary:</p>
<pre><code class="lang-bash">pi:~ $ ./blink
</code></pre>
<h2 id="heading-how-to-code-a-text-to-morse-code-translator">How to Code a Text-to-Morse-Code Translator</h2>
<p>Here is an example of an application that reads the stdin line by line, translates the input into Morse Code, and toggles the LED on and off based on the Morse Code for the characters.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> rust_gpiozero::*;
<span class="hljs-keyword">use</span> std::io::{BufRead, <span class="hljs-keyword">self</span>};
<span class="hljs-keyword">use</span> std::collections::HashMap;
<span class="hljs-keyword">use</span> std::thread::sleep;
<span class="hljs-keyword">use</span> std::time::Duration;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() -&gt; <span class="hljs-built_in">Result</span>&lt;(), std::io::Error&gt; {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Starting...\n- Type in text to turn into Morse Code\n- Type `quit()` to quit\n"</span>);
    <span class="hljs-comment">// Create a new LED attached to Pin 17</span>
    <span class="hljs-keyword">let</span> led = LED::new(<span class="hljs-number">17</span>);

    <span class="hljs-comment">/// Length of a dot in milliseconds</span>
    <span class="hljs-keyword">const</span> DOT_DELAY: <span class="hljs-built_in">u64</span> = <span class="hljs-number">80</span>;
    <span class="hljs-comment">/// Length of a dash in milliseconds</span>
    <span class="hljs-keyword">const</span> DASH_DELAY: <span class="hljs-built_in">u64</span> = DOT_DELAY * <span class="hljs-number">3</span>;
    <span class="hljs-comment">/// Delay between inputs in milliseconds</span>
    <span class="hljs-keyword">const</span> PUSH_DELAY: <span class="hljs-built_in">u64</span> = DOT_DELAY;
    <span class="hljs-comment">/// Delay between letters in milliseconds</span>
    <span class="hljs-keyword">const</span> LETTER_DELAY: <span class="hljs-built_in">u64</span> = DOT_DELAY * <span class="hljs-number">3</span>;
    <span class="hljs-comment">/// Delay between words in milliseconds</span>
    <span class="hljs-keyword">const</span> WORD_DELAY: <span class="hljs-built_in">u64</span> = DOT_DELAY * <span class="hljs-number">7</span>;

    <span class="hljs-keyword">let</span> morse_code_alphabet: HashMap&lt;<span class="hljs-built_in">char</span>, &amp;<span class="hljs-symbol">'static</span> <span class="hljs-built_in">str</span>&gt; =
    [
        (<span class="hljs-string">'a'</span>, <span class="hljs-string">".-"</span>),
        (<span class="hljs-string">'b'</span>, <span class="hljs-string">"-..."</span>),
        (<span class="hljs-string">'c'</span>, <span class="hljs-string">"-.-."</span>),
        (<span class="hljs-string">'d'</span>, <span class="hljs-string">"-.."</span>),
        (<span class="hljs-string">'e'</span>, <span class="hljs-string">"."</span>),
        (<span class="hljs-string">'f'</span>, <span class="hljs-string">"..-."</span>),
        (<span class="hljs-string">'g'</span>, <span class="hljs-string">"--."</span>),
        (<span class="hljs-string">'h'</span>, <span class="hljs-string">"...."</span>),
        (<span class="hljs-string">'i'</span>, <span class="hljs-string">".."</span>),
        (<span class="hljs-string">'j'</span>, <span class="hljs-string">".---"</span>),
        (<span class="hljs-string">'k'</span>, <span class="hljs-string">"-.-"</span>),
        (<span class="hljs-string">'l'</span>, <span class="hljs-string">".-.."</span>),
        (<span class="hljs-string">'m'</span>, <span class="hljs-string">"--"</span>),
        (<span class="hljs-string">'n'</span>, <span class="hljs-string">"-."</span>),
        (<span class="hljs-string">'o'</span>, <span class="hljs-string">"---"</span>),
        (<span class="hljs-string">'p'</span>, <span class="hljs-string">".--."</span>),
        (<span class="hljs-string">'q'</span>, <span class="hljs-string">"--.-"</span>),
        (<span class="hljs-string">'r'</span>, <span class="hljs-string">".-."</span>),
        (<span class="hljs-string">'s'</span>, <span class="hljs-string">"..."</span>),
        (<span class="hljs-string">'t'</span>, <span class="hljs-string">"-"</span>),
        (<span class="hljs-string">'u'</span>, <span class="hljs-string">"..-"</span>),
        (<span class="hljs-string">'v'</span>, <span class="hljs-string">"...-"</span>),
        (<span class="hljs-string">'w'</span>, <span class="hljs-string">".--"</span>),
        (<span class="hljs-string">'x'</span>, <span class="hljs-string">"-..-"</span>),
        (<span class="hljs-string">'y'</span>, <span class="hljs-string">"-.--"</span>),
        (<span class="hljs-string">'z'</span>, <span class="hljs-string">"--.."</span>),
        (<span class="hljs-string">'1'</span>, <span class="hljs-string">".----"</span>),
        (<span class="hljs-string">'2'</span>, <span class="hljs-string">"..---"</span>),
        (<span class="hljs-string">'3'</span>, <span class="hljs-string">"...--"</span>),
        (<span class="hljs-string">'4'</span>, <span class="hljs-string">"....-"</span>),
        (<span class="hljs-string">'5'</span>, <span class="hljs-string">"....."</span>),
        (<span class="hljs-string">'6'</span>, <span class="hljs-string">"-...."</span>),
        (<span class="hljs-string">'7'</span>, <span class="hljs-string">"--..."</span>),
        (<span class="hljs-string">'8'</span>, <span class="hljs-string">"---.."</span>),
        (<span class="hljs-string">'9'</span>, <span class="hljs-string">"----."</span>),
        (<span class="hljs-string">'0'</span>, <span class="hljs-string">"-----"</span>),
        (<span class="hljs-string">'.'</span>, <span class="hljs-string">".-.-.-"</span>),
        (<span class="hljs-string">','</span>, <span class="hljs-string">"--..--"</span>),
        (<span class="hljs-string">'?'</span>, <span class="hljs-string">"..--.."</span>),
        (<span class="hljs-string">'\''</span>, <span class="hljs-string">".----."</span>),
        (<span class="hljs-string">'!'</span>, <span class="hljs-string">"-.-.--"</span>),
        (<span class="hljs-string">'/'</span>, <span class="hljs-string">"-..-."</span>),
        (<span class="hljs-string">'('</span>, <span class="hljs-string">"-.--."</span>),
        (<span class="hljs-string">')'</span>, <span class="hljs-string">"-.--.-"</span>),
        (<span class="hljs-string">'&amp;'</span>, <span class="hljs-string">".-..."</span>),
        (<span class="hljs-string">':'</span>, <span class="hljs-string">"---..."</span>),
        (<span class="hljs-string">';'</span>, <span class="hljs-string">"-.-.-."</span>),
        (<span class="hljs-string">'='</span>, <span class="hljs-string">"-...-"</span>),
        (<span class="hljs-string">'+'</span>, <span class="hljs-string">".-.-."</span>),
        (<span class="hljs-string">'-'</span>, <span class="hljs-string">"-....-"</span>),
        (<span class="hljs-string">'_'</span>, <span class="hljs-string">"..--.-"</span>),
        (<span class="hljs-string">'"'</span>, <span class="hljs-string">".-..-."</span>),
        (<span class="hljs-string">'$'</span>, <span class="hljs-string">"...-..-"</span>),
        (<span class="hljs-string">'@'</span>, <span class="hljs-string">".--.-."</span>),
        (<span class="hljs-string">' '</span>, <span class="hljs-string">" "</span>),
    ].iter().cloned().collect();

    <span class="hljs-comment">// Read standard input per line</span>
    <span class="hljs-keyword">for</span> line_res <span class="hljs-keyword">in</span> io::stdin().lock().lines() {
        <span class="hljs-keyword">let</span> line = line_res?;
        <span class="hljs-keyword">if</span> line == <span class="hljs-string">"quit()"</span> {
            <span class="hljs-keyword">break</span>;
        }
        <span class="hljs-comment">// Turn line into morse code</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> morse = <span class="hljs-built_in">String</span>::new();
        <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> line.chars() {
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(morse_code_char) = morse_code_alphabet.get(&amp;c) {
                morse.push_str(morse_code_char);
                <span class="hljs-comment">// Separate characters with a comma</span>
                morse.push_str(<span class="hljs-string">","</span>);
            }
        }
        <span class="hljs-comment">// Blink LED based on characters</span>
        <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> morse.chars() {
            <span class="hljs-keyword">match</span> c {
                <span class="hljs-string">'.'</span> =&gt; {
                    led.on();
                    sleep(Duration::from_millis(DOT_DELAY));
                    led.off();
                    sleep(Duration::from_millis(PUSH_DELAY));
                },
                <span class="hljs-string">'-'</span> =&gt; {
                    led.on();
                    sleep(Duration::from_millis(DASH_DELAY));
                    led.off();
                    sleep(Duration::from_millis(PUSH_DELAY));
                },
                <span class="hljs-string">','</span> =&gt; {
                    sleep(Duration::from_millis(LETTER_DELAY));
                },
                <span class="hljs-string">' '</span> =&gt; {
                    sleep(Duration::from_millis(WORD_DELAY));
                },
                _ =&gt; {
                    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Unknown character: {}"</span>, c);
                    <span class="hljs-keyword">break</span>;
                }
            }
        }
        sleep(Duration::from_millis(WORD_DELAY));
    }

    <span class="hljs-comment">// Free the variable and associated resources</span>
    led.close();

    <span class="hljs-literal">Ok</span>(())
}
</code></pre>
<h2 id="heading-appendix">Appendix</h2>
<h3 id="heading-targets">Targets</h3>
<p>In Rust, the <em>target</em> is the platform (architecture) the program is compiled for. Cargo automatically detects the target, based on the file system layout <sup>[<a target="_blank" href="https://doc.rust-lang.org/cargo/reference/cargo-targets.html#target-auto-discovery">2</a>]</sup>.</p>
<p>You can see the list of built-in targets, by running:</p>
<pre><code class="lang-bash">rustc --<span class="hljs-built_in">print</span> target-list
<span class="hljs-comment"># OR</span>
rustup target list
</code></pre>
<p>From here you can add a new target to your project, by running:</p>
<pre><code class="lang-bash">rustup target add &lt;target&gt;
</code></pre>
<p>The given target is often in the form of a <em>triple</em> <sup>[<a target="_blank" href="https://rust-lang.github.io/rfcs/0131-target-specification.html">3</a>]</sup>:</p>
<ul>
<li>The architecture</li>
<li>The vendor</li>
<li>The operating system type</li>
<li>The environment type</li>
</ul>
<p><em>This is refered to as a 'target triple', because the fourth part is optional.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Connect the nRF9160 Feather to a Self-Hosted Mosquitto Instance ]]>
                </title>
                <description>
                    <![CDATA[ By Jared Wolff One thing that’s always tripped me up as an IoT developer is figuring out the best way to transmit data. There are many different kinds of radios and mediums. On top of that, there are different protocols to boot. As of this writing, t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-connect-the-nrf9160-feather-to-mosquitto/</link>
                <guid isPermaLink="false">66d8504d5a0b456f4d32147f</guid>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mqtt ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 05 Aug 2020 16:29:54 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/08/Copy-of-Bluetooth-with-nRF9160-Feather.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jared Wolff</p>
<p>One thing that’s always tripped me up as an IoT developer is figuring out the best way to transmit data. There are many different kinds of radios and mediums. On top of that, there are different protocols to boot.</p>
<p>As of this writing, there is one protocol that has reigned supreme in the IoT world:</p>
<p>MQTT.</p>
<p>Unlike an HTTP server, a device can connect, publish, and subscribe to topics. These topics then get sent to a broker and distributed to other subscribed devices. It also happens that MQTT on Nordic’s nRF9160 is well supported.</p>
<p>In this post, I’ll show you how to connect the nRF9160 Feather to a self-hosted <a target="_blank" href="https://github.com/eclipse/mosquitto">Mosquitto</a> instance. You’ll learn how to generate your own certificates, and get a hang of how to test your connections.</p>
<p>Ready to play? Let’s get to it.</p>
<h2 id="heading-where-to-host">Where to host?</h2>
<p>If you want to host Mosquitto, you’ll need a server. Since Mosquitto is written in C it’s lightweight and can go almost anywhere. Plus it sips resources so you can install it on a budget VPS without much worry. That’s where a VPS provider like Digital Ocean or Vultr comes in.</p>
<p>To set up a new server here are some steps:</p>
<ul>
<li>Login to Digital Ocean. If you don’t have Digital Ocean and would like to support click <a target="_blank" href="https://m.do.co/c/9574d3846a29">here</a> to create an account.</li>
<li>Create a new Droplet</li>
</ul>
<p><img src="https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.21_PM.png" alt="Create a new droplet" width="730" height="521" loading="lazy"></p>
<ul>
<li>Choose the FreeBSD 12.1 with UFS.</li>
</ul>
<p><img src="https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.29_PM.png" alt="Create a FreeBSD 12.1 Droplet with UFS" width="730" height="521" loading="lazy"></p>
<ul>
<li>Choose the $5 instance. That’s usually more than enough.</li>
</ul>
<p><img src="https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.42_PM.png" alt="Select tier of Droplet" width="730" height="521" loading="lazy"></p>
<ul>
<li>Make sure you import your public key. Otherwise, you won’t be able to immediately use password-less login.</li>
</ul>
<p><img src="https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.50_PM.png" alt="Choose authentication method" width="730" height="521" loading="lazy"></p>
<ul>
<li>Hit that green <strong>Create Droplet</strong> button, and let’s get this show on the road.</li>
</ul>
<p><img src="https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/Screen_Shot_2020-08-02_at_5.21.54_PM.png" alt="Create Droplet button" width="730" height="521" loading="lazy"></p>
<h3 id="heading-important-extra-step">Important Extra step</h3>
<p>For the certs to work with Mosquitto, you’ll have to set a domain to point to your VPS IP address. A CNAME or A record works. If you’re unsure how to do that, <a target="_blank" href="https://misago.gitbook.io/docs/setup/domain">here’s a good guide.</a> Note which (sub)domain you’ve used. We’ll need it in a bit…</p>
<h2 id="heading-install-mosquitto">Install Mosquitto</h2>
<p>I run my servers inside of a FreeBSD jail using <a target="_blank" href="https://bastillebsd.org/">Bastille</a>. In this tutorial, we’ll be skipping the jail part and focusing on getting the nRF9160 Feather working.</p>
<ul>
<li>You should be set with a Digital Ocean instance (or similar) using FreeBSD. If you haven't done that yet, head back up to the <strong>Where to host?</strong> section.</li>
<li>Next, to install <code>mosquitto</code> on your droplet, run <code>pkg install mosquitto</code>. If you’re running something other than FreeBSD, this command may differ. <code>apt-get install mosquitto</code> works on Debian-based systems. If you want the most up to date repositories, make sure you run <code>sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa</code> beforehand. Here’s what the full output on FreeBSD looks like:</li>
</ul>
<pre><code class="lang-bash">$ pkg install mosquitto
The package management tool is not yet installed on your system.
Do you want to fetch and install it now? [y/N]: y
Bootstrapping pkg from pkg+http://pkg.FreeBSD.org/FreeBSD:12:amd64/quarterly, please <span class="hljs-built_in">wait</span>...
Verifying signature with trusted certificate pkg.freebsd.org.2013102301... <span class="hljs-keyword">done</span>
[mosquitto] Installing pkg-1.14.6...
[mosquitto] Extracting pkg-1.14.6: 100%
Updating FreeBSD repository catalogue...
[mosquitto] Fetching meta.conf: 100%    163 B   0.2kB/s    00:01
[mosquitto] Fetching packagesite.txz: 100%    6 MiB   6.6MB/s    00:01
Processing entries: 100%
FreeBSD repository update completed. 31943 packages processed.
All repositories are up to date.
Updating database digests format: 100%
The following 4 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        c-ares: 1.16.1
        ca_root_nss: 3.55
        e2fsprogs-libuuid: 1.45.6
        mosquitto: 1.6.7

Number of packages to be installed: 4

The process will require 2 MiB more space.
682 KiB to be downloaded.

Proceed with this action? [y/N]: y
[mosquitto] [1/4] Fetching mosquitto-1.6.7.txz: 100%  226 KiB 231.1kB/s    00:01
[mosquitto] [2/4] Fetching ca_root_nss-3.55.txz: 100%  285 KiB 291.5kB/s    00:01
[mosquitto] [3/4] Fetching e2fsprogs-libuuid-1.45.6.txz: 100%   34 KiB  34.7kB/s    00:01
[mosquitto] [4/4] Fetching c-ares-1.16.1.txz: 100%  138 KiB 140.9kB/s    00:01
Checking integrity... <span class="hljs-keyword">done</span> (0 conflicting)
[mosquitto] [1/4] Installing ca_root_nss-3.55...
[mosquitto] [1/4] Extracting ca_root_nss-3.55: 100%
[mosquitto] [2/4] Installing e2fsprogs-libuuid-1.45.6...
[mosquitto] [2/4] Extracting e2fsprogs-libuuid-1.45.6: 100%
[mosquitto] [3/4] Installing c-ares-1.16.1...
[mosquitto] [3/4] Extracting c-ares-1.16.1: 100%
[mosquitto] [4/4] Installing mosquitto-1.6.7...
===&gt; Creating users
Using existing user <span class="hljs-string">'nobody'</span>.
[mosquitto] [4/4] Extracting mosquitto-1.6.7: 100%
=====
Message from ca_root_nss-3.55:

--
FreeBSD does not, and can not warrant that the certification authorities
whose certificates are included <span class="hljs-keyword">in</span> this package have <span class="hljs-keyword">in</span> any way been
audited <span class="hljs-keyword">for</span> trustworthiness or RFC 3647 compliance.

Assessment and verification of trust is the complete responsibility of the
system administrator.

This package installs symlinks to support root certificates discovery by
default <span class="hljs-keyword">for</span> software that uses OpenSSL.

This enables SSL Certificate Verification by client software without manual
intervention.

If you prefer to <span class="hljs-keyword">do</span> this manually, replace the following symlinks with
either an empty file or your site-local certificate bundle.

  * /etc/ssl/cert.pem
  * /usr/<span class="hljs-built_in">local</span>/etc/ssl/cert.pem
  * /usr/<span class="hljs-built_in">local</span>/openssl/cert.pem
=====
Message from mosquitto-1.6.7:

--
The mosquitto MQTT Python driver is now provided by net/py-paho-mqtt
</code></pre>
<p>All installed package configuration lives at <code>/usr/local/etc/mosquitto/</code>. We’ll need to edit <code>mosquitto.conf</code> in that folder to use certificates. Here’s what it looks like:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Daemon configuration</span>
pid_file /var/run/mosquitto.pid
user nobody

<span class="hljs-comment"># Port to use for the default listener.</span>
port 8885

<span class="hljs-comment"># At least one of cafile or capath must be defined.</span>
cafile /root/pki/ca.crt

<span class="hljs-comment"># Path to the PEM encoded server certificate.</span>
certfile /root/pki/issued/mosquitto.crt

<span class="hljs-comment"># Path to the PEM encoded keyfile.</span>
keyfile /root/pki/private/mosquitto.key

<span class="hljs-comment"># Path to CRL file</span>
<span class="hljs-comment">#crlfile /root/pki/crl.pem</span>

<span class="hljs-comment"># Each client has their own cert</span>
require_certificate <span class="hljs-literal">true</span>
use_identity_as_username <span class="hljs-literal">true</span>

<span class="hljs-comment"># listener port-number [ip address/host name]</span>
listener 1883
protocol mqtt

<span class="hljs-comment"># listener port-number [ip address/host name]</span>
<span class="hljs-comment"># listener 8080</span>
<span class="hljs-comment"># protocol websockets</span>

<span class="hljs-comment"># =================================================================</span>
<span class="hljs-comment"># Logging</span>
<span class="hljs-comment"># =================================================================</span>
log_dest syslog

<span class="hljs-comment"># Types of messages to log.</span>
log_type all
<span class="hljs-comment">#log_type warning</span>
<span class="hljs-comment"># websockets_log_level 127</span>

<span class="hljs-comment"># -----------------------------------------------------------------</span>
<span class="hljs-comment"># Default authentication and topic access control</span>
<span class="hljs-comment"># -----------------------------------------------------------------</span>
<span class="hljs-comment"># password_file /usr/local/etc/mosquitto/pwfile</span>
</code></pre>
<p>Before we can start the server we’ll need to provision some RSA certificates. We’ll get to that in the next step.</p>
<h2 id="heading-provision-certs">Provision Certs</h2>
<p>You can use <strong>easy-rsa</strong> to generate a CA server and client certs. (These instructions come from <a target="_blank" href="https://github.com/OpenVPN/easy-rsa/blob/master/README.quickstart.md">this guide</a>.) For production, you should generate your keys and certs on an offline machine. That way your private keys are safe if your server becomes a target.</p>
<p>First, install <code>easy-rsa</code>:</p>
<pre><code class="lang-bash">$ pkg install easy-rsa
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 1 package(s) will be affected (of 0 checked):

New packages to be INSTALLED:
        easy-rsa: 3.0.7

Number of packages to be installed: 1

44 KiB to be downloaded.

Proceed with this action? [y/N]: y
[mosquitto] [1/1] Fetching easy-rsa-3.0.7.txz: 100%   44 KiB  44.8kB/s    00:01
Checking integrity... <span class="hljs-keyword">done</span> (0 conflicting)
[mosquitto] [1/1] Installing easy-rsa-3.0.7...
[mosquitto] [1/1] Extracting easy-rsa-3.0.7: 100%
</code></pre>
<p>Then let's begin the cert creation process:</p>
<pre><code class="lang-fallback">$ easyrsa init-pki

Note: using Easy-RSA configuration from: /usr/local/share/easy-rsa/vars

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /root/pki
$
$ easyrsa build-ca

Note: using Easy-RSA configuration from: /usr/local/share/easy-rsa/vars
Using SSL: openssl OpenSSL 1.1.1d-freebsd  10 Sep 2019

Enter New CA Key Passphrase:
Re-Enter New CA Key Passphrase:
Generating RSA private key, 2048 bit long modulus (2 primes)
......................+++++
..................................................................................+++++
e is 65537 (0x010001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:testserver.jaredwolff.com

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/root/pki/ca.crt
</code></pre>
<p><strong>Note:</strong> You will be prompted for a password at the <code>build-ca</code> step. Make sure you keep this password handy.</p>
<p>Then to generate a server cert use:</p>
<pre><code class="lang-fallback">$ easyrsa gen-req mosquitto nopass

Note: using Easy-RSA configuration from: /usr/local/share/easy-rsa/vars
Using SSL: openssl OpenSSL 1.1.1d-freebsd  10 Sep 2019
Generating a RSA private key
...............+++++
........................................+++++
writing new private key to '/root/pki/easy-rsa-82720.X2NVQ0/tmp.akOxhO'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [mosquitto]:testserver.jaredwolff.com

Keypair and certificate request completed. Your files are:
req: /root/pki/reqs/mosquitto.req
key: /root/pki/private/mosquitto.key
$
$ easyrsa sign-req server mosquitto

Note: using Easy-RSA configuration from: /usr/local/share/easy-rsa/vars
Using SSL: openssl OpenSSL 1.1.1d-freebsd  10 Sep 2019

You are about to sign the following certificate.
Please check over the details shown below for accuracy. Note that this request
has not been cryptographically verified. Please be sure it came from a trusted
source or that you have verified the request checksum with the sender.

Request subject, to be signed as a server certificate for 825 days:

subject=
    commonName                = testserver.jaredwolff.com

Type the word 'yes' to continue, or any other input to abort.
  Confirm request details: yes
Using configuration from /root/pki/easy-rsa-82744.hyuGzt/tmp.lZHLEH
Enter pass phrase for /root/pki/private/ca.key:
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'testserver.jaredwolff.com'
Certificate is to be certified until Nov  3 01:12:53 2022 GMT (825 days)

Write out database with 1 new entries
Data Base Updated

Certificate created at: /root/pki/issued/mosquitto.crt
</code></pre>
<p>You’ll be prompted for both the Common Name (i.e. your server name) and the CA cert password in the above step. <strong>Important</strong>: the <strong>Common Name</strong> needs to match the domain name of your server! (Remember, we wrote that down earlier?)</p>
<p>To generate the nRF9160 cert, use:</p>
<pre><code class="lang-fallback">$ easyrsa gen-req nrf9160 nopass batch
$ easyrsa sign-req client nrf9160 batch
</code></pre>
<p>Follow the same procedure as earlier. The only difference is that we’re generating a <strong>client</strong> cert instead of a <strong>server</strong> cert.</p>
<p>Once complete, we’ll need some files. Here’s a full list:</p>
<p><strong>For your Mosquitto Server</strong></p>
<ul>
<li><code>/root/pki/ca.crt</code></li>
<li><code>/root/pki/private/mosquitto.key</code></li>
<li><code>/root/pki/issued/mosquitto.crt</code></li>
</ul>
<p><strong>For your nRF9160 Feather</strong></p>
<ul>
<li><code>/root/pki/ca.crt</code></li>
<li><code>/root/pki/private/nrf9160.key</code></li>
<li><code>/root/pki/issued/nrf9160.crt</code></li>
</ul>
<p>If you’re using the configuration from above, it’s already pointing to your server certificates. All we have to do now is start <code>mosquitto</code>!</p>
<pre><code class="lang-bash">$ service mosquitto start
Cannot <span class="hljs-string">'start'</span> mosquitto. Set mosquitto_enable to YES <span class="hljs-keyword">in</span> /etc/rc.conf or use <span class="hljs-string">'onestart'</span> instead of <span class="hljs-string">'start'</span>.
</code></pre>
<p>If you get an error about <code>mosquitto_enable</code> simply run:</p>
<pre><code class="lang-bash">$ sysrc mosquitto_enable=YES
$ service mosquitto start
Starting mosquitto.
</code></pre>
<p>This enables <code>mosquitto</code> to start when your system starts.</p>
<p>Now, check if <code>mosquitto</code> is running by using <code>ps aux</code>:</p>
<pre><code class="lang-bash">
$ ps aux
USER     PID %CPU %MEM   VSZ  RSS TT  STAT STARTED    TIME COMMAND
root   82401  0.0  0.2 11472 2424  -  SsJ  01:02   0:00.00 /usr/sbin/syslogd -ss
root   82457  0.0  0.2 11408 2284  -  IsJ  01:02   0:00.00 /usr/sbin/cron -J 60 -s
nobody 82900  0.0  0.6 16352 6212  -  SsJ  01:17   0:00.02 /usr/<span class="hljs-built_in">local</span>/sbin/mosquitto -c /usr/<span class="hljs-built_in">local</span>/etc/mosquitto/mosquitto.conf -d
root   82488  0.0  0.3 12096 2848  0  IJ   01:02   0:00.01 login [pam] (login)
root   82489  0.0  0.3 13092 3504  0  SJ   01:02   0:00.03 -csh (csh)
root   82902  0.0  0.3 11704 2540  0  R+J  01:17   0:00.00 ps aux
</code></pre>
<p>Now that we have a server loaded and running, let’s get the firmware working.</p>
<h2 id="heading-firmware-bits">Firmware bits</h2>
<p>Dealing with certificates on the nRF9160 Feather is a two-step process. The first step is to load the certificates using the <code>at_client</code> firmware. The second is to load the <code>mqtt_simple</code> library with added TLS support. Let’s tackle the certs first.</p>
<h3 id="heading-program-atclient-first">Program <code>at_client</code> first</h3>
<p>Change directories to <code>ncs/nrf/samples/nrf9160/at_client/</code> and start a fresh build:</p>
<pre><code class="lang-c">$ west build -b circuitdojo_feather_nrf9160ns -p
</code></pre>
<p>Then flash to your board using:</p>
<pre><code class="lang-c">$ west flash --erase
$ nrfjprog -r
</code></pre>
<p>We’ll need this sample on your board for the next step.</p>
<h3 id="heading-add-certs-to-device">Add certs to device</h3>
<p>To install our new certs, we’ll need nRF Connect Desktop installed. You can download it <a target="_blank" href="https://www.nordicsemi.com/Software-and-tools/Development-Tools/nRF-Connect-for-desktop/Download#infotabs">by going here.</a></p>
<p>You’ll also need a custom version of <strong>LTE Link Monitor.</strong> You can get the modified version on <a target="_blank" href="http://localhost:3000/files/pc-nrfconnect-linkmonitor-1.1.1.tgz">docs.jaredwolff.com.</a></p>
<p>First, Install nRF Connect Desktop app. Then, copy the LTE Link Monitor .tgz file to <code>%USERPROFILE%\.nrfconnect-apps\local</code> (on Windows) or <code>$HOME/.nrfconnect-apps/local</code> (on Linux/macOS). Here’s an example of where it is on Windows:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-11.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Close and re-open nRF Connect Desktop (if it’s open).</p>
<p>Then, click <em>Open</em> next to the v1.1.1 version of LTE Link Monitor. It will also have <strong>local</strong> written underneath it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-12.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, let’s launch it!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-13.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once you’ve opened the port, hit the reset button. Make sure you turn off <strong>Automatic requests.</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-14.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then in the command box send <strong>AT+CFUN=4</strong>. This will shut down your modem so it’s ready to upload certs. You can run <strong>AT+CFUN?</strong> to confirm your modem in that mode.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-15.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Open up the Certificate manager.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-16.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Make sure you set the security tag. In this case, I’m using 1234. This is an important identifier that you’ll need later. Make it whatever you want but I would avoid using 16842753. This is the default tag for NRF Cloud. You don’t want to blast away your nRF Cloud certs!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-17.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Copy and paste the contents of your <code>ca.crt</code>, <code>nrf9160.crt</code> and <code>nrf9160.key</code> into the boxes (in that order). You can easily get the certs by using <code>cat</code> on Unix/Linux:</p>
<pre><code class="lang-bash">$ cat cat.crt
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgIUDLkBxLLQO9wosNDtA7E9qvqHOxMwDQYJKoZIhvcNAQEL
BQAwJDEiMCAGA1UEAwwZdGVzdHNlcnZlci5qYXJlZHdvbGZmLmNvbTAeFw0yMDA3
MzEwMTExNDJaFw0zMDA3MjkwMTExNDJaMCQxIjAgBgNVBAMMGXRlc3RzZXJ2ZXIu
amFyZWR3b2xmZi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3
de1v8k+FXzY/Im7Z2YKS7wwbBRft5CUxqP1sdYJgMvheS9LhFufk81URZ0lHD9pK
aNPxU1UEmnLvVDTGLJ+YAmMH08xn17FS1R1UVPYzi2ouwqRM2pR9EStsSlP9Zj44
1MsdizABnnlkZndUVLL/gjc4cNsNncMLBSEbsz6b5WzhtAGg3rOpdAxSSblZVSFw
bquCgg5hb2NUzy+JxGtUIsE5d6CxTDdSs4Z3FK/RRYjmCG6qsaya4N5W35yf8h5O
StfKRecl3kq2kCnWa6P+lErG4wuxIBtMkgz2zV+zd1tz4aHXxSdoZTqLz7dTVbFA
zEVnKD+ZReBG+4fwUL6rAgMBAAGjgZ4wgZswHQYDVR0OBBYEFIvdGnjrxRPzvXQi
7XJ70LzpZSOjMF8GA1UdIwRYMFaAFIvdGnjrxRPzvXQi7XJ70LzpZSOjoSikJjAk
MSIwIAYDVQQDDBl0ZXN0c2VydmVyLmphcmVkd29sZmYuY29tghQMuQHEstA73Ciw
0O0DsT2q+oc7EzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0B
AQsFAAOCAQEAIzz1nSSDkPueNPlADRYMDOMFNkxoKA+gRXwDVa7y39As7IZp7Fqr
KAH79U1XtGyDlt6FPKTvDJ7jtd4y8auIGVQO7z3AG9pVU1imIWZHoIqgBUCEhsjp
uMxD23kRCX5kd9dsmF9WOGGxb4kkMv83Rh2rCONQmvnozuI3fJv2ZFX/ORoADGLP
OPSJPl11x+2rxPxiLi+T8RyzDh3DwqnPVsSnbRWV7hosaN0ip/cbnSTaIul9mbCY
ID6qm9leqlY/gha9aZfg+tv1Lm6PT6o8Pzek2VeDoIS5YERBMOwV84hQrZjV3vIE
jT6y663HGsl7KvqVaWdV3fM6Cr7f0QdR5A==
-----END CERTIFICATE-----
</code></pre>
<p>You’ll need everything from <code>-----BEGIN CERTIFICATE-----</code> to <code>-----END CERTIFICATE-----</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-18.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Check the <strong>Log</strong> area for more details. If all went well, it should say that your certificates updated.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-19.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-using-the-mqttsimple-example">Using the mqtt_simple example</h3>
<p>We’ll be using the <code>mqtt_simple</code> sample within the nRF Connect SDK repository. The full path is: <code>ncs/nrf/samples/nrf9160/mqtt_simple</code>. We’ll need to make a few edits to add full TLS compatibility. All the files are within the <code>mqtt_simple</code> directory.</p>
<p>First, we’ll have to update the <code>proj.conf</code> file. See the highlighted differences:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-20.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The <code># Set the PDP Context</code> section is especially important if you’re using a Hologram SIM card (included with the nRF9160 Feather). If you are using a SIM that doesn’t need it, you do not need this section.</p>
<p>Adopt your <code>CONFIG_MQTT_BROKER_HOSTNAME</code> to your hostname (configured at the beginning of this guide).</p>
<p>You’ll also have to add these lines in <code>KConfig</code>:</p>
<pre><code class="lang-c">config SEC_TAG
    <span class="hljs-keyword">int</span> <span class="hljs-string">"Security tag to use for the connection"</span>
    <span class="hljs-keyword">default</span> <span class="hljs-number">1234</span>

config PEER_VERIFY
    <span class="hljs-keyword">int</span> <span class="hljs-string">"Peer verify parameter for mqtt_client"</span>
    <span class="hljs-keyword">default</span> <span class="hljs-number">1</span>
    help
            Set to <span class="hljs-number">0</span> <span class="hljs-keyword">for</span> VERIFY_NONE, <span class="hljs-number">1</span> <span class="hljs-keyword">for</span> VERIFY_OPTIONAL, <span class="hljs-keyword">and</span> <span class="hljs-number">2</span> <span class="hljs-keyword">for</span> VERIFY_REQUIRED.
</code></pre>
<p>Finally in main add this block to the top of your file:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">if</span> defined(CONFIG_MQTT_LIB_TLS)</span>
<span class="hljs-keyword">static</span> <span class="hljs-keyword">sec_tag_t</span> sec_tag_list[] = { CONFIG_SEC_TAG };
<span class="hljs-meta">#<span class="hljs-meta-keyword">endif</span> <span class="hljs-comment">/* defined(CONFIG_MQTT_LIB_TLS) */</span></span>
</code></pre>
<p>Then add this block to <code>client_init</code> under <code>#if defined(CONFIG_MQTT_LIB_TLS)</code></p>
<pre><code class="lang-c">  <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">mqtt_sec_config</span> *<span class="hljs-title">tls_config</span> = &amp;<span class="hljs-title">client</span>-&gt;<span class="hljs-title">transport</span>.<span class="hljs-title">tls</span>.<span class="hljs-title">config</span>;</span>

    client-&gt;transport.type = MQTT_TRANSPORT_SECURE;

    tls_config-&gt;peer_verify = CONFIG_PEER_VERIFY;
    tls_config-&gt;cipher_count = <span class="hljs-number">0</span>;
    tls_config-&gt;cipher_list = <span class="hljs-literal">NULL</span>;
    tls_config-&gt;sec_tag_count = ARRAY_SIZE(sec_tag_list);
    tls_config-&gt;sec_tag_list = sec_tag_list;
    tls_config-&gt;hostname = CONFIG_MQTT_BROKER_HOSTNAME;
</code></pre>
<p>The changes should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-21.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then build with:</p>
<pre><code class="lang-c">$ west build -b circuitdojo_feather_nrf9160ns -p
</code></pre>
<p>Finally, flash it using <code>west flash</code>:</p>
<pre><code class="lang-c">$ west flash --erase
$ nrfjprog -r
</code></pre>
<p>Open your serial terminal and double check that your nRF9160 Feather is connecting. You can also use LTE Link Monitor to view your progress as well (Example below).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-22.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Lots of the information above came from <a target="_blank" href="https://devzone.nordicsemi.com/nordic/cellular-iot-guides/b/software-and-protocols/posts/enabling-and-testing-tls-in-mqtt_5f00_simple">Nordic</a>'s post on the subject.</p>
<h2 id="heading-sending-a-message">Sending a message</h2>
<p>We’re almost there! You’ve configured your nRF9160 Feather to connect to Mosquitto using self-generated certificates. The last part is connecting another device to see if the nRF9160 Feather replies to a message.</p>
<p>I’ve created a new set of certs for this purpose. I called them <code>test</code>.</p>
<pre><code class="lang-bash">$ easyrsa gen-req <span class="hljs-built_in">test</span> nopass batch
$ easyrsa sign-req client <span class="hljs-built_in">test</span> batch
</code></pre>
<p>I copied them to my desktop using CyberDuck (a great little visual SFTP client):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-23.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You can also use something like <code>scp</code> if you're confident in your command line file transfer abilities. Then, open a terminal and run:</p>
<pre><code class="lang-bash">mosquitto_sub --cafile ca.crt --cert test.crt --key test.key -q 1 -d -h testserver.jaredwolff.com -p 8885 -t <span class="hljs-string">"/my/publish/topic"</span> &amp;
mosquitto_pub --cafile ca.crt --cert test.crt --key test.key -q 1 -d -h testserver.jaredwolff.com -p 8885 -t <span class="hljs-string">"/my/subscribe/topic"</span> -m <span class="hljs-string">"hello there"</span>
</code></pre>
<p>You should see an output like this:</p>
<pre><code class="lang-bash">$ mosquitto_sub --cafile ca.crt --cert test.crt --key test.key -q 1 -d -h testserver.jaredwolff.com -p 8885 -t <span class="hljs-string">"/my/publish/topic"</span> &amp;
$ mosquitto_pub --cafile ca.crt --cert test.crt --key test.key -q 1 -d -h testserver.jaredwolff.com -p 8885 -t <span class="hljs-string">"/my/subscribe/topic"</span> -m <span class="hljs-string">"hello there"</span>
Client mosq-CczskQKzMKdtTo4O4s sending CONNECT
Client mosq-CczskQKzMKdtTo4O4s received CONNACK (0)
Client mosq-CczskQKzMKdtTo4O4s sending PUBLISH (d0, q1, r0, m1, <span class="hljs-string">'/my/subscribe/topic'</span>, ... (11 bytes))
Client mosq-CczskQKzMKdtTo4O4s received PUBACK (Mid: 1, RC:0)
Client mosq-CczskQKzMKdtTo4O4s sending DISCONNECT
MacBook-Pro:Downloads jaredwolff$ Client mosq-qK8tMlJk0Qri4Z7jUo sending PINGREQ
Client mosq-qK8tMlJk0Qri4Z7jUo received PINGRESP
MacBook-Pro:Downloads jaredwolff$ Client mosq-qK8tMlJk0Qri4Z7jUo received PUBLISH (d0, q0, r0, m0, <span class="hljs-string">'/my/publish/topic'</span>, ... (11 bytes))
hello there
</code></pre>
<p>Booyah! You have an active working connection to your very own Mosquitto server.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>We’ve made it to the end! By this point in the post, you should have a Mosquitto server running and an nRF9160 connected. Now you can use your new-found skills to add more devices to your deployments and more.</p>
<p>If you haven’t had a chance to play with the nRF9160 you should check out the nRF9160 Feather. It has Nordic Semiconductor’s nRF9160 LTE-M, NB IoT + GPS Combo, plus flexible power supply, external flash, and low power shutdown. </p>
<p>Oh, and did I mention it’s 100% open source? Learn more by checking out the campaign on <a target="_blank" href="https://www.jaredwolff.com/store/nrf91-feather/">GroupGets and Hackster Launch.</a> ?</p>
<p><img src="https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/IMG_8749.jpg" alt="nRF9160 Feather Top Side" width="730" height="730" loading="lazy"></p>
<p><img src="https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/images/IMG_8750.jpg" alt="nRF9160 Feather Bottom Side" width="730" height="730" loading="lazy"></p>
<p>Photo credit to the awesome folks at <a target="_blank" href="https://www.groupgets.com/">GroupGets</a>!</p>
<p><strong>You can read this article and lots of other good stuff at <a target="_blank" href="https://www.jaredwolff.com/how-to-connect-nrf9160-feather-to-mosquitto/">jaredwolff.com</a>.</strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Meet Pyrinas - an IoT Development Kit For Your Particle Xenon ]]>
                </title>
                <description>
                    <![CDATA[ By Jared Wolff This post is a bit lengthy. If you prefer, signup to get the full PDF here. ? After Particle's Mesh deprecation announcement, many have been left to figure out how to deploy their low power sensor networks. There was always the option ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/meet-pyrinas-an-iot-development-kit-for-your-particle-xenon/</link>
                <guid isPermaLink="false">66d85060c15439a8d5631e83</guid>
                
                    <category>
                        <![CDATA[ Bluetooth Low Energy ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ iot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ particle ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 11 Mar 2020 13:49:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/Copy-of-Particle-Mesh-App-Updates.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Jared Wolff</p>
<p>This post is a bit lengthy. If you prefer, <a target="_blank" href="https://www.jaredwolff.com/files/pyrinas-intro/">signup to get the full PDF here.</a> ?</p>
<p>After <a target="_blank" href="https://www.jaredwolff.com/particle-mesh-deprecation-livestream/">Particle's Mesh deprecation announcement</a>, many have been left to figure out how to deploy their low power sensor networks. There was always the option of using <a target="_blank" href="https://www.jaredwolff.com/how-to-use-particles-powerful-bluetooth-api/">Particle's Built in Bluetooth stack</a> but as it stands today it's not secure.</p>
<p>Previously I had helped form a very simple nRF SDK-based hub and spoke IoT deployment. Unfortunately, it was closed source and the company is no longer around.</p>
<p>So what's a guy to do?</p>
<p>Build another one and make it <em>open. (BSD licensed to be exact!)</em> Open and free for anyone to use adopt and improve upon. Plus if you're building a product that's using the code, <strong>you don't have to share your improvements or proprietary code with anyone.</strong></p>
<p>In this post I'll be talking about how to get started with Pyrinas. It uses Nordic's time tested SDK as a basis for the kernel of the system. The main concept of Pyrinas is to abstract as much IoT cruft away so you can focus on your application.</p>
<p>So without further ado, let's chat about what Pyrinas is and what it isn't.</p>
<h2 id="heading-what-pyrinas-is">What Pyrinas is</h2>
<ul>
<li>Is an embedded "kernel", written in C. It's open and permissive IoT development environment you can use for anything you want. Seriously. It's BSD licensed and can be used in closed source applications.</li>
<li>Using the power of Bluetooth 5.0 Long Range, Pyrinas allows you to communicate with many peripheral devices at one time. Currently Pyrinas has been tested with 3 simultaneous peripheral connections. Theoretically, it can support up to 20 simultaneous connections. (Thanks to <a target="_blank" href="https://cm.nordicsemi.com//Software-and-tools/Software/Bluetooth-Software">Nordic's S140 Softdevice</a>)</li>
<li><p>Pyrinas transports its data in two ways</p>
<ul>
<li>In a familiar string format used by Particle</li>
<li>A custom Protocol Buffer for raw data transmission.</li>
</ul>
<p>That way you have a <strong>choice</strong> of how you want to process and publish your data!</p>
</li>
</ul>
<h2 id="heading-what-pyrinas-isnt">What Pyrinas isn't</h2>
<ul>
<li>Pyrinas is not a RTOS (real time operating system). If you have a need to run multiple threaded applications on embedded, Pyrinas is not for you.</li>
<li>Pyrinas, at this time, does not support Mesh.</li>
<li>An OS for every single kind of Bluetooth SoC on the market. Due to the tight coupling with Nordic's nRF SDK, Pyrinas only works with Nordic's SoCs.</li>
<li>A turnkey solution for IoT. Pyrinas is early on it it's development process. The aim is for it to become a viable option for anyone to develop and publish IoT applications <em>the way they want to.</em> There's no vendor lock in. There's no surprises.</li>
</ul>
<h2 id="heading-things-youll-need">Things you'll need</h2>
<p>There are a few things you'll need in order to get going with Pyrinas.</p>
<ul>
<li>At least 2 Particle Xenons</li>
<li>At least 1 nRF Development board or J-link programmer</li>
<li>Associated USB cables</li>
</ul>
<h2 id="heading-getting-started-with-an-example">Getting started with an example</h2>
<p>Getting started with Pyrinas involves two repositories.</p>
<ul>
<li><a target="_blank" href="https://github.com/pyrinas-iot/pyrinas-os">The OS respository</a></li>
<li><a target="_blank" href="https://github.com/pyrinas-iot/pyrinas-template">The template</a></li>
</ul>
<p>The OS directory has all the source, SDK dependencies and toolchain you need to use Pyrinas.</p>
<p>The template is where you add all your application code. The template provides a starting point for you and your project.</p>
<p>Here's how everything goes together:</p>
<p>Clone the OS directory to some place on your machine:</p>
<pre><code class="lang-shell">git clone https://github.com/pyrinas-iot/pyrinas-os.git --recurse-submodules
</code></pre>
<p>Once complete, change directories to <code>pyrinas-os</code> and run <code>make setup</code></p>
<pre><code class="lang-shell">cd pyrinas-os
make setup
</code></pre>
<p>This will download your toolchain and SDK dependencies.</p>
<p>In order to use OTA DFU, you will need to also generate the DFU key for the process:</p>
<pre><code class="lang-shell">make gen_key
</code></pre>
<p>The files generated by this process will be used later.</p>
<p>Next, we'll want to use the template to make two new projects. In this example we'll have one "hub" and one "sensor." Simply navigate to the <a target="_blank" href="https://github.com/pyrinas-iot/pyrinas-template">template repository</a> and click the <strong>Use this template</strong> button.</p>
<p><img src="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.12.21_PM.png" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.12.21_PM.png" width="600" height="400" loading="lazy"></p>
<p>Then name your new repository. Click the <strong>Create repository from template</strong> button when you're happy with everything.</p>
<p><img src="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.20.15_PM.png" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.20.15_PM.png" width="600" height="400" loading="lazy"></p>
<p>Then clone your repository to the same directory as <code>pyrinas-os</code>. Make sure you replace <code>&lt;your username&gt;</code> and <code>&lt;repo name&gt;</code> with your own.</p>
<pre><code class="lang-shell">cd ..
git clone https://github.com/&lt;your username&gt;/&lt;repo name&gt;.git hub
</code></pre>
<p>After this is done, go back and create a new repository from the template. We'll be using this one for the <em>sensor</em> node.</p>
<p><img src="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.24.15_PM.png" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Screen_Shot_2020-03-09_at_5.24.15_PM.png" width="600" height="400" loading="lazy"></p>
<p>Clone this repository once you're done setting it up in the same place as your <code>hub</code> and <code>pyrinas-os</code> repositories.</p>
<p>Now that we have all our repositories, let's start with our sensor node.</p>
<h3 id="heading-setting-up-the-sensor-node-repository">Setting up the sensor node repository</h3>
<p>Open up the sensor repository using a program like Microsoft's VS Code. If you have the command line shortcuts you can use <code>code</code> to open it from the terminal:</p>
<pre><code class="lang-shell">code sensor
</code></pre>
<p>Before we do anything we'll need to set up the symbolic link to <code>pyrinas-os</code>. Make sure you're in the <code>sensor</code> directory and then run <code>ln -s ../pyrinas-os/</code> using the terminal.</p>
<pre><code class="lang-shell">cd sensor
ln -s ../pyrinas-os/ .
</code></pre>
<p>This allows your project to use all the code, SDK and toolchains within the <code>pyrinas-os</code> repository! As an added bonus you can do this as many times as you want. Have multiple Pyrinas projects? No problem.</p>
<p>Alright! Now, let's check out the Makefile. You'll want to customize some of the definitions within the file:</p>
<pre><code class="lang-makefile"><span class="hljs-comment"># Start: Your configuration!</span>

<span class="hljs-comment"># Set this to the directory of pyrinas-os</span>
<span class="hljs-comment"># If you used a symbolic link this points to</span>
<span class="hljs-comment"># the `pyrinas-os` folder in this repository</span>
OS_DIR := pyrinas-os

<span class="hljs-comment"># This should be the serial number of your Jlink programmer.</span>
<span class="hljs-comment"># To find simply run `jlinkexe`</span>
PROG_SERIAL=1234678

<span class="hljs-comment"># This is your debugger port for Jlink's RTT. If you</span>
<span class="hljs-comment"># have mulitple, you will have to change this on each app</span>
<span class="hljs-comment"># your're using</span>
PROG_PORT=19021

<span class="hljs-comment"># This is where you set your board type. Here are the supported boards:</span>
<span class="hljs-comment"># xenon - Particle Xenon</span>
BOARD=xenon

<span class="hljs-comment"># This is where you can name your app something. Make it specific</span>
APP_FILENAME=pyrinas-template

<span class="hljs-comment"># This determines whether or not you're using debug mode</span>
<span class="hljs-comment"># Comment this out or change to false</span>
DEBUG=true

<span class="hljs-comment"># End: Your Configuration</span>
</code></pre>
<p>For example, you may want to setup your programmer serial. This allows you to use multiple programmers at the same time. (Very helpful in debugging both devices at the same time) To get your programmer's serial plug in your development board and run <code>jlinkexe</code>.</p>
<p>    jlinkexe
    SEGGER J-Link Commander V6.62a (Compiled Jan 31 2020 12:59:22)
    DLL version V6.62a, compiled Jan 31 2020 12:59:05</p>
<p>    Connecting to J-Link via USB...O.K.
    Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Jan 21 2020 17:30:48
    Hardware version: V1.00
    S/N: 581758669
    License(s): RDI, FlashBP, FlashDL, JFlash, GDB
    VTref=3.300V</p>
<p>    Type "connect" to establish a target connection, '?' for help
    J-Link&gt;</p>
<p>Find the <strong>S/N</strong> area. This is your serial number!</p>
<p>Alternatively you can look at the sticker on your development kit. It will contain the serial number for your device.</p>
<p>For the <code>PROG_PORT</code> you want to use different ports for each device you're simultaneously debugging. I've found <strong>19021</strong> and <strong>19020</strong> are perfectly good options for most two-device debugging sessions.</p>
<p>The Makefile also includes the ability to choose a board. In our case there's only one option: <code>xenon</code>. Future revisions of Pyrinas will have multiple options.</p>
<p><code>APP_FILENAME</code> is the name of your app. We'll rename ours to <code>pyrinas-sensor</code></p>
<p>Finally, <code>DEBUG</code> is used to halt execution either on an error or restart. For production this should be commented out or set to <code>false</code>. We can leave it as is for now.</p>
<p>The Makefile is also the source for some of the most important commands you'll need during development:</p>
<ul>
<li><code>make build</code> - builds your app.</li>
<li><code>make clean</code> - cleans all remnants of your app.</li>
<li><code>make debug</code> - opens up the <code>jlinkexe</code> debugger console.</li>
<li><code>make erase</code> - erases the chip attached to your programmer.</li>
<li><code>make flash</code> - flashes the current app to your connected device.</li>
<li><code>make flash_softdevice</code> - flashes the soft_device</li>
<li><code>make rtt</code> - opens up the debug console.</li>
<li><code>make ota</code> - generates a zip file used for BLE DFU</li>
</ul>
<h3 id="heading-basic-sensor-node-code">Basic sensor node code</h3>
<p><img src="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-3.jpg" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-3.jpg" width="600" height="400" loading="lazy"></p>
<p>Now that we have some of the basics out of the way, let's create a very simple application that publishes on a set interval. If you look at <code>app.c</code>, you'll see some code in the <code>setup()</code> function. Let's delete all the commented out code. (We'll use it later for the hub though)</p>
<p>Your code should now look something like:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"app.h"</span></span>

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span>
</span>{
  BLE_STACK_PERIPH_DEF(init);

  <span class="hljs-comment">// Configuration for ble stack</span>
  ble_stack_init(&amp;init);

  <span class="hljs-comment">// Start advertising</span>
  advertising_start();
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span>
</span>{
}
</code></pre>
<p>Now let's create a timer that we'll use to publish on a set interval. Under <code>#include "app.h"</code> create a new timer:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"app.h"</span></span>

timer_define(m_sensor_timer);
</code></pre>
<p>We also need to set it up in the <code>setup()</code> function:</p>
<pre><code class="lang-c"><span class="hljs-comment">// Sensor timer</span>
timer_create(&amp;m_sensor_timer, TIMER_REPEATED, sensor_timer_evt);
</code></pre>
<p>You'll notice that <code>timer_create</code> is referring to a event callback called <code>sensor_timer_evt</code>. We'll need to create that guy as well:</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">sensor_timer_evt</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// We'll come back in a sec</span>
}
</code></pre>
<p>The last thing is to start the timer. Let's do that underneath <code>timer_create</code>:</p>
<pre><code class="lang-c"><span class="hljs-comment">// Start</span>
timer_start(&amp;m_sensor_timer, <span class="hljs-number">1000</span>);
</code></pre>
<p>This will start our repeating timer on a 1 second interval.  (<code>timer_start</code> is configured using milliseconds)</p>
<p>Now, inside <code>sensor_timer_evt</code> we'll publish some data. First though we need to make sure that Bluetooth is connected using <code>ble_is_connected</code>.</p>
<pre><code class="lang-c"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> sensor_timer_evt
{
  <span class="hljs-comment">// Check if we're connected</span>
  <span class="hljs-keyword">if</span> (ble_is_connected())
  {
    <span class="hljs-comment">// Sends "ping" with the event name of "data"</span>
    ble_publish(<span class="hljs-string">"data"</span>, <span class="hljs-string">"ping"</span>);
  }
}
</code></pre>
<p>Inside the if statement, we'll use <code>ble_publish</code>. The first argument is the name of the event and the second is the value.</p>
<p>Next, in order to receive messages from the other end we'll need to setup a callback:</p>
<pre><code class="lang-c"><span class="hljs-comment">// Configuration for ble stack</span>
ble_stack_init(&amp;init);

<span class="hljs-comment">// Setup BLE callback</span>
ble_subscribe(<span class="hljs-string">"data"</span>, ble_evt);
</code></pre>
<p>We'll define <code>ble_evt</code> at the top of the file:</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ble_evt</span><span class="hljs-params">(<span class="hljs-keyword">char</span> *name, <span class="hljs-keyword">char</span> *data)</span>
</span>{
  NRF_LOG_INFO(<span class="hljs-string">"%s: %s"</span>, name, data);
}
</code></pre>
<p>In this case we'll use <code>NRF_LOG_INFO</code> to print out the message from the hub.</p>
<p>Finally, in order to get the MAC address easily, we'll have to add a call to print it out in <code>setup()</code>.</p>
<pre><code class="lang-c"><span class="hljs-comment">// Print the address</span>
util_print_device_address();
</code></pre>
<p>In the end your file should look something like this:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"app.h"</span></span>

timer_define(m_sensor_timer);

<span class="hljs-comment">// Catch events sent over Bluetooth</span>
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ble_evt</span><span class="hljs-params">(<span class="hljs-keyword">char</span> *name, <span class="hljs-keyword">char</span> *data)</span>
</span>{
  NRF_LOG_INFO(<span class="hljs-string">"%s: %s"</span>, name, data);
}

<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">sensor_timer_evt</span><span class="hljs-params">()</span>
</span>{
  <span class="hljs-comment">// Check if we're connected</span>
  <span class="hljs-keyword">if</span> (ble_is_connected())
  {
    <span class="hljs-comment">// Sends "ping" with the event name of "data"</span>
    ble_publish(<span class="hljs-string">"data"</span>, <span class="hljs-string">"ping"</span>);
  }
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span>
</span>{
  BLE_STACK_PERIPH_DEF(init);

  <span class="hljs-comment">// Configuration for ble stack</span>
  ble_stack_init(&amp;init);

  <span class="hljs-comment">// Setup BLE callback</span>
  ble_subscribe(<span class="hljs-string">"data"</span>, ble_evt);

  <span class="hljs-comment">// Start advertising</span>
  advertising_start();

  <span class="hljs-comment">// Sensor sensor timer.</span>
  timer_create(&amp;m_sensor_timer, TIMER_REPEATED, sensor_timer_evt);

  <span class="hljs-comment">// Start</span>
  timer_start(&amp;m_sensor_timer, <span class="hljs-number">1000</span>);

  <span class="hljs-comment">// Print the address</span>
  util_print_device_address();
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span>
</span>{
}
</code></pre>
<p>Next, we'll program it to some hardware!</p>
<h3 id="heading-flashing-the-basic-sensor-code">Flashing the basic sensor code:</h3>
<p>For this step you'll need to have a Xenon handy. You'll also need a programmer, programming cable and two Micro-B USB cables. Here's a picture of everything connected:</p>
<p><img src="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//IMG_4586.jpg" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//IMG_4586.jpg" width="600" height="400" loading="lazy"></p>
<p>Once connected and powered run these commands:</p>
<pre><code class="lang-shell">make erase
make flash_softdevice
make flash
make debug
</code></pre>
<p>Then in a separate terminal window run</p>
<pre><code class="lang-shell">make rtt
</code></pre>
<p><code>make debug</code> and <code>make rtt</code> will create a debugging session. You can issue commands in the <code>make debug</code> terminal to control the device as well. For instance, <code>r</code> followed by <code>Enter</code> will restart the device. (By far my most common use case).</p>
<p>If you've flashed everything successfully, your device should start blinking green. That's a good sign!</p>
<p>Additionally, if you take a look at the <code>make rtt</code> side your output should be similar to this:</p>
<p>    ###RTT Client: Connecting to J-Link RTT Server via localhost:19021 ...</p>
<p>    ###RTT Client: Connected.</p>
<p>    SEGGER J-Link V6.62a - Real time terminal output
    J-Link OB-SAM3U128-V2-NordicSemi compiled Jan 21 2020 17:30:48 V1.0, SN=581758669
    Process: JLinkExe
     app_timer: RTC: initialized.
     app: Boot count: 4
     app: Pyrinas started.
     app: Address: 11:22:33:44:55:66</p>
<p>Take note of the address displayed above. We'll need that for the hub code!</p>
<h3 id="heading-setting-up-the-hub-repository">Setting up the hub repository</h3>
<p><img src="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-2.jpg" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-2.jpg" width="600" height="400" loading="lazy"></p>
<p>If you haven't already, clone your hub repository locally. We'll want to do some of the same steps as we did with the sensor repo like:</p>
<ul>
<li>Setting up the symbolic link</li>
<li>Updating the Makefile<ul>
<li>Setting your <code>PROG_SERIAL</code></li>
<li>Setting <code>PROG_PORT</code> to a port not used by the sensor setup. <code>19020</code> in this case is fine.</li>
<li>Setting <code>APP_FILENAME</code> to <code>pyrinas-hub</code></li>
</ul>
</li>
</ul>
<p>If you need a reminder how any of these steps work, go back and review the earlier section.</p>
<p>Next, we'll want to open <code>app.c</code> and uncomment the central/hub based code. Plus you'll want to remove the default un-commented code. Your <code>setup()</code> should look something like this:</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span>
</span>{
  <span class="hljs-comment">// Default config for central mode</span>
  BLE_STACK_CENTRAL_DEF(init);

  <span class="hljs-comment">// Add an addresses to scan for</span>
  <span class="hljs-keyword">ble_gap_addr_t</span> first = {
      .addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC,
      .addr = {<span class="hljs-number">0x81</span>, <span class="hljs-number">0x64</span>, <span class="hljs-number">0x4C</span>, <span class="hljs-number">0xAD</span>, <span class="hljs-number">0x7D</span>, <span class="hljs-number">0xC0</span>}};
  init.config.devices[<span class="hljs-number">0</span>] = first;

  <span class="hljs-keyword">ble_gap_addr_t</span> second = {
      .addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC,
      .addr = {<span class="hljs-number">0x7c</span>, <span class="hljs-number">0x84</span>, <span class="hljs-number">0x9d</span>, <span class="hljs-number">0x32</span>, <span class="hljs-number">0x8d</span>, <span class="hljs-number">0xe4</span>}};
  init.config.devices[<span class="hljs-number">1</span>] = second;

  <span class="hljs-comment">// Increment the device_count</span>
  init.config.device_count = <span class="hljs-number">2</span>;

  <span class="hljs-comment">// Configuration for ble stack</span>
  ble_stack_init(&amp;init);

  <span class="hljs-comment">// Start scanning.</span>
  scan_start();
}
</code></pre>
<p>You'll notice immediately that there are two clients/devices defined here. Let's remove the second one. Should you, in the future, want to connect more devices this is an example of how to do it.</p>
<p><strong>Reminder:</strong> also make sure that you change the <code>init.config.device_count</code> to <code>1</code>.</p>
<p>Then, you'll want to update the <code>.addr</code> field in <code>ble_gap_addr_t first</code> to match the address we got earlier from <code>make rtt</code>:</p>
<pre><code class="lang-c"><span class="hljs-comment">// Add an addresses to scan for</span>
<span class="hljs-keyword">ble_gap_addr_t</span> first = {
    .addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC,
    .addr = {<span class="hljs-number">0x11</span>,<span class="hljs-number">0x22</span>,<span class="hljs-number">0x33</span>,<span class="hljs-number">0x44</span>,<span class="hljs-number">0x55</span>,<span class="hljs-number">0x66</span>}};
init.config.devices[<span class="hljs-number">0</span>] = first;
</code></pre>
<p>The address field uses raw bytes, so it has to be represented that way. Remove the <code>:</code> and place <code>0x</code> in front of each byte. We end up going from <code>11:22:33:44:55:66</code> to <code>{0x11,0x22,0x33,0x44,0x55,0x66}</code></p>
<p>Now before we flash anything, let's also set up the Bluetooth event handler. As with earlier we'll use <code>ble_subscribe</code> to attach an event handler:</p>
<pre><code class="lang-c"><span class="hljs-comment">// Setup BLE callback</span>
ble_subscribe(<span class="hljs-string">"data"</span>, ble_evt);

Then place the function at the top of the file:

<span class="hljs-comment">// Catch events sent over Bluetooth</span>
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ble_evt</span><span class="hljs-params">(<span class="hljs-keyword">char</span> *name, <span class="hljs-keyword">char</span> *data)</span>
</span>{
  NRF_LOG_INFO(<span class="hljs-string">"%s: %s"</span>, name, data);

  ble_publish(<span class="hljs-string">"data"</span>, <span class="hljs-string">"pong"</span>);
}
</code></pre>
<p>You'll notice we're printing out the message using <code>NRF_LOG_INFO</code>. We're also sending a message <em>back</em> to the sensor in the form of <code>ble_publish("data","pong");</code> In other-words we're playing a game of ping-pong between the two devices!</p>
<p>In the end your code should look something like this:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">"app.h"</span></span>

<span class="hljs-comment">// Catch events sent over Bluetooth</span>
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ble_evt</span><span class="hljs-params">(<span class="hljs-keyword">char</span> *name, <span class="hljs-keyword">char</span> *data)</span>
</span>{
  NRF_LOG_INFO(<span class="hljs-string">"%s: %s"</span>, name, data);

  ble_publish(<span class="hljs-string">"data"</span>, <span class="hljs-string">"pong"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span>
</span>{
  <span class="hljs-comment">// Default config for central mode</span>
  BLE_STACK_CENTRAL_DEF(init);

  <span class="hljs-comment">// Add an addresses to scan for</span>
  <span class="hljs-keyword">ble_gap_addr_t</span> first = {
      .addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC,
      .addr = {<span class="hljs-number">0x11</span>, <span class="hljs-number">0x22</span>, <span class="hljs-number">0x33</span>, <span class="hljs-number">0x44</span>, <span class="hljs-number">0x55</span>, <span class="hljs-number">0x66</span>}};
  init.config.devices[<span class="hljs-number">0</span>] = first;

  <span class="hljs-comment">// Increment the device_count</span>
  init.config.device_count = <span class="hljs-number">1</span>;

  <span class="hljs-comment">// Configuration for ble stack</span>
  ble_stack_init(&amp;init);

  <span class="hljs-comment">// Setup BLE callback</span>
  ble_subscribe(<span class="hljs-string">"data"</span>, ble_evt);

  <span class="hljs-comment">// Start scanning.</span>
  scan_start();
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span>
</span>{
}
</code></pre>
<p><strong>Reminder:</strong> make sure you set <code>ble_gap_addr_t first</code> or the two devices will not connect!</p>
<p>To program, connect the Xenon to program as you did before. We'll flash it using the same methods as before:</p>
<pre><code class="lang-shell">make erase
make flash_softdevice
make flash
make debug
</code></pre>
<p>Then in a separate terminal window run</p>
<pre><code class="lang-shell">make rtt
</code></pre>
<p>Then take a look at each of the <code>make rtt</code> screens. There should be some output! For the hub it should look something like this:</p>
<p>    Process: JLinkExe
     app: Boot count: 4
     app: Pyrinas started.
     ble_m_central: Connected to handle 0x0
     ble_m_central: Protobuf Service discovered
     app: data: ping
     app: data: ping</p>
<p>And the sensor side like this:</p>
<p>    Process: JLinkExe
     app_timer: RTC: initialized.
     app: Boot count: 4
     app: Pyrinas started.
     app: Address: 11:22:33:44:55:66
     ble_m_periph: Notifications enabled!
     app: data: pong
     app: data: pong</p>
<p>The ping and pong messages should continue until you stop them. Awesome! If you get any warnings like this one:</p>
<p>    Unable to write. Notifications not enabled!</p>
<p>Use the reset button on either of the devices. This should fix the problem.</p>
<p><strong>Side note:</strong> the pairing process for Bluetooth is inherently <strong><em>insecure</em></strong>. Once the pairing process is complete though, the devices are secure. (With the caveat that no one was sniffing the pairing process!) There may be improvements on security in the future.</p>
<p><strong>Congrats! ?</strong>If you've made it this far, you've deployed your first Pyrinas hub and sensor client! </p>
<p>For more information about what Pyrinas can do you should check out the header files under <code>pyrinas-os/include/</code>. Also, Pyrinas can do anything that you'd normally be able to do with Nordic's SDK. <a target="_blank" href="https://infocenter.nordicsemi.com/topic/struct_sdk/struct/sdk_nrf5_latest.html?cp=7_1">Nordic's Infocenter</a> is a great resource for learning more about what the SDK has to offer.</p>
<h2 id="heading-what-does-the-future-hold-for-pyrinas">What does the future hold for Pyrinas?</h2>
<p><img src="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-5.jpg" alt="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/images//Copy_of_Particle_Mesh_App_Updates-5.jpg" width="600" height="400" loading="lazy"></p>
<p>All future tasks for Pyrinas are shared openly on the <a target="_blank" href="https://github.com/pyrinas-iot/pyrinas-os/projects">Github Repo.</a> Here are some of the high level improvements on the roadmap:</p>
<ul>
<li>Particle Boron + LTE support - That's right! Cellular will be coming to Pyrinas. As of this writing, the first board to support Pyrinas LTE will be Particle's Boron.</li>
<li>MQTT (over TLS) and HTTPS support - Once we have cellular, we need something to communicate with. That's where MQTT and HTTPS come in. They're some of the most popular protocols for IoT today.</li>
<li>Built in remote OTA support - As it stands today, devices programmed with Pyrinas uses Nordic's Secure Bootloader. That means they can be updated over the air by a computer or cellphone nearby. This isn't sustainable for long term deployments though!
Instead, you will be able to push updates to Pyrinas devices over the Cloud. Yup. No reason to get off your couch, you can deploy your updates from anywhere.</li>
<li>Dynamic configuration and management - adding and removing devices from a Pyrinas system currently takes some effort. In future revisions, it will be easier to add and remove devices on the fly. This allows for remote device management with zero headaches.</li>
<li>Support for pre-certified modules and other development boards based on Nordic's nRF52840. Currently the Xenon is the only supported board. Development boards aren't great for full production though. Stay tuned for support for pre-certified modules from vendors like <a target="_blank" href="https://www.fanstel.com/bluenor-summaries">Fanstel</a> and more..</li>
<li>Support for more development environments. Currently Pyrinas supports Mac <em>only</em>.</li>
</ul>
<h3 id="heading-star-and-watch">Star and Watch!</h3>
<p>This is only the tip of the iceberg! Stay tuned for more updates and make you you star and watch <a target="_blank" href="https://github.com/pyrinas-iot/pyrinas-os">the repository</a>.</p>
<p>Or, better yet, looking to help out? Contributions are welcome!</p>
<p><strong>You can read other articles on my blog, <a target="_blank" href="https://www.jaredwolff.com/meet-pyrinas-a-new-way-to-use-your-xenon/">jaredwolff.com</a></strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to land your dream embedded software job ]]>
                </title>
                <description>
                    <![CDATA[ By Rohan Dasika Guides on preparing for software interviews are aplenty. Embedded software interviews are somewhat similar, but it’s still a different game you have to play. There is some helpful material on the Internet and some content for software... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-landing-your-embedded-software-dream-job-4ff9674bf1c4/</link>
                <guid isPermaLink="false">66c353075ced6d98e4bd3353</guid>
                
                    <category>
                        <![CDATA[ careers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Computer Science ]]>
                    </category>
                
                    <category>
                        <![CDATA[ embedded systems ]]>
                    </category>
                
                    <category>
                        <![CDATA[ interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Sun, 26 Nov 2017 16:39:23 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*doISq7nDJgKnuvp9UjlALA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Rohan Dasika</p>
<p>Guides on preparing for software interviews are aplenty. Embedded software interviews are somewhat similar, but it’s still a different game you have to play.</p>
<p>There is some helpful material on the Internet and some content for software interview prep does carry over. But in general, I wasn’t able to find a comprehensive guide to get me started.</p>
<p>Recruiting and doing interview prep for embedded software over the last 4 months taught me a great deal about how to approach the process. I recently received offers from a few big tech companies. In this post, I’ll be sharing some insights I gained along the way.</p>
<p>I’ll be dividing this post into a couple sections, so feel free to jump around! There are already tons of content on smart ways to recruit, so I’m not going to go into how to get interviews here.</p>
<ul>
<li>Content to prepare and review</li>
<li>The interview itself!</li>
</ul>
<h3 id="heading-picking-a-language">Picking a language</h3>
<p>As a standard, embedded software development is mostly done in C, although C++ is becoming more popular recently. If you’ve taken any computer architecture or embedded systems courses, you’ve probably used one or the other. Make sure you know which language the company you’re interviewing with uses. If you’re more familiar with C++ but they use C, be upfront about that — if you know one, you’ll be able to switch between the two fairly easily.</p>
<p>Since I’m most familiar with C++, companies have allowed me to write code in C++. If necessary, they helped me transition that code to C. For the most part, it doesn’t really make a difference. This is unless you’re working with some specific C++ standard library functions and containers.</p>
<p>Verilog is used mostly for FPGA development. Python is used a fair amount to communicate between the user and the embedded system they’re working on. Chances are you won’t be asked any questions on those.</p>
<p>Same goes with assembly language (thankfully!! ?).</p>
<h3 id="heading-the-basics-are-the-basics">The basics are the basics</h3>
<p>I began the journey in a similar fashion to most computer science students — by reviewing my data structures and algorithms. But soon, I realized that content for embedded software splits off at a certain point. It’s <em>much</em> more focused on computer architecture, operating systems, and some hardware fundamentals than higher level data structures like trees or sorting algorithms.</p>
<p>You deal with lower level code and hardware in embedded software roles. But from a programming standpoint, your data structures and algorithms are still <em>highly</em> relevant. Similar to software interviews, there are tons of resources to help you prepare for the basics! Doing a few problems from each section in <a target="_blank" href="https://www.amazon.com/Cracking-Coding-Interview-Programming-Questions/dp/098478280X">Cracking the Coding Interview</a> was a good starting point.</p>
<p>Quickly after that, I preferred to use <a target="_blank" href="https://leetcode.com/">LeetCode</a> due to the capability to run and test instantly. LeetCode is an incredible platform with a great community. In my opinion, has questions <em>most similar</em> to ones you’ll encounter in interviews. It saves all your submitted solutions and calculates runtimes as well. Do most of the “Easy” and a decent portion of the “Medium” questions and you should be okay :)</p>
<p>Additionally, <a target="_blank" href="http://www.geeksforgeeks.org/">Geeks For Geeks</a> is a great resource with very thorough explanations for hundreds of problems.</p>
<h4 id="heading-carry-over-from-software-interviews">Carry-over from Software Interviews</h4>
<p>The following few topics are very similar to the concepts from software interviews, and these are tested heavily, so make sure you know them well!</p>
<ul>
<li>Algorithmic complexity (both time and memory)</li>
<li>Pointers</li>
<li>Arrays</li>
<li>Linked Lists</li>
<li>Strings (and C-strings)</li>
<li>Stacks &amp; Queues</li>
</ul>
<p>The following topics aren’t really tested, but be familiar with them conceptually. Know how they work, their complexities, and how to solve them at a basic level.</p>
<ul>
<li>Recursion</li>
<li>Trees</li>
<li>Heaps</li>
<li>Hashing</li>
<li>Sorting</li>
</ul>
<h3 id="heading-beyond-the-software">Beyond the Software</h3>
<p>This is where the real embedded stuff starts!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/H-cg252SKkDHBoTxRqvVExi2Re4dWEbJFJQz" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-bit-manipulation">Bit manipulation!!</h4>
<p>Know this like the back of your hand.</p>
<p>It’s possibly the <strong>most important</strong> topic of your interviews. On this specific topic, do <em>all</em> the questions on LeetCode.</p>
<ul>
<li>Know how negative numbers are represented in binary</li>
<li>Know the differences between one’s and two’s complements</li>
<li>Be able to convert between binary, decimal, and hexadecimal</li>
<li>The XOR operation is <em>powerful</em>. Know what all it can do.</li>
</ul>
<p><a target="_blank" href="https://discuss.leetcode.com/topic/50315/a-summary-how-to-use-bit-manipulation-to-solve-problems-easily-and-efficiently">Here’s a great resource</a> I used for helpful tips and tricks</p>
<h3 id="heading-computer-architecture">Computer architecture</h3>
<p>A close second to bit manipulation.</p>
<p>You probably won’t be asked to implement any of these, but you definitely will be asked about how things work underneath the hood. Some topics to read up on include…</p>
<ul>
<li>Important registers and how they work</li>
<li>Difference between caller-save and callee-save</li>
<li>How interrupts work</li>
<li>A basic understanding of instruction pipelines</li>
<li>Caches, TLBs, and how virtual memory is implemented</li>
<li>Various types of memory (ROM vs RAM, DDR, EEPROM, Flash, etc)</li>
<li>Memory padding (instructs &amp; classes)</li>
<li>What happens when you boot a system</li>
</ul>
<h4 id="heading-operating-systems">Operating Systems</h4>
<p>Depending on the company and the role, operating systems can be a <em>very</em> important topic. You won’t be asked to implement any of these, but know how things work at a conceptual level!</p>
<ul>
<li>Process vs. Thread</li>
<li>How multi-threading works</li>
<li>Real-Time Operating Systems vs traditional OS</li>
<li>Task scheduling (FIFO, Round Robin, Priority-based)</li>
<li>How semaphores &amp; mutexes protect data</li>
<li>Priority inversion, priority inheritance, spinlocks, and deadlocks</li>
<li>What makes a function ‘reentrant’?</li>
<li>Critical sections</li>
<li>Priority levels in microcontrollers (EL0 — EL3)</li>
</ul>
<h4 id="heading-communication-protocols">Communication Protocols</h4>
<p>Know the benefits and tradeoffs of using the following protocols:</p>
<ul>
<li>UART</li>
<li>SPI</li>
<li>I2C</li>
</ul>
<p>Based on your previous experiences, the role you’re applying for, and the company, you may be asked about others as well. Typically, companies don’t expect you to know about these specific protocols and will train you on the job. But having a basic understanding can always help wow the interviewer!</p>
<ul>
<li><strong>Automotive:</strong> CAN, LIN</li>
<li><strong>Wireless:</strong> 3G, 4G LTE, basics of 5G, 802.11(Wifi), Bluetooth</li>
<li><strong>Networking:</strong> HTTP, TCP/UDP, IP, 802.11(Wifi), Ethernet</li>
</ul>
<h4 id="heading-hardware-fundamentals">Hardware Fundamentals</h4>
<p>Hardware isn’t tested a whole lot for embedded software, but depending on the role, your level of interaction may vary. Check the job descriptions and talk to people who work there for a better idea!</p>
<ul>
<li>Watchdog timers</li>
<li>Timers in general</li>
<li>Details about <em>any</em> peripherals you may have used in your projects (accelerometers, any sensors, LiDAR, motors, etc)</li>
</ul>
<h3 id="heading-practicing">Practicing</h3>
<p><strong>Practice is key</strong> — there’s really no way around that. Make sure to dedicate <em>at least</em> 2 hours a day for interview prep, not including applying for jobs and reaching out to recruiters.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/GT7z8aGsV2fxl4P2D8Vqa7vVGBWppn5wCXMQ" alt="Image" width="600" height="400" loading="lazy"></p>
<p>While practicing on LeetCode, comment your code. Explain your algorithm and the runtime complexities. Every day before you begin coding, review the problems you worked on the previous day. At the end of the week, review every problem you worked on that week. This’ll help you remember the algorithms better and bit by bit, you’ll become a pro at pattern matching.</p>
<p>Embedded interviews tend to be more conceptual than your typical software interviews, due to the nature of some of the topics tested. For these areas, I maintained a Google doc of all the questions that I might be asked, but also all their answers. I also included a link to where I can read more information to be helpful. It helped me stay organized and review faster.</p>
<blockquote>
<p>A lot of people have asked me for the link to the Google doc, so here it is: <a target="_blank" href="https://docs.google.com/document/d/18HMyd-lFu1hWiixFLS2Pc7-SgyzDDqitzXbfAnUVeBE/edit?usp=sharing">Embedded Interview Questions</a></p>
</blockquote>
<h3 id="heading-at-the-interview">At the interview</h3>
<p>You made it — great job!</p>
<p>Take a deep breath. It’s time to put all your hard work to good use. Usually, you and the interviewer will go through a few conceptual questions. You will also discuss your previous experiences and projects. Make sure you’re able to answer questions about your contributions and various challenges you faced in detail.</p>
<p>If this is an interview that’s happening remotely, you’ll probably be asked to code in a shared document. Have some paper ready to jot down important points and diagrams. If it helps, you can also have some notes that you can refer to during the interview. I always keep a sheet of basic algorithmic complexities handy.</p>
<p>Check the timezones. Double check them. Don’t forget to use headphones. Be in a quiet environment so there are no disturbances. Communication issues are only going to make the interview more difficult.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/tz9ML84FNkxqAsD8TFCW47fa9z72LsPIAVqm" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-when-you-get-the-question">When you get the question</h4>
<p>Never start coding right away. As important as arriving at the right solution is, the interviewer is really looking at your approach.</p>
<p>Take a second and repeat the question back to the interviewer, just to make sure you both are on the same page. And if there are any misunderstandings, the interviewer can always repeat and clarify any doubts.</p>
<p>Next, understand the scope of the problem</p>
<ul>
<li>How big is the input?</li>
<li>Is it sorted?</li>
<li>Is there a certain time or memory complexity that you should meet?</li>
<li>Are there duplicates? Negative values? Empty values?</li>
<li>Do you have to perform error checking?</li>
</ul>
<p>Then, walk through your algorithm. Start off with the most basic, brute-force approach. It can be super inefficient and mention that you’re using this as a starting point. Explain the time and memory complexities and why it’s a poor solution.</p>
<p>From there, it’s time to optimize. Generally look for places where you might be storing unnecessary amounts of data or repeating sections of code. For embedded applications, memory is important! Instead of using an array or vector, consider using a bitset. If you’re just dealing with values between 0 and 31, toggle bits in an integer! This is where bit manipulation comes in handy.</p>
<p>It’s important to think out loud while you’re brainstorming. If you’re stuck or heading in the wrong direction, the interviewer can help bring you back on track. Once you’ve both agreed on a solution, then it’s finally time to start coding.</p>
<h4 id="heading-writing-code">Writing code</h4>
<p>Reading others’ code isn’t always the most enjoyable thing. Make the job a little easier for your interviewer by using good style. That doesn’t mean that you have to put down <em>every</em> semicolon or bracket, but indent well and use meaningful variable names. Try to write neatly and use the whiteboard space well.</p>
<p>While you’re writing code, keep checking against the algorithm you’ve devised. Similar to the brainstorming phase, <em>think out loud</em>. Provide verbal comments to your code. At each step, explain what you’re checking, what you hope to achieve, and any design decisions you make.</p>
<p>After you’re done writing code, don’t say you’re done yet. Take a step back and analyze your code from a high-level perspective. Check the inputs, the outputs, and your logic for any bugs. Be sure to spot any off-by-one errors! Then, walk through the code with a few test cases. If there are any issues that you see, review and rework your code as necessary.</p>
<p>Depending on the interview, you might have one big question or a couple smaller ones. But once the interviewer has agreed with your solution, there is a chance he or she might extend it different parameters. You probably won’t have to rework the code, but you might have to discuss how to change parts of your approach. Like I mentioned before, memory is important in embedded applications. So a common follow-up question is usually about further memory optimization.</p>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>The nature of embedded software interviews is heavily dependent on the company and the work they prioritize. Companies working on a specific communication protocol will look for different things than a company developing a real-time operating system or a company working on an IoT product.</p>
<p>I wanted to use this article as a place to share some common themes in the interviews I’ve had, but this is by no means comprehensive. This is intended to be used as a starting point. But please reach out to current employees and check <a target="_blank" href="https://www.glassdoor.com/index.htm">Glassdoor</a> for more company-specific interview tips.</p>
<p>Interviews are tough, but preparing well and working hard now can help you land a job you love :)</p>
<p>I hope this article helped, and wish you the best of luck!</p>
<p>If you enjoyed this article, please don’t forget to leave a ?. You can also follow me on T<a target="_blank" href="https://twitter.com/rohandasika">witter</a> or Q<a target="_blank" href="https://www.quora.com/profile/Rohan-Dasika">uora:</a>)</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
