<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shilleh</title>
    <description>The latest articles on DEV Community by Shilleh (@shilleh).</description>
    <link>https://dev.to/shilleh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1045503%2Ffc861dbf-7606-4dba-8f85-387ca9a406a8.JPG</url>
      <title>DEV Community: Shilleh</title>
      <link>https://dev.to/shilleh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shilleh"/>
    <language>en</language>
    <item>
      <title>Maker Component Cabinet Resistor Kits: Find Parts Fast</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Wed, 22 Jul 2026 15:26:08 +0000</pubDate>
      <link>https://dev.to/shilleh/maker-component-cabinet-resistor-kits-find-parts-fast-29ii</link>
      <guid>https://dev.to/shilleh/maker-component-cabinet-resistor-kits-find-parts-fast-29ii</guid>
      <description>&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Maker component cabinet setup:&lt;/strong&gt; Organize resistor, capacitor, and transistor kits (1900+ total parts) into a labeled drawer cabinet so you can find the right value fast and stop buying duplicates.&lt;/p&gt;

&lt;p&gt;This guide focuses on a repeatable system: drawers labeled for quick scanning, bags inside for exact values, and a small “everyday” drawer stocked with the parts you use constantly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 2 to 4 hours (plus optional label printing time)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; A scalable component storage cabinet layout for resistors, capacitors, transistors, and modules&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-120pcs-multicolored-dupont-wire" rel="noopener noreferrer"&gt;120-Pack DuPont Wire Kit&lt;/a&gt; - common “grab-and-go” wiring set to keep in an everyday drawer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-830-point-breadboard-for-arduino-raspberry-pi-esp32-and-other-microcontrollers" rel="noopener noreferrer"&gt;830-Point Breadboard&lt;/a&gt; - useful to store with core prototyping items so new builds start faster&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-20pcs-1x40pin-female-header-2-54mm-header-pins" rel="noopener noreferrer"&gt;20-Pack 1x40 Female Header Pins&lt;/a&gt; - prototyping staple to sort and restock regularly&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-10pcs-1x40pin-straight-single-row-pin-header-2-54mm-breakable" rel="noopener noreferrer"&gt;10-Pack 1x40 Male Pin Header&lt;/a&gt; - keep accessible for quick module and board connections&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/50cm-alligator-clips-test-leads-double-ended-electrical-jumper-wire" rel="noopener noreferrer"&gt;Alligator Clip Test Leads (10pcs)&lt;/a&gt; - handy test leads to store with your everyday tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Multi-drawer plastic storage cabinet (24- to 60-drawer, plastic, about $20 to $50)&lt;/li&gt;
&lt;li&gt;Small zip-top antistatic bags for ICs and transistors&lt;/li&gt;
&lt;li&gt;A label printer (Brother P-touch / DYMO LetraTag) or a Sharpie plus masking tape&lt;/li&gt;
&lt;li&gt;4-pocket card sleeves for IC datasheet printouts (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: The drawer counts below assume common kits like an 820-piece resistor assortment, 600-piece ceramic capacitor assortment, 200-piece electrolytic assortment, and a 300-piece transistor assortment. Adjust drawer allocation based on what you actually reach for most.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Use frequency-of-use indexing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Make the parts you use the most the fastest to reach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Avoid organizing only by component type (all resistors in one region, all capacitors in another). Instead, arrange drawers by how often you grab the parts. Put everyday values in the top row and long-tail parts in lower rows.&lt;/p&gt;

&lt;p&gt;Example everyday parts: 220Ω, 1 kΩ, 10 kΩ, 100 kΩ, 100 nF, 10 µF, BC547, BC557, and common LED colors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbms8h7oskill44cbuxho.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbms8h7oskill44cbuxho.jpg" alt="Organize drawers by how often you reach for the part." width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can open the most-used drawers first and find common parts quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Lay out an 820-piece resistor kit by decades
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Store 41 resistor values so you always know which drawer to open first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use one drawer per E12 decade (10s, 100s, 1k, 10k, 100k, 1M). Inside each drawer, keep small bags labeled with the exact value (for example “10k”). Label the drawer front with the range and optionally list the values inside.&lt;/p&gt;

&lt;p&gt;Total: 6 drawers for 41 resistor values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can jump straight to the correct decade drawer and then the exact bag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Lay out capacitor kits by type and range
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Keep ceramic and electrolytic capacitors easy to scan and restock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Split drawers by capacitor type and commonly used ranges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ceramic disc / MLCC small values (1pF to 100nF):&lt;/strong&gt; 2 drawers by decade&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ceramic disc large values (470nF to 10µF):&lt;/strong&gt; 1 drawer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electrolytics, low voltage (10µF to 100µF at 16V):&lt;/strong&gt; 1 drawer with separate bags per value&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electrolytics, high capacitance (220µF to 1000µF):&lt;/strong&gt; 1 drawer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electrolytics, high voltage (35V+):&lt;/strong&gt; 1 drawer for less frequent use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Capacitors are grouped the way you choose them in real builds, not buried in a mixed assortment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Sort a 300-piece transistor kit by part number and family
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Prevent transistor mix-ups and reduce pinout lookups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Put each tube or group into a small zip-top antistatic bag labeled with the part number (BC547, 2N2222, 2N3904, TIP120, and similar). Sort labeled bags by family (NPN small-signal, PNP small-signal, NPN power, MOSFET) across one or two drawers.&lt;/p&gt;

&lt;p&gt;Add a small printed datasheet card with each bag (pinout, β, VCE max, IC max) so you do not have to re-search pinouts repeatedly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7h1p89ufa9tdowowzip.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7h1p89ufa9tdowowzip.jpg" alt="Label by part number, then group by transistor family." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can grab the correct transistor quickly and verify the pinout without slowing down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Create an everyday drawer at eye level
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Make 90% of builds start from a single drawer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Stock one drawer with the parts you reach for constantly, such as common resistors (1 kΩ, 10 kΩ, 220Ω), 100 nF and 10 µF capacitors, a few BC547 and BC557 transistors, common LEDs, push-buttons, 1N4148 and 1N4007 diodes, and some hookup wire (for example 22 AWG). Refill this drawer from your main stash on a regular schedule.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Most prototypes can start without digging through multiple drawers and bags.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Store modules and breakouts by project category
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Keep larger items accessible even when they do not fit small drawers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use one drawer or shoebox per category such as displays, sensors, wireless, motor drivers, USB-to-serial, and power management. Do not sort by alphabet; sort so you can grab a category that matches the project you are building.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; When you start a project type (for example a weather station), the related hardware is already grouped together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Apply a three-location labeling system
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Make drawers readable from the front, from above, and when opened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use three labels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Front label:&lt;/strong&gt; a quick description of what is inside (for example “Caps 10nF to 100nF”)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top label:&lt;/strong&gt; useful for stackable drawers so you can scan from above&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inside label:&lt;/strong&gt; a small note listing exact values for the bags inside&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fye3fsjfpx791c0z9wu6b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fye3fsjfpx791c0z9wu6b.jpg" alt="Use consistent labeling so parts stay findable over time." width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can locate a part quickly, even months later, without re-learning your own system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8 - Add a tools drawer for frequent build items
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Keep build tools from scattering across your workspace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Dedicate one drawer to common build tools and accessories such as jumper wires, a mini screwdriver, USB-C and USB-A cables, multimeter probes, alligator clips, and an anti-static wristband.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You spend less time hunting for tools during prototyping.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9 - Schedule a monthly restock day
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Keep the system accurate as your habits change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Once a month, check the everyday drawer. Refill anything below about 5 pieces from your main stash. If something sat untouched for 30 days, move it back to the main bins so the everyday drawer reflects what you use right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Your fastest-access drawer stays relevant and your cabinet stays organized long term.&lt;/p&gt;

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

&lt;p&gt;After one weekend of setup, this component cabinet system makes resistor, capacitor, and transistor values easy to find, helps you finish builds faster, and reduces duplicate purchases. By indexing drawers by frequency of use and keeping an everyday drawer stocked, your bench stays ready for the next project.&lt;/p&gt;

&lt;p&gt;This approach was inspired by &lt;a href="https://www.instructables.com/Resistor-Organizer-and-Storage/" rel="noopener noreferrer"&gt;"Resistor Organizer and Storage" on Instructables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this setup? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing an organization system for your lab or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>electronics</category>
      <category>diy</category>
      <category>maker</category>
      <category>hardware</category>
    </item>
    <item>
      <title>ESP32 SD Card: Local CSV Data Logging Setup</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Wed, 22 Jul 2026 15:14:41 +0000</pubDate>
      <link>https://dev.to/shilleh/esp32-sd-card-local-csv-data-logging-setup-192p</link>
      <guid>https://dev.to/shilleh/esp32-sd-card-local-csv-data-logging-setup-192p</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8v9uzdudped98mnftcos.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8v9uzdudped98mnftcos.jpg" width="800" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ESP32 + SD card data logger:&lt;/strong&gt; Build an ESP32 data logger that writes sensor readings to a microSD card as CSV, optionally adding DS3231 RTC timestamps so your logs stay readable even without internet.&lt;/p&gt;

&lt;p&gt;Cloud logging is great until WiFi drops, your MQTT broker restarts, or you deploy somewhere with no connectivity. An ESP32 with a microSD card gives you reliable local logging that keeps writing through network hiccups.&lt;/p&gt;

&lt;p&gt;This guide wires an SD card module to an ESP32, writes CSV data, shows how to add timestamps using a DS3231 RTC, and covers file size and speed tradeoffs for higher-rate logging.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 30 to 60 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner to Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; An ESP32 that appends timestamped sensor data into CSV files on a microSD card.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/esp32-wroom-dev-board-cp2102-usb-c-presoldered" rel="noopener noreferrer"&gt;ESP-WROOM-32 (USB-C)&lt;/a&gt; - main ESP32 dev board used for SPI SD logging.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/xiao-seeed-esp32s3-pre-soldered-with-usb-c-cable" rel="noopener noreferrer"&gt;XIAO ESP32-S3&lt;/a&gt; - PSRAM-heavy alternative for buffered logging.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-dht22-with-cables" rel="noopener noreferrer"&gt;DHT22 Sensor&lt;/a&gt; - example temperature and humidity data source in the code.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/bme280-pre-soldered-atmospheric-temperature-pressure-and-humidity-sensor" rel="noopener noreferrer"&gt;BME280&lt;/a&gt; - higher precision environmental sensor alternative.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/ds18b20-waterproof-digital-temp-sensor-probe-1m-for-arduino-pi" rel="noopener noreferrer"&gt;DS18B20 Waterproof Probe&lt;/a&gt; - outdoor-friendly temperature sensor option.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;MicroSD card module (SPI interface, 5V-tolerant preferred).&lt;/li&gt;
&lt;li&gt;microSD card (up to 32 GB, FAT32 format; basic Class 10 is fine).&lt;/li&gt;
&lt;li&gt;DS3231 RTC module (optional but recommended for real timestamps).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: Many SD modules are not truly 3.3V-safe on all pins. Confirm whether your SD module expects 3.3V or can accept 5V on VCC, and always share GND with the ESP32.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Decide when local logging beats cloud logging
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand why writing to microSD is a strong default for sensor deployments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use local CSV logs when you need data to survive outages and be easy to retrieve later.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Survives WiFi outages:&lt;/strong&gt; data keeps writing regardless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher sample rate:&lt;/strong&gt; SD logging can handle much higher rates than typical cloud endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No cloud dependency:&lt;/strong&gt; works where there is no internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No monthly fee:&lt;/strong&gt; large microSD cards can store extremely long time series at low sample rates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data ownership:&lt;/strong&gt; remove the card and open the CSV locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You have a clear reason to log locally, either as your primary method or as a fallback when cloud connectivity fails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Wire the SD card module and optional DS3231 RTC
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Connect the SD card module via SPI and (optionally) connect the DS3231 via I2C for timestamps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Wire the SD module to the ESP32 SPI pins, then wire DS3231 to ESP32 I2C pins if you want real date and time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiring map:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SD Card Module    ESP32
VCC               --&amp;gt; 5V (some modules need 3.3V, check yours)
GND               --&amp;gt; GND
CS                --&amp;gt; GPIO5
SCK               --&amp;gt; GPIO18
MOSI              --&amp;gt; GPIO23
MISO              --&amp;gt; GPIO19

DS3231 RTC (optional but recommended)
VCC               --&amp;gt; 3.3V
GND               --&amp;gt; GND
SDA               --&amp;gt; GPIO21
SCL               --&amp;gt; GPIO22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Your ESP32 can talk to the SD card over SPI. If installed, the DS3231 can provide a stable time source over I2C.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Upload a basic CSV logger sketch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Create a CSV file on the SD card, write a header once, and append sensor rows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the SD library to initialize the card, create &lt;code&gt;/log.csv&lt;/code&gt; if it does not exist, then append rows on a fixed interval.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include &amp;lt;SPI.h&amp;gt;
#include &amp;lt;SD.h&amp;gt;
#include &amp;lt;DHT.h&amp;gt;

DHT dht(4, DHT22);
const int SD_CS = 5;
const char* LOG_FILE = "/log.csv";

void setup() {
  Serial.begin(115200);
  dht.begin();
  if (!SD.begin(SD_CS)) {
    Serial.println("SD failed");
    while (1);
  }
  // Write header if file doesn't exist
  if (!SD.exists(LOG_FILE)) {
    File f = SD.open(LOG_FILE, FILE_WRITE);
    f.println("millis,temp_c,humidity_pct");
    f.close();
  }
}

void loop() {
  float t = dht.readTemperature();
  float h = dht.readHumidity();
  File f = SD.open(LOG_FILE, FILE_APPEND);
  f.print(millis());
  f.print(","); f.print(t, 1);
  f.print(","); f.println(h, 1);
  f.close();
  delay(5000);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The SD card contains &lt;code&gt;log.csv&lt;/code&gt; with a header and new rows appended every 5 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Add real timestamps using a DS3231
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Replace relative time (&lt;code&gt;millis()&lt;/code&gt;) with human-readable timestamps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Read the current time from the DS3231 and prepend it to each CSV row.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include &amp;lt;RTClib.h&amp;gt;
RTC_DS3231 rtc;

void logRow(float t, float h) {
  DateTime now = rtc.now();
  char buf[24];
  sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
          now.year(), now.month(), now.day(),
          now.hour(), now.minute(), now.second());
  File f = SD.open(LOG_FILE, FILE_APPEND);
  f.print(buf); f.print(",");
  f.print(t, 1); f.print(",");
  f.println(h, 1);
  f.close();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Each CSV row contains a real timestamp that is readable in Excel or pandas without conversion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Rotate daily files on boot
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Avoid extremely large CSV files that can become slow to open or process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Generate a filename based on the current date and write logs into one file per day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;char filename[24];
DateTime now = rtc.now();
sprintf(filename, "/log_%04d%02d%02d.csv",
        now.year(), now.month(), now.day());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Your logger writes to files like &lt;code&gt;/log_20260710.csv&lt;/code&gt;, keeping each file smaller and easier to manage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Increase throughput for high-rate logging
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Improve performance when logging at 100+ samples per second.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Keep the file open and flush periodically instead of opening and closing the file every sample.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;File logFile;
void setup() {
  // ...
  logFile = SD.open("/fast.csv", FILE_APPEND);
}
void loop() {
  logFile.print(micros());
  logFile.print(",");
  logFile.println(analogRead(A0));
  static int count = 0;
  if (++count % 100 == 0) logFile.flush();   // flush every 100 rows
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Higher sustained logging rates because SD writes are block-based and frequent flushes waste space and time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Apply the logger to real deployments
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Map this build to practical sensor logging scenarios.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the same SD CSV approach with the sensor you care about, then retrieve the card periodically for analysis.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Weather station in a remote field&lt;/strong&gt; - no WiFi needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vehicle GPS and accelerometer black box&lt;/strong&gt; - second-by-second driving data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beehive weight and temperature&lt;/strong&gt; - log long term, then retrieve the card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fermentation temperature curve&lt;/strong&gt; - brewing, kombucha, sourdough starter tracking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fridge or freezer defrost cycle profiling&lt;/strong&gt; - identify a failing seal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bird box camera trigger log&lt;/strong&gt; - timestamp motion events.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can deploy sensors anywhere and still get complete time series data, even when connectivity is unreliable or unavailable.&lt;/p&gt;

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

&lt;p&gt;An ESP32 with an SD card module and optional DS3231 RTC gives you a durable CSV data logger that keeps working when WiFi fails. You can remove the card and analyze the file in Excel or pandas with no cloud dependencies.&lt;/p&gt;

&lt;p&gt;Inspiration credit: &lt;a href="https://www.instructables.com/SD-Card-Datalogging-with-the-DHT22-Temp-Humidity-S/" rel="noopener noreferrer"&gt;SD Card Datalogging With the DHT22 Temp Humidity Sensor on Instructables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>esp32</category>
      <category>arduino</category>
      <category>iot</category>
      <category>datalogger</category>
    </item>
    <item>
      <title>Arduino Nano EM4100 RFID: EEPROM multi-card door lock</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Mon, 20 Jul 2026 15:27:50 +0000</pubDate>
      <link>https://dev.to/shilleh/arduino-nano-em4100-rfid-eeprom-multi-card-door-lock-3d0a</link>
      <guid>https://dev.to/shilleh/arduino-nano-em4100-rfid-eeprom-multi-card-door-lock-3d0a</guid>
      <description>&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Arduino Nano + EM4100 125 kHz RFID reader:&lt;/strong&gt; In this build, you will read EM4100 card IDs, store authorized cards in EEPROM, and unlock an electric strike using a relay for a simple multi-card RFID door lock.&lt;/p&gt;

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

&lt;p&gt;Two flavors of RFID dominate maker projects: &lt;strong&gt;13.56 MHz&lt;/strong&gt; (the RC522, MIFARE) and &lt;strong&gt;125 kHz&lt;/strong&gt; (the EM4100, HT4168). The 125 kHz family is what many commercial proximity-card door locks use. The EM4100 standard is open and easy to read with an Arduino and a low-cost 125 kHz reader module. For under $15 of parts, you can build an RFID door lock with multi-card support and Wiegand-style access logging.&lt;/p&gt;

&lt;p&gt;This guide walks through the wiring, reads cards, stores authorized IDs in EEPROM, drives an electric strike lock through a relay, and covers upgrade ideas like time-of-day rules, master/admin cards, and WiFi-logged entry events.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 45 to 90 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; An Arduino Nano-based RFID access controller that unlocks a strike for authorized EM4100 card IDs stored in EEPROM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-nano-v3-presoldered-ch340g-atmega328p" rel="noopener noreferrer"&gt;Arduino Nano V3.0&lt;/a&gt; - reads the RFID module, checks EEPROM, and drives the relay.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/1-channel-12v-relay-module" rel="noopener noreferrer"&gt;1-Channel 12V Relay Module&lt;/a&gt; - switches power to the electric strike.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/4-bits-tm1637-red-led-display-module-clock" rel="noopener noreferrer"&gt;TM1637 4-Bit Display&lt;/a&gt; - optional, for showing card ID.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/rc522-rfid-reader-writer-module-13-56mhz-spi-kit-for-arduino" rel="noopener noreferrer"&gt;RC522 RFID Reader (13.56 MHz)&lt;/a&gt; - optional alternative if you want to use MIFARE cards instead.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/esp8266-d1-mini-v3-4mb-dev-board-presoldered" rel="noopener noreferrer"&gt;ESP8266 D1 Mini&lt;/a&gt; - optional upgrade for WiFi logging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;125 kHz RFID reader module (RDM6300 or similar) - outputs UART serial when a card is read.&lt;/li&gt;
&lt;li&gt;EM4100 RFID cards or HT4168 keytag fobs.&lt;/li&gt;
&lt;li&gt;12 V electric strike lock or solenoid.&lt;/li&gt;
&lt;li&gt;12 V DC power supply rated for the lock.&lt;/li&gt;
&lt;li&gt;Jumper wires, breadboard or perfboard, and basic tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: The RDM6300 typically uses 5V TTL serial. The relay module in this guide is treated as active-low (IN goes LOW to energize). The lock power (12V) must remain isolated from the Arduino 5V side except through the relay contacts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Choose 125 kHz vs 13.56 MHz for your use case
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Pick the RFID type that matches your cards and security needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; If you only need a yes/no check against a list of authorized cards, 125 kHz EM4100 is the simplest and cheapest approach. If you need read/write card storage or more advanced credentials, consider 13.56 MHz MIFARE options instead.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;125 kHz EM4100:&lt;/strong&gt; read-only ID (40 bits / 10 hex chars), passive cards last decades, about 3 to 5 cm read range. Best for "is this a known card?" access control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;13.56 MHz MIFARE:&lt;/strong&gt; read/write, often around 1 KB user data on the card, about 3 to 5 cm range. Use when you want to store extra info on the card (balance, access level).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You know whether you are building for EM4100 125 kHz IDs (this guide) or a 13.56 MHz MIFARE workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Wire the reader and the relay
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Connect the RDM6300 (or similar) to the Arduino Nano, and wire the relay to switch the 12V lock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the wiring map below. The reader TX goes to the Arduino software-serial RX pin. The relay IN pin is driven by the Arduino and the lock power is routed through COM and NO on the relay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiring map:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RDM6300 / 125kHz Reader     Arduino Nano
VCC                      -&amp;gt; 5V
GND                      -&amp;gt; GND
TX                       -&amp;gt; D2  (RX in SoftwareSerial)
ANT1, ANT2               -&amp;gt; the included coil antenna (already wired)

Relay Module                Arduino Nano
VCC                      -&amp;gt; 5V
GND                      -&amp;gt; GND
IN                       -&amp;gt; D7 (active LOW)
Lock(+)                  -&amp;gt; Relay NO
Lock(-)                  -&amp;gt; 12V GND
+12V                     -&amp;gt; Relay COM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The Arduino can receive card data from the reader, and it can energize the relay to apply 12V to the strike lock.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Understand the card data format and test a read
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Verify you can read a 10-character EM4100 ID from the serial stream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; The RDM6300 streams a 14-byte serial frame whenever a card is in range. Use the format below and upload the test sketch to print the ID to the Serial Monitor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frame format:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x02  [10 hex chars of card ID]  [2 hex chars of checksum]  0x03
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;SoftwareSerial.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="n"&gt;SoftwareSerial&lt;/span&gt; &lt;span class="nf"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// RX, TX&lt;/span&gt;

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

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mh"&gt;0x02&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ID: "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// flush&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; When you wave a card, the 10-character ID prints to Serial. Write it down because it is the card's permanent identity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Store and check authorized cards in EEPROM
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Create a simple authorization check that compares a scanned ID to IDs stored in EEPROM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; This example stores up to 10 cards (100 bytes total, 10 bytes per ID) in the Nano's EEPROM. The enrollment flow described here is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Power on with the ENROLL button held.&lt;/li&gt;
&lt;li&gt; Wave a card and write its ID to the next free EEPROM slot.&lt;/li&gt;
&lt;li&gt; Release the button; the card is now authorized.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;EEPROM.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;MAX_CARDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;isAuthorized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MAX_CARDS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;stored&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;stored&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EEPROM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strncmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stored&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can call &lt;code&gt;isAuthorized(id)&lt;/code&gt; and get a true/false result based on what is stored in EEPROM.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Drive the lock relay based on authorization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Unlock for authorized cards and deny access for unknown cards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the loop below to read an ID, check authorization, then unlock by pulling the relay pin LOW for a fixed time. Flash an OK LED for success and a FAIL LED for denial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;LOCK_RELAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;OK_LED&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;FAIL_LED&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;UNLOCK_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mh"&gt;0x02&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="n"&gt;rfid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isAuthorized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCK_RELAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// unlock&lt;/span&gt;
      &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OK_LED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UNLOCK_MS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LOCK_RELAY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// re-lock&lt;/span&gt;
      &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OK_LED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FAIL_LED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FAIL_LED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Authorized cards unlock the door for the configured time; unauthorized cards trigger the failure indicator.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Consider upgrade paths for a more product-like system
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand common feature upgrades you can add after the basic lock works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; If you want to expand the project beyond a basic allow-list door lock, these are common additions:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Master card:&lt;/strong&gt; reserve EEPROM slot 0 as the master so only it can enroll/delete cards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-of-day rules:&lt;/strong&gt; add a DS3231 RTC and reject some cards outside allowed hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WiFi event log:&lt;/strong&gt; swap the Nano for an ESP8266 D1 Mini and POST reads to a Google Sheet or Home Assistant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backup keypad:&lt;/strong&gt; add a 4x4 keypad for a PIN fallback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-tamper:&lt;/strong&gt; add a tilt switch that triggers a buzzer if the unit is moved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time-limited admin cards:&lt;/strong&gt; create visitor cards that auto-expire.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You have a clear roadmap for turning the basic RFID door lock into a more complete access system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Understand the security limitations of EM4100
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Make an informed decision about when EM4100 is appropriate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; EM4100 is not a secure protocol. The card ID is broadcast in plaintext and can be cloned with low-cost tools. For workshops, garages, maker spaces, or low-risk doors, it can be acceptable. For higher security needs, use 13.56 MHz credentials designed for encrypted challenge-response (for example, MIFARE DESFire EV2/EV3 or HID iCLASS).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You understand the tradeoff between cost and security for 125 kHz EM4100 systems.&lt;/p&gt;

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

&lt;p&gt;Using a 125 kHz EM4100 reader with an Arduino Nano and a relay is one of the cheapest ways to build a working RFID door lock with EEPROM-based multi-card access. Once the basic read-check-unlock loop works, you can expand it with admin controls, time rules, and WiFi logging.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Attribution: This guide was inspired by &lt;a href="https://www.instructables.com/id/Open-Sesame-Arduino-RFID-lock-and-automations/" rel="noopener noreferrer"&gt;"Open Sesame! Arduino RFID Lock and Automations" on Instructables&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>rfid</category>
      <category>electronics</category>
      <category>iot</category>
    </item>
    <item>
      <title>XL4015 Buck Converter: Adjustable Bench PSU Build</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Mon, 20 Jul 2026 15:20:54 +0000</pubDate>
      <link>https://dev.to/shilleh/xl4015-buck-converter-adjustable-bench-psu-build-4o67</link>
      <guid>https://dev.to/shilleh/xl4015-buck-converter-adjustable-bench-psu-build-4o67</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmqx8t0bxmuzbm3c7ztbx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmqx8t0bxmuzbm3c7ztbx.jpg" width="800" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;XL4015 buck converter module:&lt;/strong&gt; In this build, you will turn an XL4015 5A CC/CV buck converter (with onboard voltmeter and current display) into an adjustable 1.25V to 36V DIY bench power supply for regulated, current-limited DC output.&lt;/p&gt;

&lt;p&gt;This guide wires the XL4015 into an enclosure with binding posts, walks through constant-voltage (CV) and constant-current (CC) adjustment, and highlights a few practical XL4015 use cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; About 15 to 30 minutes (not including enclosure cutting/printing)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner to Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; A compact bench-style power supply powered by a 12V to 24V DC brick with adjustable voltage and current limiting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-nano-v3-presoldered-ch340g-atmega328p" rel="noopener noreferrer"&gt;Arduino Nano V3.0&lt;/a&gt; - optional if you want to add USB monitoring of voltage/current&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/pdsink-pd-decoy-pd-fast-charging-test-board-5-20v" rel="noopener noreferrer"&gt;PDSink PD Decoy Board&lt;/a&gt; - optional for a USB-C-powered bench PSU (pull 9V/12V/15V/20V and feed the XL4015)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/4-bits-tm1637-red-led-display-module-clock" rel="noopener noreferrer"&gt;TM1637 4-Bit Display&lt;/a&gt; - optional alternative for displaying voltage/current&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-120pcs-multicolored-dupont-wire" rel="noopener noreferrer"&gt;DuPont Wires&lt;/a&gt; - helpful for internal wiring and prototyping add-ons&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;XL4015 5A 75W buck converter module (variant with onboard voltmeter / current meter and CC/CV adjustment)&lt;/li&gt;
&lt;li&gt;24V DC laptop brick or AC-to-DC power supply (rated for 5A+ if you want full output capability)&lt;/li&gt;
&lt;li&gt;Project enclosure with binding-post cutouts (or one you can drill/cut)&lt;/li&gt;
&lt;li&gt;Two banana-plug binding posts (red and black)&lt;/li&gt;
&lt;li&gt;Inline 5A blade fuse on the output (recommended for safety)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: XL4015 boards vary by seller. Confirm your specific module has CC (current limit) adjustment and the same terminal labeling before wiring it into an enclosure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Choose XL4015 for higher current projects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand when the XL4015 is a better choice than smaller buck converters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; If your project needs more than about 2A continuous, the XL4015 is a strong option. Compared with common LM2596 and MP1584 modules, it supports higher continuous current and typically offers better efficiency at higher loads.&lt;/p&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;LM2596&lt;/th&gt;
&lt;th&gt;MP1584&lt;/th&gt;
&lt;th&gt;XL4015&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Max current (continuous)&lt;/td&gt;
&lt;td&gt;2A&lt;/td&gt;
&lt;td&gt;3A&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5A&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max input voltage&lt;/td&gt;
&lt;td&gt;40V&lt;/td&gt;
&lt;td&gt;28V&lt;/td&gt;
&lt;td&gt;38V&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Switching freq&lt;/td&gt;
&lt;td&gt;150kHz&lt;/td&gt;
&lt;td&gt;1.5MHz&lt;/td&gt;
&lt;td&gt;180kHz&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Efficiency (24V to 12V)&lt;/td&gt;
&lt;td&gt;~80%&lt;/td&gt;
&lt;td&gt;~93%&lt;/td&gt;
&lt;td&gt;~96%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Onboard CC/CV?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (some variants)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;$1.50&lt;/td&gt;
&lt;td&gt;$2&lt;/td&gt;
&lt;td&gt;$4 to $6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You know why the XL4015 is commonly used when you need higher current and want CC/CV adjustment in a low-cost module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Identify the XL4015 terminal wiring
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Map each terminal to your input power and output binding posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Most XL4015 boards expose screw terminals for input and output, plus an optional enable (EN) pin on some variants. Wire your DC brick to IN+/IN-, and wire your front-panel binding posts to OUT+/OUT-.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiring map:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IN+  &amp;lt;-- 24V DC from your power brick
IN-  &amp;lt;-- GND from power brick
OUT+ --&amp;gt; red binding post
OUT- --&amp;gt; black binding post
EN   (optional) &amp;lt;-- pull HIGH or to IN+ to enable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You have a clear wiring plan for connecting input power and the bench output terminals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Set CV (constant voltage) output
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Adjust the output voltage to a known target before connecting a load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the CV potentiometer (often labeled V-ADJ) to set the output voltage with no load connected.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt; Power up the input.&lt;/li&gt;
&lt;li&gt; Put a multimeter on OUT+ and OUT-.&lt;/li&gt;
&lt;li&gt; Turn the V-ADJ pot until the meter reads your target voltage (for example 5.0V or 12.0V).&lt;/li&gt;
&lt;li&gt; Confirm the onboard display matches your meter (typically within about 50mV).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The output voltage is stable at your target value and matches your multimeter reading closely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Set CC (constant current) limit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Configure the current limit so the supply can protect loads and support CC/CV behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; On the XL4015 variant with onboard voltmeter/current display, use the second potentiometer (often labeled I-ADJ or CC) to set the current limit.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Short OUT+ to OUT- through your multimeter set to 10A DC current.&lt;/li&gt;
&lt;li&gt; Turn the I-ADJ pot until the meter reads your target current (for example 1.5A).&lt;/li&gt;
&lt;li&gt; Remove the short. The supply will deliver up to that current. Above the limit, voltage will drop to maintain the set current.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The output current clamps at the value you set when the output is shorted through the meter, confirming CC limiting works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Install the module into an enclosure
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Make the XL4015 module bench-friendly and safer to use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Mount the bare PCB in a project box and add proper panel connectors so it behaves like a small bench power supply.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3D-print or buy a project box with a window for the LED display.&lt;/li&gt;
&lt;li&gt;Add front-panel binding posts (red and black).&lt;/li&gt;
&lt;li&gt;Add a panel-mount DC barrel jack for the input.&lt;/li&gt;
&lt;li&gt;Add an inline 5A fuse on the output. The XL4015 has protection features, but a fuse provides hard protection.&lt;/li&gt;
&lt;li&gt;Add a power LED (some kits include this onboard).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The module is mechanically protected and you have reliable, repeatable front-panel connections for input and output.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Apply the XL4015 to other common builds
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand other practical scenarios where CC/CV and 5A capability are useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Consider these additional use cases where an XL4015 is handy to keep in your parts bin.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solar-charged 12V system:&lt;/strong&gt; Use the XL4015 as the buck stage between an MPPT controller and a 12V battery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LED strip dimmer:&lt;/strong&gt; Set the CC limit to control brightness without PWM flicker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Battery charger (1S/2S/3S Li-ion):&lt;/strong&gt; CC plus CV matches lithium charging needs. Calibrate carefully; dedicated chargers are safer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workshop hot-wire foam cutter:&lt;/strong&gt; 5A at 3V to 6V is useful for cutting foam.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;USB-C laptop charger replacement:&lt;/strong&gt; Pair with a PDSink board to draw 20V from USB-C PD and convert to 5V/12V/other outputs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can identify where an adjustable CC/CV buck converter solves real power problems beyond a bench PSU.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Account for heat, ripple, and polarity limits
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Avoid common performance and safety issues when using the XL4015 near its limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Keep these constraints in mind when you design your enclosure and choose loads.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Heat:&lt;/strong&gt; At 5A continuous, the XL4015 can get hot enough to burn. Add a small heatsink or active fan if you draw more than 3A for long periods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output ripple:&lt;/strong&gt; Around 50mV at full load. For sensitive analog circuits (audio, low-noise sensors), add a downstream LC filter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse polarity:&lt;/strong&gt; Many XL4015 boards have no input reverse-polarity protection. Add a Schottky diode in series with IN+ if reverse connection is a concern.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You know what to upgrade (cooling, filtering, protection) when you push the XL4015 toward higher current or lower-noise applications.&lt;/p&gt;

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

&lt;p&gt;The XL4015 buck converter module with an onboard voltmeter/current display is a simple way to build an adjustable bench power supply that supports CC/CV behavior and can deliver up to 5A in the right thermal setup. With a DC brick, enclosure, binding posts, and a fuse, it becomes a reliable regulated DC source for everyday bench work.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>electronics</category>
      <category>xl4015</category>
      <category>powersupply</category>
      <category>diy</category>
    </item>
    <item>
      <title>Arduino Nano + ACS712: Measure AC/DC Current Up to 30A</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Sun, 19 Jul 2026 15:24:17 +0000</pubDate>
      <link>https://dev.to/shilleh/arduino-nano-acs712-measure-acdc-current-up-to-30a-24j</link>
      <guid>https://dev.to/shilleh/arduino-nano-acs712-measure-acdc-current-up-to-30a-24j</guid>
      <description>&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Arduino Nano + ACS712 current sensor:&lt;/strong&gt; In this project, you will wire an ACS712 Hall-effect current sensor module to an Arduino and measure AC or DC current up to 30A using the Arduino ADC.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;ACS712&lt;/strong&gt; measures current by sensing the magnetic field around the current-carrying conductor and outputs a proportional analog voltage centered around &lt;strong&gt;2.5V at 0A&lt;/strong&gt;, which an Arduino can read directly. It is available in &lt;strong&gt;5A / 20A / 30A&lt;/strong&gt; variants, and it is a common choice when you want AC and DC current measurement with galvanic isolation from the measured line.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 30 to 60 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner to Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; An Arduino sketch that reads ACS712 voltage, calibrates the zero offset, and reports DC current or AC RMS current.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-nano-v3-presoldered-ch340g-atmega328p" rel="noopener noreferrer"&gt;Arduino Nano V3.0&lt;/a&gt; - reads the ACS712 analog output via the ADC.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/esp32-wroom-dev-board-cp2102-usb-c-presoldered" rel="noopener noreferrer"&gt;ESP32 WROOM Dev Board&lt;/a&gt; - optional if you want WiFi logging later (the wiring and concept still apply).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-wcs1700-measure-current-70a" rel="noopener noreferrer"&gt;WCS1700 Current Sensor&lt;/a&gt; - a higher-current Hall-effect alternative (up to 70A).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-nano-ina219-measure-voltage-current-power" rel="noopener noreferrer"&gt;INA219 Sensor&lt;/a&gt; - a precise I2C shunt-based alternative for DC current measurement.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-lcd1602-16x2-character-display-module" rel="noopener noreferrer"&gt;LCD1602 Display&lt;/a&gt; - optional for live readouts without the Serial Monitor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ACS712 module (5A, 20A, or 30A variant) - the current sensor module used in this guide.&lt;/li&gt;
&lt;li&gt;DC or AC load (motor, heater, lamp) - something to draw measurable current.&lt;/li&gt;
&lt;li&gt;Multimeter - used to validate readings and help with calibration.&lt;/li&gt;
&lt;li&gt;Jumper wires and a breadboard (optional) - for easier prototyping.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: The ACS712 variant matters. ACS712-05B sensitivity is 0.185 V/A, the 20A variant is 0.100 V/A, and the 30A variant is 0.066 V/A. Also, treat mains/high-current wiring as hazardous: use proper enclosures and fusing, and double-check wiring before powering anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Choose the right current sensor (ACS712 vs INA219 vs WCS1700)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Pick the correct sensor type for your use case (AC vs DC, precision vs current range).&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use these quick differences to choose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ACS712 (5A / 20A / 30A):&lt;/strong&gt; Hall effect, measures AC and DC, analog output, galvanically isolated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WCS1700:&lt;/strong&gt; Hall effect, up to 70A, hole-through design for heavier cables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INA219:&lt;/strong&gt; shunt-based, DC only (or full-wave rectified AC), I2C output, higher precision (down to about mA resolution).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick ACS712 for typical hobby AC/DC monitoring up to 30A. Pick INA219 when you need precision DC current in the mA range. Pick WCS1700 for higher-current battery packs and similar projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You confirm the sensor and variant you are using and note the correct sensitivity constant for your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Wire the ACS712 module to the Arduino
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Connect the sensor's signal pins to the Arduino and place the screw terminals inline with your load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Wire the 3-pin header for power and signal, then route the load current through the ACS712 screw terminals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiring map:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ACS712 module     Arduino Nano
VCC          -&amp;gt;   5V
GND          -&amp;gt;   GND
OUT          -&amp;gt;   A0

Load wiring:
Power source (+) -&amp;gt; ACS712 IN1
ACS712 IN2       -&amp;gt; Load (+)
Power source (-) -&amp;gt; Load (-)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current-carrying screw terminals go inline with your load; the 3-pin header is the analog signal side.&lt;/p&gt;

&lt;p&gt;Safety note: The sensor is designed for galvanic isolation, but wiring mistakes can still create dangerous situations. Use proper fusing and an enclosure for anything above low current levels, and do not let mains wiring touch your Arduino circuitry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The module powers from 5V/GND, and OUT is connected to A0 with the load current routed through IN1/IN2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Read DC current in Arduino
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Convert the analog reading to voltage, then to current using your module's sensitivity and a calibrated zero offset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Start with a basic sketch that prints current to the Serial Monitor. Use the sensitivity constant that matches your ACS712 variant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const int SENSOR = A0;
const float SENSITIVITY_5A = 0.185;   // V per Amp for ACS712-05B
// 20A variant = 0.100, 30A variant = 0.066

float zeroOffset = 2.5;   // will calibrate later

void setup() { Serial.begin(9600); }
void loop() {
  int raw = analogRead(SENSOR);
  float voltage = raw * 5.0 / 1023.0;
  float current = (voltage - zeroOffset) / SENSITIVITY_5A;
  Serial.print(current, 3);
  Serial.println(" A");
  delay(500);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important: If you use a 20A or 30A ACS712, update the sensitivity constant to match your part number, or the current reading will be wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The Serial Monitor prints a current value in amps that changes as your DC load changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Calibrate the zero-point offset (0A level)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Measure the true no-load sensor output voltage so the current reads near zero when no current is flowing.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; With &lt;strong&gt;no current&lt;/strong&gt; through the sensor, the output should be near 2.5V, but real modules and ADCs vary (for example 2.45V or 2.53V). Calibrate like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Disconnect the load side so there is no current through the sensor terminals.&lt;/li&gt;
&lt;li&gt; Print the raw voltage using: &lt;code&gt;voltage = raw * 5.0 / 1023.0;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Take the average over 100 samples.&lt;/li&gt;
&lt;li&gt; Update &lt;code&gt;zeroOffset&lt;/code&gt; to that averaged voltage.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; After calibration, no-load current should read very close to 0A (typically under about 5 mA in this guide's setup).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Read AC current using RMS sampling
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Compute RMS current for AC loads by sampling many points of the waveform and calculating the RMS value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; The ACS712 output for AC is a sine-like waveform centered around the calibrated zero offset. Use an RMS calculation over a time window that covers at least one full mains cycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;float readAcRms(int pin, unsigned long window_ms = 200) {
  unsigned long start = millis();
  double sumsq = 0;
  int n = 0;
  while (millis() - start &amp;lt; window_ms) {
    int raw = analogRead(pin);
    float v = raw * 5.0 / 1023.0 - zeroOffset;
    float i = v / SENSITIVITY_5A;
    sumsq += i * i;
    n++;
  }
  return sqrt(sumsq / n);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample for at least one full mains cycle (20 ms for 50 Hz, 16.7 ms for 60 Hz). A 200 ms window gives 10+ cycles of averaging for smoother readings.&lt;/p&gt;

&lt;p&gt;If readings are noisy, you can average more samples in software. A common hardware approach is adding a 10 µF capacitor between OUT and GND.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Your AC load produces a stable RMS current value that is more meaningful than instantaneous samples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Apply the readings to real projects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand practical ways to use the current measurement in your builds.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the measured current as an input signal for monitoring and automation, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Power monitoring:&lt;/strong&gt; measure load current and log usage over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Motor stall detection:&lt;/strong&gt; a spike in current can indicate a stalled motor; cut power via a relay to protect hardware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Appliance state sensing:&lt;/strong&gt; if current is above a threshold (for example &amp;gt; 500 mA), treat the appliance as "running."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Battery monitoring:&lt;/strong&gt; track DC current into/out of a battery bank to estimate charge/discharge behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heater duty cycle monitoring:&lt;/strong&gt; observe ON/OFF cycling to spot abnormal behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EV charging monitor:&lt;/strong&gt; pair with a WiFi-capable board to publish a simple dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can map current readings to meaningful states, alerts, or logs in your project.&lt;/p&gt;

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

&lt;p&gt;You wired an &lt;strong&gt;ACS712&lt;/strong&gt; to an &lt;strong&gt;Arduino&lt;/strong&gt;, calibrated the 0A offset, and computed both DC current and AC RMS current from the module's analog output. This makes the ACS712 a practical choice for power monitoring, motor state detection, and appliance sensing without directly tapping into the measured line electrically.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Credit: This guide was inspired by &lt;a href="https://www.instructables.com/Simplified-Arduino-AC-Current-Measurement-Using-AC/" rel="noopener noreferrer"&gt;"Simplified Arduino AC Current Measurement Using ACS712 Hall Effect Sensor" on Instructables&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>electronics</category>
      <category>sensors</category>
      <category>iot</category>
    </item>
    <item>
      <title>Arduino Nano KY-018: Build a Light-Following Robot</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Sun, 19 Jul 2026 15:11:09 +0000</pubDate>
      <link>https://dev.to/shilleh/arduino-nano-ky-018-build-a-light-following-robot-4j52</link>
      <guid>https://dev.to/shilleh/arduino-nano-ky-018-build-a-light-following-robot-4j52</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcgddb7um909rztkk65nc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcgddb7um909rztkk65nc.jpg" width="800" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Arduino Nano + KY-018 photoresistors + L298N:&lt;/strong&gt; In this build you make a light-following robot where two LDR sensors act like eyes, and differential steering drives two DC motors toward the brightest light source.&lt;/p&gt;

&lt;p&gt;Two photoresistors (LDRs) detect which side is brighter, the Arduino compares the readings, and two motors steer the chassis toward the light. Shine a flashlight and watch the robot chase it.&lt;/p&gt;

&lt;p&gt;This guide wires the KY-018 photoresistor module (or bare LDRs with 10 k pull-down resistors), connects two DC motors through an L298N motor driver, and programs the differential steering behavior.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; About 60 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; A two-sensor Arduino robot that turns and drives toward a bright light.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-nano-v3-presoldered-ch340g-atmega328p" rel="noopener noreferrer"&gt;Arduino Nano V3.0&lt;/a&gt; - reads the two photoresistor signals and controls motor speed and direction.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-l298n-motor-driver-controller-board" rel="noopener noreferrer"&gt;L298N Motor Driver&lt;/a&gt; - drives two DC motors from a battery pack using Arduino control pins.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-830-point-breadboard-for-arduino-raspberry-pi-esp32-and-other-microcontrollers" rel="noopener noreferrer"&gt;Breadboard&lt;/a&gt; + &lt;a href="https://shillehtek.com/products/shillehtek-120pcs-multicolored-dupont-wire" rel="noopener noreferrer"&gt;jumper wires&lt;/a&gt; - quick prototyping for the sensors and signal wiring.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-820pcs-metal-film-resistors-kit" rel="noopener noreferrer"&gt;820pc Resistor Kit&lt;/a&gt; - includes 10 k resistors if you are using bare LDRs (not KY-018 modules).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/1-x-18650-battery-holder-box-with-wire-no-cover" rel="noopener noreferrer"&gt;18650 Battery Holder&lt;/a&gt; - motor power option (a 4x AA holder also works).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Two KY-018 photoresistor modules (or two bare LDRs + two 10 k resistors).&lt;/li&gt;
&lt;li&gt;Two DC gearmotors + wheels (yellow TT motors are common).&lt;/li&gt;
&lt;li&gt;Chassis (2-wheel + caster).&lt;/li&gt;
&lt;li&gt;4x AA battery holder or single 18650 pack (for motor power).&lt;/li&gt;
&lt;li&gt;Flashlight or bright phone light for testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: If you use the L298N for motor power from a battery pack, make sure the Arduino GND is connected to the L298N/battery negative (shared ground), and ensure your motor supply voltage matches your motors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Understand how photoresistors work
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Know what the LDR output means so you can wire and tune it correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; A photoresistor (LDR) is a semiconductor whose resistance drops in bright light. Typical values can range from about 10 M in darkness to about 10 k in room light and around 500 in direct sunlight.&lt;/p&gt;

&lt;p&gt;Wire a bare LDR as the top half of a voltage divider (LDR to +5V, 10 k pull-down to GND, analog input pin in the middle). The ADC then reads a voltage proportional to light. On the KY-018 module the divider is already built in, so you get three pins: VCC, GND, AOUT.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can explain why brighter light changes the analog reading and why two sensors let the robot decide which way to turn.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Wire the two-eye sensor setup and motor driver
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Connect two KY-018 sensors to A0/A1 and connect the L298N to the Arduino pins for differential steering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Wire the two sensors to 5V, GND, and separate analog inputs, then wire the L298N input pins and enable (PWM) pins to the Arduino. Connect your motor battery pack to the L298N motor power input, and connect battery negative to Arduino GND so they share ground.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiring map:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Left KY-018      Arduino Nano
VCC          -&amp;gt; 5V
GND          -&amp;gt; GND
AOUT         -&amp;gt; A0

Right KY-018     Arduino Nano
VCC          -&amp;gt; 5V
GND          -&amp;gt; GND
AOUT         -&amp;gt; A1

L298N            Arduino Nano
IN1          -&amp;gt; D5  (left motor direction A)
IN2          -&amp;gt; D6  (left motor direction B)
IN3          -&amp;gt; D9  (right motor direction A)
IN4          -&amp;gt; D10 (right motor direction B)
ENA          -&amp;gt; D3  (left PWM speed)
ENB          -&amp;gt; D11 (right PWM speed)
+12V         -&amp;gt; battery pack +
GND          -&amp;gt; battery pack - + Arduino GND
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mount the two LDRs facing forward and angled slightly outward, about 5 cm apart at the front of the robot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Both sensors read on A0 and A1, and the L298N is ready to drive each motor forward or backward with PWM speed control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Upload the differential-steering Arduino sketch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Make the robot turn toward the brighter sensor by speeding up one motor and slowing the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Upload the sketch below. It reads both analog inputs, computes the difference, and adjusts the left and right motor PWM values around a base speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const int LEFT_LDR  = A0;
const int RIGHT_LDR = A1;
const int LEFT_PWM  = 3;
const int RIGHT_PWM = 11;
const int L_A = 5, L_B = 6;
const int R_A = 9, R_B = 10;
const int BASE_SPEED = 150;

void setMotor(int a, int b, int pwm, int speed) {
  digitalWrite(a, speed &amp;gt; 0 ? HIGH : LOW);
  digitalWrite(b, speed &amp;gt; 0 ? LOW : HIGH);
  analogWrite(pwm, abs(speed));
}

void setup() {
  pinMode(L_A, OUTPUT); pinMode(L_B, OUTPUT);
  pinMode(R_A, OUTPUT); pinMode(R_B, OUTPUT);
}

void loop() {
  int L = analogRead(LEFT_LDR);
  int R = analogRead(RIGHT_LDR);
  int diff = L - R;     // positive = light is on left side

  int leftSpeed  = BASE_SPEED - diff / 4;
  int rightSpeed = BASE_SPEED + diff / 4;
  leftSpeed  = constrain(leftSpeed, 0, 255);
  rightSpeed = constrain(rightSpeed, 0, 255);

  setMotor(L_A, L_B, LEFT_PWM,  leftSpeed);
  setMotor(R_A, R_B, RIGHT_PWM, rightSpeed);

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; If the left LDR sees brighter light, the left motor slows and the right motor speeds up so the robot turns left. If the right LDR sees brighter light, it turns right. Balanced light drives it forward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Calibrate the behavior for your room lighting
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Prevent wandering when the room is too dark and make the response stable under your ambient light.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Ambient light varies. Print the raw LDR values with the flashlight ON and OFF and pick a threshold. You can also add a simple stop condition so the robot stops when it is too dark to see anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code (optional stop check):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (L &amp;lt; 100 &amp;amp;&amp;amp; R &amp;lt; 100) {
  // too dark, stop
  analogWrite(LEFT_PWM, 0);
  analogWrite(RIGHT_PWM, 0);
  return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; In low light the robot stops instead of driving randomly, and with a flashlight it reliably turns toward the brighter side.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Try the line-following variant (same hardware)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Reuse the same sensors and steering logic to follow a dark line on a bright surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Keep the wiring the same, but mount the LDRs facing downward a few cm above the ground. A dark line reflects less light than a white surface, so the differential steering traces the line.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The robot steers to keep the sensor readings balanced while moving along the line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Explore where this sensor pattern leads
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand how the same two-sensor comparison approach applies to other builds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the same idea of comparing two sensor inputs and steering toward the difference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic solar tracker:&lt;/strong&gt; same two-LDR pattern but drives a servo instead of wheels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunflower art installation:&lt;/strong&gt; multiple servos + LDRs to face the brightest light source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive room lighting:&lt;/strong&gt; LDRs decide when to trigger relays based on ambient light.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turtle-style crawler:&lt;/strong&gt; three LDRs for triangulated homing on a specific light.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can reuse the exact same "two sensors + difference" logic for new motion and control projects.&lt;/p&gt;

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

&lt;p&gt;With an Arduino Nano, two photoresistors (KY-018 or bare LDRs), and an L298N motor driver, you can build a real autonomous light-following robot using differential steering. The same pattern also works for line-following and other sensor-guided motion projects.&lt;/p&gt;

&lt;p&gt;Inspiration credit: &lt;a href="https://www.instructables.com/Arduino-Flashlight-Following-Robot/" rel="noopener noreferrer"&gt;Arduino Flashlight Following Robot on Instructables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>robotics</category>
      <category>electronics</category>
      <category>iot</category>
    </item>
    <item>
      <title>Arduino YF-S201 Water Flow Sensor: Measure L/min</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Sat, 18 Jul 2026 22:30:43 +0000</pubDate>
      <link>https://dev.to/shilleh/arduino-yf-s201-water-flow-sensor-measure-lmin-3ji4</link>
      <guid>https://dev.to/shilleh/arduino-yf-s201-water-flow-sensor-measure-lmin-3ji4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo37nybx2jx1aod4nzm8a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo37nybx2jx1aod4nzm8a.jpg" width="630" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Arduino + YF-S201 water flow sensor:&lt;/strong&gt; This project reads the YF-S201 hall-effect pulse output on an Arduino and converts it into real-time liters-per-minute (L/min) and total liters.&lt;/p&gt;

&lt;p&gt;The YF-S201 contains a small pinwheel with an embedded magnet. Water flow spins the wheel, and a hall-effect sensor produces pulses proportional to flow rate. The datasheet value is about &lt;strong&gt;450 pulses per liter&lt;/strong&gt; at moderate flow rates.&lt;/p&gt;

&lt;p&gt;This guide wires the sensor to an Arduino, converts pulses to L/min and total liters, and shows common applications like volume dosing for watering, consumption tracking, and basic leak detection.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 20 to 40 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; A flow meter that prints L/min to Serial and can be extended to total liters and valve shutoff.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-nano-v3-presoldered-ch340g-atmega328p" rel="noopener noreferrer"&gt;Arduino Nano V3.0&lt;/a&gt; - reads the pulse signal and computes flow rate.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/esp32-wroom-dev-board-cp2102-usb-c-presoldered" rel="noopener noreferrer"&gt;ESP32 WROOM Dev Board&lt;/a&gt; - optional, for WiFi reporting and alerts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-lcd1602-16x2-character-display-module" rel="noopener noreferrer"&gt;LCD1602 Display&lt;/a&gt; - optional, for a live on-device flow and total readout.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/1-channel-12v-relay-module" rel="noopener noreferrer"&gt;1-Channel 12V Relay&lt;/a&gt; - optional, to control a 12V solenoid shutoff valve.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;YF-S201 water flow sensor (1/2 inch NPS thread, rated 1 to 30 L/min).&lt;/li&gt;
&lt;li&gt;1/2 inch plumbing adapters to fit your hose or pipe.&lt;/li&gt;
&lt;li&gt;Optional: 12V solenoid valve for automatic shutoff.&lt;/li&gt;
&lt;li&gt;Teflon tape for threaded joints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: The YF-S201 sensor typically runs from 5V to 24V (per common specs) and provides a pulse output. Use an interrupt-capable Arduino pin (like D2 on the Nano) for reliable counting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Understand how the YF-S201 generates pulses
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Know what you are measuring so the code and calibration make sense.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Review the internal mechanism: a magnetized paddle wheel spins with water flow, and a hall-effect sensor outputs a pulse stream. Faster flow equals more pulses per second.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fea3of4makwwsdwrm02am.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fea3of4makwwsdwrm02am.png" alt="The YF-S201 uses a magnetized paddle wheel and a hall-effect sensor to generate pulses proportional to flow." width="517" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You understand that the Arduino is counting pulses and converting them into flow using a pulses-per-liter constant (often around 450 pulses per liter).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Wire the YF-S201 to the Arduino Nano
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Connect power and the pulse output to an interrupt-capable input pin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the three sensor wires: red (VCC), black (GND), yellow (pulse output). Wire the pulse output to Arduino D2 so you can use an external interrupt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiring:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;YF&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;S201&lt;/span&gt;        &lt;span class="n"&gt;Arduino&lt;/span&gt; &lt;span class="n"&gt;Nano&lt;/span&gt;
&lt;span class="n"&gt;Red&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VCC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;
&lt;span class="n"&gt;Black&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GND&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;GND&lt;/span&gt;
&lt;span class="n"&gt;Yellow&lt;/span&gt;     &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;D2&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;capable&lt;/span&gt; &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plumb the sensor inline. Use the arrow molded on the body to match the flow direction. If installed backwards, readings can drop by about 30%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The sensor is powered and the yellow signal wire is connected to D2, ready for pulse counting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Upload a basic sketch to read liters per minute (L/min)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Count pulses over a fixed time window and convert them to L/min.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Upload the sketch below, then open Serial Monitor at 9600 baud. The code counts pulses using an interrupt, measures for 1 second, and converts using 450 pulses per liter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;SENSOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;volatile&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;pulses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onPulse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;pulses&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SENSOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INPUT_PULLUP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;attachInterrupt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digitalPinToInterrupt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SENSOR&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;onPulse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RISING&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pulses&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;pulses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// measure over 1 second&lt;/span&gt;
  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;flow_lpm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;60.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;450.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flow_lpm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" L/min"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; When you open a tap, Serial Monitor prints a live flow rate. Typical kitchen sinks are often around 6 to 9 L/min.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Calibrate pulses-per-liter for your specific sensor
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Improve accuracy, since the nominal constant can vary by up to about 10% from sensor to sensor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Measure an actual known volume over a known time and compute your own pulses-per-liter constant.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Run the sensor for exactly 60 seconds into a graduated bucket.&lt;/li&gt;
&lt;li&gt; Note the pulse count (print total pulses to Serial).&lt;/li&gt;
&lt;li&gt; Measure the actual water in the bucket (in liters).&lt;/li&gt;
&lt;li&gt; Compute: &lt;code&gt;pulses_per_liter = total_pulses / actual_liters&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Update the constant in your sketch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Your computed L/min matches your real-world measurements more closely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Add a running total in liters (and display it on an LCD)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Track total consumption, not just instantaneous flow rate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Accumulate liters each second using the current L/min reading. If you have an LCD1602 connected in your existing project, you can print both values.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fah7r92vwtei4wpjjxco6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fah7r92vwtei4wpjjxco6.png" alt="Example LCD output showing both flow rate and total liters." width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;total_liters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pulses&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;pulses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;lpm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;60.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;450.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;total_liters&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;lpm&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;60.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// 1 second of flow&lt;/span&gt;

  &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setCursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lpm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" L/min "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setCursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Total "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_liters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;lcd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" L"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Total liters increases steadily while water flows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Automatic dosing: water a target volume then stop
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Shut off flow automatically after a set number of liters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Add a relay and a 12V solenoid valve on the output side of the sensor, then close the valve when &lt;code&gt;total_liters&lt;/code&gt; reaches your target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;TARGET_LITERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;VALVE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// valve OPEN (active LOW)&lt;/span&gt;
  &lt;span class="c1"&gt;// ...usual sensor setup...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...update total_liters...&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_liters&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;TARGET_LITERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;VALVE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// valve CLOSED&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The valve closes automatically once the measured total reaches the target volume. This is useful for garden zones, livestock waterers, aquaponics, or dispensing a fixed amount of water.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Leak detection concept using periodic logging and alerts
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Detect unexpected flow when water usage should be zero.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Log flow every minute. If flow is greater than 0 at a time like 3 AM, send an alert using an ESP32 integration (for example via Telegram).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can flag suspicious water usage patterns and potentially catch leaks early.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8 - Understand the YF-S201 limitations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Know when this sensor is not a good fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Keep these constraints in mind when choosing an installation and interpreting readings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Low flow accuracy:&lt;/strong&gt; Below 1 L/min the paddle can stall and readings become unreliable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chemistry:&lt;/strong&gt; The plastic body is not rated for hot water, acids, or bleach. Use a stainless-steel variant for harsh fluids.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pressure:&lt;/strong&gt; Often rated around 2 MPa (300 psi). This is typically fine for household plumbing, but not necessarily for higher-pressure irrigation mains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can decide if the YF-S201 is appropriate for your flow range, fluid type, and plumbing conditions.&lt;/p&gt;

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

&lt;p&gt;You built an Arduino-based flow meter using the YF-S201 water flow sensor, converting pulse counts into liters per minute and optionally tracking total liters for dosing and monitoring.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Attribution: This guide was inspired by &lt;a href="https://www.instructables.com/How-to-Use-Water-Flow-Sensor-Arduino-Tutorial/" rel="noopener noreferrer"&gt;How to Use Water Flow Sensor - Arduino Tutorial on Instructables&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>sensors</category>
      <category>electronics</category>
      <category>iot</category>
    </item>
    <item>
      <title>Arduino Nano DRV8825: Smooth NEMA 17 Motor Control</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Sat, 18 Jul 2026 22:27:15 +0000</pubDate>
      <link>https://dev.to/shilleh/arduino-nano-drv8825-smooth-nema-17-motor-control-3lal</link>
      <guid>https://dev.to/shilleh/arduino-nano-drv8825-smooth-nema-17-motor-control-3lal</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6flterrrxkrgty9fzbqo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6flterrrxkrgty9fzbqo.png" width="800" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Arduino Nano + DRV8825 stepper driver:&lt;/strong&gt; In this build, you will wire a DRV8825 to an Arduino Nano to drive a NEMA 17 stepper motor with correct current limiting and optional 1/32 microstepping for smoother motion.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;DRV8825&lt;/strong&gt; is a common stepper driver for 3D printers, CNC machines, and precision positioning systems. It shares the same footprint as the A4988, but supports 1/32 microstepping (vs 1/16), higher current capability (2.5 A per coil vs 2 A), and improved thermal behavior.&lt;/p&gt;

&lt;p&gt;This guide covers wiring, setting the current limit (critical to avoid damaging the driver), selecting microstepping modes, and using acceleration control for smoother starts and stops.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 30 to 60 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner to Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; A DRV8825-controlled NEMA 17 stepper setup that spins reliably and can ramp smoothly using AccelStepper.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-nano-v3-presoldered-ch340g-atmega328p" rel="noopener noreferrer"&gt;Arduino Nano V3.0&lt;/a&gt; - the example controller board used for STEP/DIR control.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/esp32-wroom-dev-board-cp2102-usb-c-presoldered" rel="noopener noreferrer"&gt;ESP32 WROOM Dev Board&lt;/a&gt; - optional, for WiFi-controlled motion projects.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-830-point-breadboard-for-arduino-raspberry-pi-esp32-and-other-microcontrollers" rel="noopener noreferrer"&gt;Breadboard&lt;/a&gt; + &lt;a href="https://shillehtek.com/products/shillehtek-120pcs-multicolored-dupont-wire" rel="noopener noreferrer"&gt;DuPont wires&lt;/a&gt; - for quick prototyping and clean jumper connections.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-l298n-motor-driver-controller-board" rel="noopener noreferrer"&gt;L298N Motor Driver&lt;/a&gt; - alternative option for DC motors (not for steppers).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;DRV8825 stepper driver module (use one that includes a 100 uF capacitor, or add one).&lt;/li&gt;
&lt;li&gt;NEMA 17 bipolar stepper motor (example: 1.7 A per phase, 200 steps/rev).&lt;/li&gt;
&lt;li&gt;12 V or 24 V motor power supply (rated at least 2 A).&lt;/li&gt;
&lt;li&gt;Small multimeter for current-limit calibration (Vref measurement).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: Connect a 100 uF capacitor between VMOT and GND close to the DRV8825. Without it, motor kickback can create voltage spikes that damage the driver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Understand why DRV8825 is used over A4988
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Know what you gain (and why it matters) before wiring anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Compare the driver limits and capabilities so you choose the right module for your motor and supply voltage.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Current:&lt;/strong&gt; DRV8825 = 2.5 A/coil (with heatsink), A4988 = 2 A/coil max.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microstepping:&lt;/strong&gt; DRV8825 goes to 1/32; A4988 caps at 1/16.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voltage:&lt;/strong&gt; DRV8825 accepts 8.2 to 45 V; A4988 accepts 8 to 35 V.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thermal shutdown:&lt;/strong&gt; DRV8825 has automatic thermal cutoff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You understand when DRV8825 is the better default (most 3D printer and CNC use cases) and when A4988 can still be acceptable for lower-current experiments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Wire the DRV8825 to the Arduino Nano and motor supply
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Connect logic, motor power, and coil outputs correctly before powering on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Follow the mapping below and double-check polarity for VMOT and GND.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DRV8825      Arduino Nano
STEP     -&amp;gt;  D3
DIR      -&amp;gt;  D2
ENABLE   -&amp;gt;  D8   (LOW = enabled, HIGH = disabled)
M0, M1, M2 -&amp;gt; GND for full step / +5V combos for microstepping
VMOT     -&amp;gt;  Motor +12V (or +24V)
GND (motor) -&amp;gt; Motor supply GND
VDD      -&amp;gt;  Arduino 5V
GND (logic) -&amp;gt; Arduino GND
1A, 1B, 2A, 2B -&amp;gt; stepper motor coils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; connect the 100 uF capacitor between VMOT and GND as close as possible to the module. Without it, motor kickback voltage can spike into the driver and destroy it. Many modules ship without one soldered on, so add it if needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Your DRV8825 has correct logic power, motor power, common grounds, and coil wiring ready for calibration and testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Set the DRV8825 current limit using Vref
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Prevent overheating and protect the driver and motor by setting the correct coil current.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Calculate a target current, compute Vref, and adjust the trim pot while measuring with a multimeter.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt; Look up your motor's rated phase current (example: 1.7 A for a common NEMA 17).&lt;/li&gt;
&lt;li&gt; Multiply by 0.7 for a safety margin: 1.7 × 0.7 = 1.19 A.&lt;/li&gt;
&lt;li&gt; Calculate Vref = current × 2 × Rsense. Rsense is 0.1Ω on most DRV8825 boards. Example: Vref = 1.19 × 2 × 0.1 = 0.238 V.&lt;/li&gt;
&lt;li&gt; Power the driver with motor + logic supply. Do NOT connect the motor yet.&lt;/li&gt;
&lt;li&gt; Put a multimeter between the VREF pad (small pad next to the trim pot) and any GND.&lt;/li&gt;
&lt;li&gt; Turn the trim pot until the meter reads your calculated Vref.&lt;/li&gt;
&lt;li&gt; Now connect the motor.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The motor runs with good torque while staying cool to the touch during continuous motion.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Upload a basic sketch to make the motor spin
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Confirm your wiring and driver enable/dir/step control works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Upload the sketch below, then change direction by toggling DIR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;STEP_PIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;DIR_PIN&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;EN_PIN&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EN_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EN_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// enable driver&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DIR_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delayMicroseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STEP_PIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delayMicroseconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The motor spins. In full-step mode, this sketch outputs 1000 steps/sec which is about 5 revolutions/sec for a 200 steps/rev motor. Reverse by setting DIR_PIN LOW.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Select microstepping modes with M0, M1, and M2
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Use DRV8825 microstepping to reduce vibration and smooth motion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Set M0/M1/M2 HIGH or LOW (via wiring) to select the microstepping mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;M2 M1 M0 :: Microstep
L  L  L  :: full step (200 steps/rev)
L  L  H  :: 1/2 step
L  H  L  :: 1/4 step
L  H  H  :: 1/8 step
H  L  L  :: 1/16 step
H  H  L  :: 1/32 step (smoothest, quietest)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At 1/32, one motor revolution takes 6400 STEP pulses instead of 200. This greatly smooths motion at slow speeds. Trade-off: lower per-microstep torque and a higher STEP pulse rate needed for the same visible speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The motor runs quieter and smoother, especially at low RPM, when you switch to higher microstepping.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Add smooth acceleration and deceleration with AccelStepper
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Avoid jerky starts by ramping speed and improving usable torque.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the AccelStepper library to set max speed, acceleration, and a target move.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;AccelStepper.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;AccelStepper&lt;/span&gt; &lt;span class="nf"&gt;motor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// driver mode, STEP, DIR&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;motor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setMaxSpeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;      &lt;span class="c1"&gt;// steps/sec&lt;/span&gt;
  &lt;span class="n"&gt;motor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setAcceleration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// steps/sec^2&lt;/span&gt;
  &lt;span class="n"&gt;motor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;moveTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           &lt;span class="c1"&gt;// 1 rev at 1/32&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;motor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;                  &lt;span class="c1"&gt;// must call often&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The motor accelerates smoothly toward the target instead of starting instantly at full speed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Map the setup to real-world motion projects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand where this DRV8825 + stepper approach is commonly used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the same wiring and control concepts for these typical applications.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3D printer axes&lt;/strong&gt; (X, Y, Z) where DRV8825 is commonly used on maker control stacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small CNC builds&lt;/strong&gt; such as MPCNC and similar gantry systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Camera sliders and motorized dollies&lt;/strong&gt; that need smooth microstepped motion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic curtain openers&lt;/strong&gt; with a gear reduction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus stacking rails&lt;/strong&gt; for macro photography.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Astronomy trackers&lt;/strong&gt; where high microstepping plus gear reduction supports very slow motion rates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can reuse the same DRV8825 wiring, current limiting, and step control patterns in a wide range of motion projects.&lt;/p&gt;

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

&lt;p&gt;The DRV8825 is a strong upgrade path over the A4988: same footprint and control signals, higher current capability, more microstepping options, and better thermal protection. Once you set the current limit correctly and add the input capacitor on VMOT, it becomes a reliable driver for 3D printers, CNC machines, camera rigs, and precision positioning projects.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Credit: This guide was inspired by &lt;a href="https://www.instructables.com/Controll-a-Stepper-Motor-With-the-DRV8825/" rel="noopener noreferrer"&gt;"Controll a Stepper Motor With the DRV8825" on Instructables&lt;/a&gt;. Images credited to the original author.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>drv8825</category>
      <category>robotics</category>
      <category>electronics</category>
    </item>
    <item>
      <title>XIAO nRF52840 BME280: Build a BLE Temp Sensor</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Sat, 18 Jul 2026 22:23:53 +0000</pubDate>
      <link>https://dev.to/shilleh/xiao-nrf52840-bme280-build-a-ble-temp-sensor-31g9</link>
      <guid>https://dev.to/shilleh/xiao-nrf52840-bme280-build-a-ble-temp-sensor-31g9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl0oxe1ghv1311nd5qlal.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl0oxe1ghv1311nd5qlal.jpg" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Seeed XIAO nRF52840 + BME280:&lt;/strong&gt; In this build, you set up Arduino IDE and create a BLE temperature/humidity peripheral that advertises to your phone and targets ultra-low sleep current for long battery life.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Seeed XIAO nRF52840&lt;/strong&gt; is a strong choice when you want a BLE device that can run for years on a coin cell. Nordic Semi's nRF52840 is used in many commercial wearables: ARM Cortex-M4F @ 64 MHz, 1 MB Flash, 256 KB SRAM, BLE 5 (including long-range PHY), NFC, and native USB, all on a thumbnail-sized board. With Arduino IDE support and Adafruit's Bluefruit libraries, you can go from zero to a BLE sensor quickly.&lt;/p&gt;

&lt;p&gt;This guide gets a new XIAO nRF52840 board onto Arduino IDE, builds a BLE-broadcast temperature/humidity sensor, and explains where the nRF52840 beats ESP32 variants for battery life.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; About 20 to 30 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner to Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; A BLE advertising sensor that reports temperature (and optionally humidity) from a small I2C sensor to a phone app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/xiao-seeed-esp32c3-pre-soldered-with-usb-to-usb-c-cable" rel="noopener noreferrer"&gt;XIAO ESP32-C3 Pre-Soldered&lt;/a&gt; - a close comparable for ESP-side BLE projects.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/xiao-seeed-esp32s3-pre-soldered-with-usb-c-cable" rel="noopener noreferrer"&gt;XIAO ESP32-S3 Pre-Soldered&lt;/a&gt; - an alternative option when you need more features beyond BLE-only builds.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-dht22-with-cables" rel="noopener noreferrer"&gt;DHT22 Sensor&lt;/a&gt; - a simple temperature/humidity sensor option.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/bme280-pre-soldered-atmospheric-temperature-pressure-and-humidity-sensor" rel="noopener noreferrer"&gt;BME280&lt;/a&gt; - I2C temperature, pressure, and humidity sensing for the BLE peripheral example.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/351015-500mah-3-7v-lithium-rechargeable-battery" rel="noopener noreferrer"&gt;500 mAh LiPo&lt;/a&gt; - or use a CR2032 coin cell for ultra-long battery life experiments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Seeed XIAO nRF52840 board - the board this guide focuses on.&lt;/li&gt;
&lt;li&gt;USB-C cable.&lt;/li&gt;
&lt;li&gt;Arduino IDE 2.x with Adafruit nRF52 boards installed.&lt;/li&gt;
&lt;li&gt;(Optional) A BLE scanner app such as nRF Connect (mobile).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: This guide uses Adafruit's nRF52 (Bluefruit) Arduino support. For the BME280 example, the sensor is assumed to be on I2C at address 0x76.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Understand why nRF52840 can beat ESP32 for BLE-only battery life
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Know when the nRF52840 is a better fit than ESP32 variants for wearables and coin-cell sensors.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Compare your project requirements. The ESP32-C3 is excellent for WiFi plus BLE, but for pure BLE wearables the nRF52840 has advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deep-sleep current:&lt;/strong&gt; nRF52840 is about 1.5 µA with RAM retention. ESP32-C3 is about 5 µA typical, and can be much higher if BLE keeps any state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BLE radio efficiency:&lt;/strong&gt; Nordic's BLE stack is well known for low power; long-range PHY can reach about 1 km line of sight in ideal conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NFC tag emulation:&lt;/strong&gt; nRF52840 can be tapped by a phone to launch a URL or assist pairing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native USB:&lt;/strong&gt; Useful for certain workflows and device modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coin-cell friendly voltage:&lt;/strong&gt; nRF52840 can operate down to about 1.7 V. ESP32 typically needs around 3.0 V minimum.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can decide if your project is BLE-only and battery-limited (nRF52840 advantage) or if you need WiFi and higher performance (often ESP32).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Install the Adafruit nRF52 board support in Arduino IDE
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Get Arduino IDE ready to compile and upload sketches for the Seeed XIAO nRF52840.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Open &lt;strong&gt;File&lt;/strong&gt; - &lt;strong&gt;Preferences&lt;/strong&gt;. Add this to Additional Boards Manager URLs: &lt;code&gt;https://www.adafruit.com/package_adafruit_index.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt; Open &lt;strong&gt;Boards Manager&lt;/strong&gt;, search &lt;strong&gt;nRF52&lt;/strong&gt;, then install &lt;strong&gt;Adafruit nRF52 by Adafruit&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Go to &lt;strong&gt;Tools&lt;/strong&gt; - &lt;strong&gt;Board&lt;/strong&gt; - &lt;strong&gt;Adafruit nRF52 Boards&lt;/strong&gt; - select &lt;strong&gt;Seeed XIAO nRF52840&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Go to &lt;strong&gt;Tools&lt;/strong&gt; - &lt;strong&gt;Port&lt;/strong&gt; and select the USB CDC port that appears when you plug the board in.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The XIAO nRF52840 appears as a selectable board and a valid serial/USB port is available for upload and Serial Monitor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Upload a first sketch that advertises over BLE
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Confirm the toolchain works and the board can advertise as a BLE device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Paste this sketch into Arduino IDE and upload it to the board.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;bluefruit.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;115200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ShillehTek-XIAO"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setTxPower&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Advertising&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Advertising&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addTxPower&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Advertising&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Advertising"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Open a BLE scanner app (for example nRF Connect) and you should see &lt;strong&gt;ShillehTek-XIAO&lt;/strong&gt; appear in the scan list quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Build a BLE temperature/humidity peripheral using a BME280
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Read temperature from a BME280 and expose it over BLE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Wire your BME280 to the XIAO nRF52840 using I2C (SDA, SCL, 3V3, GND). Then upload the sketch below.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;bluefruit.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Adafruit_BME280.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="n"&gt;Adafruit_BME280&lt;/span&gt; &lt;span class="n"&gt;bme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;BLEService&lt;/span&gt; &lt;span class="nf"&gt;envSvc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"181A"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                       &lt;span class="c1"&gt;// Environmental Sensing&lt;/span&gt;
&lt;span class="n"&gt;BLECharacteristic&lt;/span&gt; &lt;span class="nf"&gt;tempC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2A6E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BLECharacteristic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;READ&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Wire&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;bme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x76&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"XIAO-Env"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;envSvc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;tempC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CHR_PROPS_READ&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;CHR_PROPS_NOTIFY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;tempC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFixedLen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;tempC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Advertising&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;envSvc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Bluefruit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Advertising&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int16_t&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int16_t&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;bme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;readTemperature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;tempC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;uint8_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; In your BLE app, you can connect to &lt;strong&gt;XIAO-Env&lt;/strong&gt; and see the Environmental Sensing service with a temperature characteristic that updates every 5 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Apply the low-power approach needed to reach very low sleep current
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand what changes are required to approach the sub-20 µA sleep target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use a sleep strategy consistent with low-power BLE designs: stop BLE advertising during sleep, drop the CPU into System ON sleep, then wake on a GPIO interrupt (button) or an RTC timer. In the Adafruit nRF52 environment, relevant primitives include &lt;code&gt;sd_app_evt_wait()&lt;/code&gt; and &lt;code&gt;NRF_POWER-&amp;gt;SYSTEMOFF&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You have a clear path to modify the sketch so it only advertises when needed and spends most of its time sleeping.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Choose a power source that matches your runtime target
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Pick a battery and power approach appropriate for a wearable or sensor node.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use one of these common options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CR2032 coin cell (240 mAh):&lt;/strong&gt; roughly 3 to 5 years for a sensor that reports every 5 minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;500 mAh LiPo + TP4056:&lt;/strong&gt; roughly 6 months between charges for a worn device with frequent BLE traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single 18650 + boost regulator:&lt;/strong&gt; roughly 5 years for a stationary sensor (high capacity, often overkill).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can match the chemistry and capacity to your update interval and BLE activity level.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Map the platform to real projects where it makes sense
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Identify practical BLE-first products where nRF52840 strengths matter.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the nRF52840 approach for projects like these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Door and window sensors:&lt;/strong&gt; wake on a reed switch, broadcast once, then sleep. CR2032 can last 5+ years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BLE asset tags and iBeacons:&lt;/strong&gt; multi-year coin-cell battery life with low-duty advertising.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fitness wearable:&lt;/strong&gt; heart rate plus accelerometer with strong power efficiency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart-home button:&lt;/strong&gt; wake on press, send an event, then sleep again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plant moisture trackers:&lt;/strong&gt; wake on a schedule, transmit, then deep sleep for long intervals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can quickly tell whether your application is a good fit for coin-cell BLE design and the nRF52840 feature set.&lt;/p&gt;

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

&lt;p&gt;The Seeed XIAO nRF52840 is a strong MCU choice when BLE battery life matters more than adding WiFi. With Arduino IDE and Bluefruit libraries, you can build a small BLE temperature sensor using a BME280 and then extend it toward very low sleep current by advertising only when needed.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Credits: This guide was inspired by &lt;a href="https://www.instructables.com/Easy-Very-Low-Power-BLE-2022/" rel="noopener noreferrer"&gt;Easy Very Low Power BLE &amp;lt;20µA (2022/2024) With Arduino&lt;/a&gt; on Instructables. Images credited to the original author.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>nrf52840</category>
      <category>ble</category>
      <category>sensors</category>
    </item>
    <item>
      <title>Seeeduino XIAO SAMD21 USB-HID: Build a Macro Numpad</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Sat, 18 Jul 2026 22:20:12 +0000</pubDate>
      <link>https://dev.to/shilleh/seeeduino-xiao-samd21-usb-hid-build-a-macro-numpad-aoa</link>
      <guid>https://dev.to/shilleh/seeeduino-xiao-samd21-usb-hid-build-a-macro-numpad-aoa</guid>
      <description>&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Seeeduino XIAO SAMD21 USB-HID macropad:&lt;/strong&gt; In this build, you use the Seeeduino XIAO SAMD21 with a 4x4 matrix keypad to create a USB-HID numpad that types keys and full macro strings on your computer.&lt;/p&gt;

&lt;p&gt;The Seeeduino XIAO SAMD21 is a thumbnail-sized Arduino-class board (ATSAMD21G18 ARM Cortex-M0+ @ 48 MHz, 256 KB Flash, 32 KB SRAM) with native USB-HID support. It runs CircuitPython, MicroPython, and Arduino C, and it is a strong choice when you need a tiny board that can act like a USB keyboard, mouse, or MIDI device.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 30 to 60 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner to Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; A USB-HID numpad/macropad that types single keys or multi-character macros when you press buttons.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/xiao-seeed-esp32s3-pre-soldered-with-usb-c-cable" rel="noopener noreferrer"&gt;XIAO ESP32-S3&lt;/a&gt; - bigger sibling for advanced builds.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/xiao-seeed-esp32c3-pre-soldered-with-usb-to-usb-c-cable" rel="noopener noreferrer"&gt;XIAO ESP32-C3&lt;/a&gt; - cheap BLE alternative.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/pro-micro-atmega32u4-5v-16mhz-presoldered-micro-usb" rel="noopener noreferrer"&gt;Pro Micro ATmega32U4&lt;/a&gt; - the classic USB-HID board.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/4x4-membrane-matrix-keypad-16-key-switch-module-for-arduino-diy" rel="noopener noreferrer"&gt;4x4 Membrane Matrix Keypad&lt;/a&gt; - for the numpad layout.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-120pcs-multicolored-dupont-wire" rel="noopener noreferrer"&gt;DuPont Wires&lt;/a&gt; - for quick prototyping and keypad connections.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Seeeduino XIAO SAMD21 board&lt;/li&gt;
&lt;li&gt;USB-C cable&lt;/li&gt;
&lt;li&gt;16 mechanical key switches (Cherry MX / Kailh) + keycaps, OR a 4x4 membrane keypad&lt;/li&gt;
&lt;li&gt;Arduino IDE 2.x with the "Seeeduino SAMD" boards package installed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: This guide uses the XIAO SAMD21 because it supports native USB-HID. If you swap boards, confirm the board and core you choose supports the Keyboard library or an equivalent HID stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Understand why the XIAO SAMD21 is still a great USB-HID choice
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Know when the XIAO SAMD21 is the right tool for a macropad or HID project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Keep these strengths in mind before you start building:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;USB-HID projects:&lt;/strong&gt; Native USB-HID plus a small footprint is ideal for macropads, dial controllers, and similar devices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boards that fit anywhere:&lt;/strong&gt; About 20 mm x 17 mm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CircuitPython for beginners:&lt;/strong&gt; Drag-and-drop firmware updates and edit code from a simple drive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Industrial deployments:&lt;/strong&gt; SAMD21 is an automotive-grade chip with long-term availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No WiFi/BLE radio:&lt;/strong&gt; Helpful when you do not want radio certification overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You know why this board is commonly used for small USB keyboard-style builds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Install the Seeed SAMD board package in Arduino IDE
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Make Arduino IDE recognize the Seeeduino XIAO SAMD21.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In Arduino IDE: File -&amp;gt; Preferences. Add this Boards Manager URL:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open Tools -&amp;gt; Board -&amp;gt; Boards Manager, then install: &lt;strong&gt;Seeed SAMD Boards by Seeed Studio&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to Tools -&amp;gt; Board -&amp;gt; Seeeduino SAMD -&amp;gt; &lt;strong&gt;Seeeduino XIAO&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The Seeeduino XIAO board option is available and selected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Upload a first USB-HID sketch that types text
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Confirm USB-HID keyboard output works on your computer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Upload this sketch, then focus a text editor and plug in (or reset) the board.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Keyboard.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from ShillehTek XIAO!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; After about 2 seconds, the board types the message into whichever app currently has focus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Wire a 4x4 keypad and scan it as a numpad
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Read a 4x4 matrix keypad and send a keypress over USB when a key is pressed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Wire 4 columns and 4 rows of the keypad to XIAO pins D0 through D7. The code below scans the matrix in &lt;code&gt;loop()&lt;/code&gt; and emits a mapped character.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;Keyboard.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ROW_PINS&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;COL_PINS&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;KEYMAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'+'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;'4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'5'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'6'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;'7'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'8'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'9'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'*'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;'0'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'='&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="sc"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ROW_PINS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;COL_PINS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INPUT_PULLUP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ROW_PINS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROW_PINS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digitalRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;COL_PINS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KEYMAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;releaseAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ROW_PINS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Pressing a keypad button types the mapped character (1, 2, 3, +, etc.) on the host computer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Upgrade keys into real macros (type whole snippets)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Replace single key outputs with multi-character strings or sequences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Update your key handling so certain keys type full snippets such as &lt;code&gt;git push&lt;/code&gt;, a common email address, or any repeated text. The XIAO SAMD21 stores these strings in flash, so they persist across power cycles.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Pressing selected keys types whole snippets instead of single characters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Use CircuitPython instead of Arduino (optional)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Get the same HID behavior using CircuitPython.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Double-tap the reset pin so the XIAO mounts as an &lt;strong&gt;ARDUINO&lt;/strong&gt; drive.&lt;/li&gt;
&lt;li&gt; Copy the CircuitPython UF2 onto it. It reboots as &lt;strong&gt;CIRCUITPY&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Edit &lt;code&gt;code.py&lt;/code&gt; in any text editor. Changes auto-reload.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;digitalio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usb_hid&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;adafruit_hid.keyboard&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Keyboard&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;adafruit_hid.keycode&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Keycode&lt;/span&gt;

&lt;span class="n"&gt;kbd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Keyboard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usb_hid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;btn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;digitalio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DigitalInOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;D0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;switch_to_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pull&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;digitalio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pull&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;kbd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Keycode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CONTROL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Keycode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;C&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Pressing the button on D0 sends the Ctrl+C shortcut over USB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Know where the XIAO SAMD21 wins over an ESP32-C3 for HID
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand tradeoffs if you are choosing between boards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use these points as a quick comparison:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small footprint and simple USB-HID behavior.&lt;/li&gt;
&lt;li&gt;No radio, which can reduce certification complexity for production hardware.&lt;/li&gt;
&lt;li&gt;CircuitPython is typically more polished on SAMD21 than on ESP32-C3.&lt;/li&gt;
&lt;li&gt;USB-HID works without extra boot configuration steps.&lt;/li&gt;
&lt;li&gt;Industrial-grade silicon with long-term availability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can justify the SAMD21 choice for wired HID macropads and similar projects.&lt;/p&gt;

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

&lt;p&gt;You built a Seeeduino XIAO SAMD21 USB-HID macropad using a 4x4 keypad matrix, starting from a simple "type on plug-in" sketch and expanding into a real numpad and macro-typing workflow.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Inspiration and image credit: &lt;a href="https://www.instructables.com/HID-Numpad-With-XIAO-SAMD21/" rel="noopener noreferrer"&gt;HID Numpad With XIAO SAMD21 (Instructables)&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>circuitpython</category>
      <category>usbhid</category>
      <category>embedded</category>
    </item>
    <item>
      <title>Arduino 4-Channel Relay Module: Reverse a DC Motor</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Sat, 18 Jul 2026 22:16:20 +0000</pubDate>
      <link>https://dev.to/shilleh/arduino-4-channel-relay-module-reverse-a-dc-motor-57kc</link>
      <guid>https://dev.to/shilleh/arduino-4-channel-relay-module-reverse-a-dc-motor-57kc</guid>
      <description>&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Arduino + 4-channel relay module motor reversing:&lt;/strong&gt; In this build, you wire a 4-channel relay module as a simple H-bridge so an Arduino can drive a DC motor forward and reverse for high-current on/off motion projects.&lt;/p&gt;

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

&lt;p&gt;An H-bridge built from four contactor relays is a low-cost way to drive a high-current DC motor forward and reverse from an Arduino. Two relays do direction, and two more can be used for braking or current-limiting behavior. A programmable 4-channel relay module with built-in timer logic works well for tasks like a linear actuator extending and retracting on a schedule, an aquarium pump alternating its flow direction, or electric blinds opening and closing on a daily routine.&lt;/p&gt;

&lt;p&gt;This guide builds a working motor-reversing system around a 4-channel relay module, a 12 V DC motor, and an Arduino. The same wiring scales to lock actuators, valve drivers, sliding doors, and roller blinds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 30 to 60 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; A relay-based H-bridge that lets an Arduino reverse a DC motor and stop it safely with a software interlock.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/4-channel-12v-relay-module" rel="noopener noreferrer"&gt;4-Channel 12V Relay Module&lt;/a&gt; - the workhorse for this build.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/4-channel-5v-relay-module" rel="noopener noreferrer"&gt;4-Channel 5V Relay Module&lt;/a&gt; - if you are driving the coils from a 5V Arduino.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-l298n-motor-driver-controller-board" rel="noopener noreferrer"&gt;L298N Motor Driver&lt;/a&gt; - the electronic-only alternative for under 2A loads.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/arduino-nano-v3-presoldered-ch340g-atmega328p" rel="noopener noreferrer"&gt;Arduino Nano V3.0&lt;/a&gt; - controller for relay outputs and safety interlocks.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-hc-sr04-with-rgb-light-distance" rel="noopener noreferrer"&gt;HC-SR04 Distance Sensor&lt;/a&gt; - optional for end-stop detection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;12 V or 24 V DC motor (or linear actuator).&lt;/li&gt;
&lt;li&gt;An appropriate power supply rated for the motor's stall current.&lt;/li&gt;
&lt;li&gt;Two limit switches (mechanical micro-switches) for end-stop safety.&lt;/li&gt;
&lt;li&gt;A fuse inline with the motor - size to motor running current plus 50%.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: Many relay modules are active LOW (a LOW turns the relay on). Also match relay coil voltage (5V or 12V) to the module you choose, and never power a motor directly from the Arduino.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Decide if relays are the right H-bridge choice
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand why a relay H-bridge is useful for high current and what you give up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; If you only need on/off control and your motor current is beyond typical small H-bridge driver chips, a 4-relay H-bridge can be a good fit. It is simple, cheap, and galvanically isolated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9yt6n9i98vjfr15hivjr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9yt6n9i98vjfr15hivjr.png" alt="Four relays wired as an H-bridge to reverse a DC motor." width="800" height="491"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You know when to use relays (high current, on/off reversing) and when not to (PWM speed control and ultra-high cycle life requirements).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Wire the 4-relay H-bridge for forward and reverse
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Connect the relays so the motor polarity can be swapped safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Wire four relays in two "sides," so you can energize a diagonal pair for forward, and the opposite diagonal pair for reverse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wiring reference:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             +12V
              |
              +-------+-------+
              |               |
            [REL1]           [REL3]
            COM-NO           COM-NO
              |               |
              +---- MOTOR ----+
              |               |
            [REL2]           [REL4]
            COM-NO           COM-NO
              |               |
              +-------+-------+
                      |
                     GND

To drive forward:   REL1 + REL4 closed (the diagonal)
To drive reverse:   REL3 + REL2 closed (the other diagonal)
To brake:           any two on the same side closed
To coast:           all four open
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; Never close REL1 + REL2 (or REL3 + REL4) at the same time. That is a direct short across the supply. You must add an interlock in your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Your wiring matches the diagonal-pair logic for forward and reverse without creating a supply short condition.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Upload Arduino code with a software interlock (dead-time)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Ensure both relay pairs are opened before switching directions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use an "all off" function, then add a short delay (dead-time) before turning on the next relay pair.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;   &lt;span class="c1"&gt;// REL1..REL4&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;allOff&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// active LOW&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;allOff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                          &lt;span class="c1"&gt;// dead-time&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// REL1&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// REL4&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;allOff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// REL2&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// REL3&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;brake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;allOff&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// REL1&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                 &lt;span class="c1"&gt;// REL3 (high side together)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RELAYS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;forward&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;brake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;brake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;delay(50)&lt;/code&gt; dead-time between modes helps guarantee both relay pairs are open before either pair closes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The motor runs forward, brakes, runs reverse, and brakes again without ever energizing an unsafe relay combination.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Add end-stop limit switches for safer motion
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Stop motion at the physical endpoints even if timing is wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Install and wire two limit switches, then read them using &lt;code&gt;INPUT_PULLUP&lt;/code&gt; so a pressed switch reads LOW. If either end-stop triggers, call &lt;code&gt;brake()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fytfmqsd1l7zdf02pk6xn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fytfmqsd1l7zdf02pk6xn.jpg" alt="Limit switches on a linear actuator to stop at open and closed positions." width="800" height="935"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;LIMIT_FWD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;LIMIT_REV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LIMIT_FWD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INPUT_PULLUP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LIMIT_REV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INPUT_PULLUP&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digitalRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LIMIT_FWD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;brake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digitalRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LIMIT_REV&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;brake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// ... rest of motion logic ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a sliding door or window opener, limit switches help the motor stop precisely at the open and closed positions. Do not rely on timing alone for safety-critical motion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Pressing either limit switch forces the motor to brake, preventing overtravel.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 - Choose applications that fit the 4-relay approach
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Match this design to real projects where high-current reversing matters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F44nxuz8mmavzzk3fw1du.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F44nxuz8mmavzzk3fw1du.jpg" alt="Common projects that benefit from relay-based reversing." width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the relay H-bridge when you want robust reversing with on/off control.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Electric linear actuator&lt;/strong&gt; for a desk-height changer, kitchen pop-up monitor, or solar-tracking panel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic chicken coop door&lt;/strong&gt; that opens at sunrise and closes at sunset (sun-tracking plus RTC).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sliding window opener&lt;/strong&gt; for greenhouse ventilation that reverses on a rain sensor trigger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pet feeder auger reversal&lt;/strong&gt; to clear jams by briefly running backward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aquarium current reverser&lt;/strong&gt; to alternate flow direction every hour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3D printer build-plate elevator&lt;/strong&gt; for tall prints with a small Z motor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can quickly decide whether your project is a good match for relay reversing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Know when to use an L298N motor driver instead
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Avoid using relays when you need PWM speed control.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; If your motor is under 2 A and you want PWM speed control and smooth ramping, use an electronic H-bridge like our &lt;a href="https://shillehtek.com/products/shillehtek-l298n-motor-driver-controller-board" rel="noopener noreferrer"&gt;L298N motor driver&lt;/a&gt;. The relay approach is best for high-current, on/off only reversing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You select relays for high-current switching, and a motor driver when speed control is required.&lt;/p&gt;

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

&lt;p&gt;With four relays, an Arduino, and a small dead-time interlock, you can build a DC motor reverser that handles serious current. The same relay H-bridge pattern is widely used for actuators, doors, valves, and other reversing motion systems.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Credit: Inspired by &lt;a href="https://www.instructables.com/DC-Motor-Controller-With-Two-Relay/" rel="noopener noreferrer"&gt;DC Motor Controller With Two Relay&lt;/a&gt; on Instructables.&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>electronics</category>
      <category>robotics</category>
      <category>embedded</category>
    </item>
    <item>
      <title>STM32H723ZGT6 STM32duino: Install and benchmark</title>
      <dc:creator>Shilleh</dc:creator>
      <pubDate>Sat, 18 Jul 2026 22:13:00 +0000</pubDate>
      <link>https://dev.to/shilleh/stm32h723zgt6-stm32duino-install-and-benchmark-4eph</link>
      <guid>https://dev.to/shilleh/stm32h723zgt6-stm32duino-install-and-benchmark-4eph</guid>
      <description>&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;STM32H723ZGT6 + STM32duino:&lt;/strong&gt; In this guide, you will set up an STM32H723ZGT6 development board in Arduino IDE (via the STM32duino core), run a first blink sketch, and measure a simple high-speed loop benchmark.&lt;/p&gt;

&lt;p&gt;The STM32H723ZGT6 is a Cortex-M7 running at &lt;strong&gt;550 MHz&lt;/strong&gt; with 1 MB Flash, 564 KB RAM, hardware FPU, DSP instructions, and enough peripherals for 3D printers, audio synthesis, real-time motor control, oscilloscopes, and CAN-bus gateway nodes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time:&lt;/strong&gt; 30 to 60 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill level:&lt;/strong&gt; Beginner to Intermediate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What you will build:&lt;/strong&gt; An Arduino IDE setup for the STM32H723ZGT6 plus a blink test and a float-math timing benchmark over Serial&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parts List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  From ShillehTek
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-pre-soldered-stm32h723zgt6-core-development-board" rel="noopener noreferrer"&gt;STM32H723ZGT6 Pre-Soldered Core Development Board&lt;/a&gt; - the H7 board used for setup and benchmarking&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-pre-soldered-authentic-stm32f103c8t6-arm-stm32" rel="noopener noreferrer"&gt;STM32F103 Blue Pill&lt;/a&gt; - older sibling for performance reference&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/shillehtek-pre-soldered-authentic-stm32f411ceu6" rel="noopener noreferrer"&gt;STM32F411 Black Pill&lt;/a&gt; - mid-tier comparison&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://shillehtek.com/products/cp2102-usb-to-ttl-uart-serial-converter-module-6pin-for-arduino" rel="noopener noreferrer"&gt;CP2102 USB-to-TTL&lt;/a&gt; - for UART bootloader flashing when SWD is not available&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  External
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ST-Link V2 programmer (or use the onboard one if your board has it)&lt;/li&gt;
&lt;li&gt;USB-C cable&lt;/li&gt;
&lt;li&gt;Arduino IDE 2.x with the STM32duino board package installed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: Upload methods vary by board variant. Some boards expose USB DFU directly, while others are easiest to program via SWD (ST-Link) or the UART bootloader.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Guide
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 - Decide if the H7 is the right fit
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand what makes the STM32H723 (H7 family) different from an ESP32 or older STM32 parts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the list below to sanity-check whether you actually need the H7 class of performance and peripherals.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;550 MHz Cortex-M7&lt;/strong&gt; - significantly faster per core than the ESP32's 240 MHz LX6, plus a deeper pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-precision + double-precision FPU&lt;/strong&gt; - STM32F103 has none; ESP32-S3 has single only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DSP instructions&lt;/strong&gt; - the M7 includes SIMD for parallel 16-bit math (audio, FFT, motor control).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time peripherals&lt;/strong&gt; - advanced timers, hardware CAN-FD, and fast ADC options.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic timing&lt;/strong&gt; - no WiFi/BLE interrupts hijacking your control loop, unlike ESP32.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You have a clear reason to use the H7 (real-time control, DSP-heavy workloads, or high-performance peripherals) instead of a smaller MCU or an ESP32.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 - Install STM32duino in Arduino IDE
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Add ST's STM32 Arduino core so Arduino IDE can build and upload sketches to the STM32H723.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Follow these menu steps in Arduino IDE 2.x.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; File -&amp;gt; Preferences. Add this to Additional Boards URLs: &lt;code&gt;https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt; Boards Manager -&amp;gt; search "STM32" -&amp;gt; install "STM32 MCU based boards" by ST-Microelectronics.&lt;/li&gt;
&lt;li&gt; Tools -&amp;gt; Board -&amp;gt; STM32 boards -&amp;gt; &lt;strong&gt;Generic STM32H7 Series&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Tools -&amp;gt; Board part number -&amp;gt; &lt;strong&gt;STM32H723ZG&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt; Tools -&amp;gt; Upload method -&amp;gt; &lt;strong&gt;STM32CubeProgrammer (SWD)&lt;/strong&gt; (or "DFU" if your board exposes USB DFU).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; Arduino IDE shows the STM32H7 board options, and you can select STM32H723ZG plus an upload method that matches your hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 - Upload your first blink sketch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Confirm that your toolchain, board selection, and upload method work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Create a new sketch and upload the code below (adjust the LED pin if your board uses a different pin).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;LED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PA0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; The LED toggles on and off every 500 ms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 - Run a quick loop benchmark
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Measure a simple float-math loop to get a feel for the H7's performance and FPU capability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Upload the benchmark sketch below, open Serial Monitor at 115200 baud, and watch the loop timing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;115200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;micros&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000000UL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;1.0001&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.00001&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;Serial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"loop = %lu us&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;micros&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You see a printed microsecond time for "loop = ... us". For reference, the original comparison numbers were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Arduino Nano (no FPU): ~1.4 seconds&lt;/li&gt;
&lt;li&gt;STM32F103 Blue Pill: ~85 ms&lt;/li&gt;
&lt;li&gt;ESP32 (LX6, hard FP): ~12 ms&lt;/li&gt;
&lt;li&gt;STM32H723 @ 550 MHz: &lt;strong&gt;~1.6 ms&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5 - Map the H7 to real projects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Identify project types that actually benefit from the STM32H7 family's real-time performance and peripherals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Use the list below as a starting point for choosing a next build.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3D printer / CNC controller:&lt;/strong&gt; Klipper, Marlin 2.x, GRBL HAL support H7 for smooth high-microstep motion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audio synthesizer / effects pedal:&lt;/strong&gt; Real-time FFT and effects benefit from FPU + DSP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Motor control (FOC, BLDC):&lt;/strong&gt; High update rates and deterministic timing enable better control loops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logic / hobby oscilloscope:&lt;/strong&gt; Fast ADC into DMA and render to a display.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAN-FD gateway:&lt;/strong&gt; Hardware CAN-FD support for real-time nodes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;USB MIDI host / audio interface:&lt;/strong&gt; High-speed USB OTG with isochronous endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You have one concrete "next project" idea that matches what the H7 is good at.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 - Choose a flashing method (SWD, DFU, or UART)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Understand your options for getting firmware onto the board based on what hardware you have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; Pick one of the methods below and use it consistently while developing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SWD via ST-Link V2:&lt;/strong&gt; Fastest and most reliable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;USB DFU:&lt;/strong&gt; Hold BOOT0 button and plug USB so the board appears as a DFU device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UART bootloader:&lt;/strong&gt; Connect a &lt;a href="https://shillehtek.com/products/cp2102-usb-to-ttl-uart-serial-converter-module-6pin-for-arduino" rel="noopener noreferrer"&gt;CP2102&lt;/a&gt; to A9/A10, pull BOOT0 high, then flash via stm32flash.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You can reliably upload firmware using a method that matches your board and tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7 - Know when not to use an H7
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Avoid overkill and pick the right chip for the job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt; If you need WiFi/BLE on the same chip, the H7 does not include it. Pair with an ESP32 over UART, or use an STM32WB55 instead. If your project is simply "blink an LED when a button is pressed", the H7 is unnecessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected result:&lt;/strong&gt; You understand the tradeoff: H7-class deterministic performance versus integrated wireless features.&lt;/p&gt;

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

&lt;p&gt;The STM32H723ZGT6 is a strong choice when an ESP32 or STM32F103 has run out of headroom for real-time math, motor control loops, audio DSP, or high-performance peripherals. With STM32duino installed, you can validate your setup quickly with a blink sketch and a simple benchmark loop.&lt;/p&gt;

&lt;p&gt;Want the exact parts used in this build? Grab them from &lt;a href="https://shillehtek.com" rel="noopener noreferrer"&gt;ShillehTek.com&lt;/a&gt;. If you want help customizing this project or building something for your product, check out our &lt;a href="https://shillehtek.com/pages/iot-consulting" rel="noopener noreferrer"&gt;IoT consulting services&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Inspiration: &lt;a href="https://www.instructables.com/Automation-With-the-Giant-STM32F746G-and-ESP32/" rel="noopener noreferrer"&gt;Automation With the Giant STM32F746G and ESP32 (Instructables)&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>stm32</category>
      <category>arduino</category>
      <category>embedded</category>
      <category>cpp</category>
    </item>
  </channel>
</rss>
