<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged esp32</title>
<link>https://coderlegion.com/tag/esp32</link>
<description>Powered by Question2Answer</description>
<item>
<title>Build a bird-feeder camera that sends photos to your phone (ESP32-CAM, one weekend)</title>
<link>https://coderlegion.com/33084/build-a-bird-feeder-camera-that-sends-photos-to-your-phone-esp32-cam-one-weekend</link>
<description>&lt;p&gt;&lt;em&gt;&quot;I want photos of birds at my feeder sent to my phone.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A tiny camera board next to the feeder takes a photo when something moves and sends it to your phone. It's cheap and fun; the photos are decent, not wildlife-magazine sharp.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt; $15–30 · &lt;strong&gt;Time&lt;/strong&gt; one weekend · &lt;strong&gt;Level&lt;/strong&gt; some experience helps · &lt;strong&gt;Power&lt;/strong&gt; USB (solar is harder)&lt;/p&gt;
&lt;h3 id=&quot;what-to-buy&quot;&gt;What to buy&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Part &lt;/th&gt;&lt;th&gt;Why this one &lt;/th&gt;&lt;th&gt;Rough cost&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;ESP32-CAM with its USB programming board (ESP32-CAM-MB) &lt;/td&gt;&lt;td&gt;Camera and WiFi on one small board. The add-on board makes uploading code easy. &lt;/td&gt;&lt;td&gt;$8–15&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;PIR motion sensor (e.g. AM312) &lt;/td&gt;&lt;td&gt;Detects warm, moving bodies. Small and low-power. &lt;/td&gt;&lt;td&gt;$1–3&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5V USB power supply, 2 A &lt;/td&gt;&lt;td&gt;The camera and WiFi together draw a lot; weak supplies cause random resets. &lt;/td&gt;&lt;td&gt;$5&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Weatherproof box with a clear window &lt;/td&gt;&lt;td&gt;Keeps rain and condensation off the lens. &lt;/td&gt;&lt;td&gt;$5–10&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Prices are rough ranges, not quotes. Check the current price and stock before you order.&lt;/p&gt;
&lt;h3 id=&quot;what-to-install&quot;&gt;What to install&lt;/h3&gt;
&lt;p&gt;An Arduino sketch that wakes on motion, takes a photo and sends it through a Telegram bot (the UniversalTelegramBot library has ESP32-CAM examples). &lt;a rel=&quot;nofollow&quot; href=&quot;https://ntfy.sh&quot;&gt;ntfy&lt;/a&gt; can take photo attachments too.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Runner-up:&lt;/strong&gt; a Raspberry Pi with its camera module. It costs more and uses more power, but takes much better photos and can spot motion in the image itself.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;build-it-in-three-steps&quot;&gt;Build it in three steps&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Take a photo on your desk.&lt;/strong&gt; Flash the camera web-server example that comes with the ESP32 Arduino package, and view the picture in a browser.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Send one to your phone.&lt;/strong&gt; Set up the bot, and send a photo when you wave at the motion sensor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Move it outside.&lt;/strong&gt; Mount it 30–60 cm from the perch, facing away from the sun, and adjust from there.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;what-catches-people-out&quot;&gt;What catches people out&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Motion sensors miss small birds.&lt;/strong&gt; PIR sensors are made to spot people; a small bird a metre away may not trigger one. Mount the sensor close to the perch, or take a photo every few seconds and keep only the ones that changed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus.&lt;/strong&gt; The stock lens is usually focused further away. For close-ups it can be turned carefully to refocus (it's often held by a dab of glue), or move the camera back.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Random resets.&lt;/strong&gt; &quot;Brownout detector was triggered&quot; in the log means the power sagged. Use a good 5V supply and a short, thick cable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solar is a step up.&lt;/strong&gt; The camera draws a lot while it's awake. Get it working on USB before you try solar.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This guide first appeared on &lt;a rel=&quot;nofollow&quot; href=&quot;https://iotmentor.dev/guides/bird-feeder-camera&quot;&gt;IotMentor&lt;/a&gt;, a free chat mentor for small electronics builds (I make it). If your setup is different (distance, power, budget, experience), you can ask it there and it will plan your version.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33084/build-a-bird-feeder-camera-that-sends-photos-to-your-phone-esp32-cam-one-weekend</guid>
<pubDate>Fri, 30 Oct 2026 11:14:48 +0000</pubDate>
</item>
<item>
<title>Build a water-leak alarm for under the sink (ESP32, one evening)</title>
<link>https://coderlegion.com/33083/build-a-water-leak-alarm-for-under-the-sink-esp32-one-evening</link>
<description>&lt;p&gt;&lt;em&gt;&quot;How do I get warned before a leak under the sink floods the kitchen?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Water conducts a little electricity. Two metal contacts a few millimetres apart on the cupboard floor, a small board that checks them, a loud buzzer and a phone alert. The test is dramatic: pour a glass of water and it screams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cost&lt;/strong&gt; $6–12 · &lt;strong&gt;Time&lt;/strong&gt; one evening · &lt;strong&gt;Level&lt;/strong&gt; first build · &lt;strong&gt;Power&lt;/strong&gt; USB or battery&lt;/p&gt;
&lt;h3 id=&quot;what-to-buy-1&quot;&gt;What to buy&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Part &lt;/th&gt;&lt;th&gt;Why this one &lt;/th&gt;&lt;th&gt;Rough cost&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;ESP32-C3 board &lt;/td&gt;&lt;td&gt;Small, cheap, WiFi built in. &lt;/td&gt;&lt;td&gt;$4–7&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Leak probe: two wires or two screws, a few mm apart &lt;/td&gt;&lt;td&gt;Or a cheap &quot;rain sensor&quot; board with a comparator, which gives a simple wet/dry signal. &lt;/td&gt;&lt;td&gt;$0–2&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Active buzzer &lt;/td&gt;&lt;td&gt;Beeps by itself when powered, so no tone code is needed. It still warns you when your phone is off. &lt;/td&gt;&lt;td&gt;$1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;A 100 kΩ resistor &lt;/td&gt;&lt;td&gt;For reading the probe (see step 1). &lt;/td&gt;&lt;td&gt;$0.10&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;USB power (there's often a socket under the sink) or batteries &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Prices are rough ranges, not quotes. Check the current price and stock before you order.&lt;/p&gt;
&lt;h3 id=&quot;what-to-install-1&quot;&gt;What to install&lt;/h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://esphome.io&quot;&gt;ESPHome&lt;/a&gt; or a short Arduino sketch: check the probe, sound the buzzer when it's wet, and send a phone alert through &lt;a rel=&quot;nofollow&quot; href=&quot;https://ntfy.sh&quot;&gt;ntfy&lt;/a&gt; or Home Assistant. Repeat the alert every few minutes until it's dry; a leak is urgent.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Ready-made option:&lt;/strong&gt; leak sensors for Zigbee or WiFi cost about $10–20. Build your own to learn, or to get a loud buzzer right at the sink.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;build-it-in-three-steps-1&quot;&gt;Build it in three steps&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Test with a glass of water.&lt;/strong&gt; Connect one probe to ground. Connect the other to an analog pin, and through the 100 kΩ resistor to a second pin that you switch on only while measuring. Print the reading dry, and with the probe in water: wet should read clearly lower.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add the buzzer and the alert.&lt;/strong&gt; Drive the buzzer from a pin (through a small transistor if it needs more current than a pin can give). Alert once when it turns wet, then every few minutes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Place it.&lt;/strong&gt; Probes flat on the lowest point of the cupboard floor; board and buzzer higher up, where they stay dry.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;what-catches-people-out-1&quot;&gt;What catches people out&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Probes corrode if they're powered all the time.&lt;/strong&gt; Constant current through wet metal eats it away. Power the probe only for the moment you measure, which is why the resistor goes to a pin, not straight to 3.3V.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Damp isn't a leak.&lt;/strong&gt; Set the alert point from your own test, well away from the dry reading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Battery version:&lt;/strong&gt; wake every 30–60 seconds, check, sleep. A leak takes minutes to do damage, so that's fast enough.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low voltage only.&lt;/strong&gt; USB or batteries; never run mains wiring near the water.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This guide first appeared on &lt;a rel=&quot;nofollow&quot; href=&quot;https://iotmentor.dev/guides/water-leak-alarm&quot;&gt;IotMentor&lt;/a&gt;, a free chat mentor for small electronics builds (I make it). If your setup is different (distance, power, budget, experience), you can ask it there and it will plan your version.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33083/build-a-water-leak-alarm-for-under-the-sink-esp32-one-evening</guid>
<pubDate>Fri, 30 Oct 2026 11:14:12 +0000</pubDate>
</item>
<item>
<title>Exploiting &quot;Trust-by-Default&quot;: How I transformed an ESP32-S3 into a BadUSB</title>
<link>https://coderlegion.com/26983/exploiting-trust-by-default-how-i-transformed-an-esp32-s3-into-a-badusb</link>
<description>&lt;p&gt;The first tool I decided to arm my Swiss army knife with was a BadUSB emulator. First and foremost, this extremely powerful tool (provided you have physical access to the target machine) relies on an inherent vulnerability of the USB protocol (born in 1996), originally designed to make connecting peripherals as simple and invisible as possible.&lt;/p&gt;
&lt;p&gt;Practically, when we plug a USB device into a computer, a rigorous process called &lt;em&gt;Enumeration&lt;/em&gt; begins. This is divided into several phases: the device is physically connected, its communication speed is identified, and immediately after, the device sends its Descriptors. These descriptors are a set of vital information that defines how the host machine will communicate with the device; subsequently, the device is reset, an address is assigned to it, the interfaces are loaded, and finally, the drivers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(If you want to delve deeper into the enumeration phase, I recommend &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.totalphase.com/blog/2020/08/what-is-enumeration-why-usb-descriptors-important/&quot;&gt;this article by TotalPhase&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Having defined this, it's easy to guess that if we alter the descriptors via software, we can disguise an ordinary flash drive (or in our case, an M5Stick) making it appear as a keyboard or a mouse.&lt;/p&gt;
&lt;p&gt;And this is where the &quot;Trust-by-Default&quot; vulnerability comes into play: all operating systems blindly trust an HID (Human Interface Device) like a keyboard, as it is an essential input tool. Generic drivers are already pre-installed in the kernel (zero incompatibilities or warning popups) and, moreover, antiviruses have no say in the matter, since they are not designed to scan hardware inputs sent via electrical impulses.&lt;/p&gt;
&lt;h4 id=&quot;the-architectural-choice-dynamic-parser-vs-hardcoding&quot;&gt;The architectural choice: Dynamic Parser vs Hardcoding&lt;/h4&gt;
&lt;p&gt;Initially, for &quot;simplicity&quot;, I thought about hardcoding the payloads directly in the C source code. But thinking about it carefully, the use cases in pentesting are infinite: the end-user must have the ability to modify scripts on the fly, without having to recompile or flash the ESP32 firmware every time.&lt;/p&gt;
&lt;p&gt;For this reason, I decided to implement a dynamic Parser for DuckyScript. This allows the user to upload their own plain text scripts (&lt;code&gt;.txt&lt;/code&gt;) onto the device's LittleFS memory and have the microcontroller interpret them on the fly.&lt;/p&gt;
&lt;p&gt;For those unfamiliar with it, DuckyScript is a macro language created by Hak5 that abstracts interaction with the victim machine. It's simple and easily readable.&lt;br&gt;
To better explain what I mean by &quot;abstract&quot;: if I were to write a payload directly in low-level C or Bash, I would have to carefully calculate every single delay between one command and the next to give the PC time to open windows, and I would have to manually map the hexadecimal codes (HID Keycodes) of every single key to &quot;press&quot;. DuckyScript, combined with my parser, automates all of this.&lt;/p&gt;
&lt;p&gt;Originally, to use Rubber Ducky scripts, they had to be transformed from simple text files into binary files; this conversion was performed by a PC tool called Ducky Encoder, which converted them into &lt;code&gt;inject.bin&lt;/code&gt;. This tool was used because many older devices lacked the computational power to analyze text during runtime, a problem that the ESP32-S3 has now overcome.&lt;/p&gt;
&lt;p&gt;So I decided to integrate a native parser into my code: this way, a user can simply upload a file from their phone (via the dedicated internal HTML page) or from a computer and execute it immediately, without having to use an external tool for the conversion. Practically, this is done using standard C functions to separate the actual command from the argument at runtime:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;char *cmd = strtok(line, &quot; &quot;);
char *arg = strtok(NULL, &quot;&quot;);

if (strcmp(cmd, &quot;DELAY&quot;) == 0 &amp;amp;&amp;amp; arg) {
    int delay_ms = atoi(arg); 
    if (delay_ms &amp;gt; 0) { 
        vTaskDelay(pdMS_TO_TICKS(delay_ms)); 
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;ram-security-o-1-space-complexity&quot;&gt;RAM Security: O(1) Space Complexity&lt;/h4&gt;
&lt;p&gt;But we all know that C doesn't joke around when it comes to memory allocation, so I tried to make the script reading phase as secure as possible. The underlying problem is that if a user decided to use a very long script (for example, uploading an entire binary file encoded in text), the M5Stick S3 would risk running out of the memory designated for the SRAM, resulting in a Kernel Panic followed by a crash and a device reboot.&lt;/p&gt;
&lt;p&gt;To bypass this hardware limit, I thought of allocating a buffer that reads the uploaded file in small, controlled blocks with the help of the &lt;code&gt;fgets()&lt;/code&gt; function, which is memory-safe by nature. This mathematically prevents a Buffer Overflow from occurring: even if the user uploads a malicious or malformed file with a single line 10,000 characters long, &lt;code&gt;fgets&lt;/code&gt; will still only read the first 255 characters per cycle, safeguarding the microcontroller's stack.&lt;/p&gt;
&lt;p&gt;If we look at the reading process from a space-complexity perspective, you can see that the way I decided to implement this mechanic has a space complexity of O(1); whereas the classic implementation would have had a complexity of O(N), as it would have loaded the entire file into RAM simultaneously.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;// RAM consumption locked at only 256 bytes
char line[256];

// The file is strictly read and processed line by line
while (fgets(line, sizeof(line), f)) {
    trim_trailing(line); 
    if (strlen(line) == 0) continue;
    
    // ... start parsing for this specific line ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;beyond-abstraction-usb-protocol-and-polling-rate&quot;&gt;Beyond abstraction: USB Protocol and Polling Rate&lt;/h4&gt;
&lt;p&gt;Despite all the numerous advantages that come from using the ESP32-S3, one &quot;limit&quot; remains: not being able to use libraries that allow a high level of abstraction, such as Arduino's &lt;code&gt;Keyboard.h&lt;/code&gt;. This is a problem because, in the USB standard, a keyboard must regularly build and send 8-byte raw packets (divided into modifier mask, protocol byte, and key array) known as HID Reports.&lt;/p&gt;
&lt;p&gt;Sending these reports falls under the polling operation, which is a mechanism by which the Host (the PC) cyclically queries the USB device to check if there have been any state changes. Specifically, I handle these cyclic requests with &lt;code&gt;tud_hid_ready()&lt;/code&gt;, which checks if the USB Endpoint is free and if the PC has confirmed the reception of the previous packet. Through the &lt;code&gt;while (!tud_hid_ready()) vTaskDelay(...)&lt;/code&gt; block, I prevent the ESP32 from overwriting its own buffer before the packets have actually traveled across the cable. Without this blocking check, the keystrokes would be &quot;dropped&quot; (lost), and a DuckyScript command like &lt;code&gt;STRING powershell&lt;/code&gt; would be read by the victim PC as an incomprehensible &lt;code&gt;pwsel&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;state-based-logic-and-the-auto-repeat-trap&quot;&gt;State-Based Logic and the Auto-Repeat Trap&lt;/h4&gt;
&lt;p&gt;Added to this strict temporal &quot;dance&quot; of polling is one last huge engineering pitfall related to the nature of 8-byte reports. The most common cognitive error people run into is thinking that USB sends &quot;print commands&quot; (e.g., &quot;type the letter R&quot;). In reality, the USB architecture imposes a State-Based logic: the keyboard only communicates its electromechanical state to the PC at that precise moment.&lt;/p&gt;
&lt;p&gt;When we send a report with a specific key, we are not telling it to print it, but we are communicating: &quot;Right now, the user is physically pressing this key&quot;.&lt;/p&gt;
&lt;p&gt;The operating system's driver receives this state. If the microcontroller were to immediately move to the next command without doing anything else, the PC would think the user's finger was glued to the key, triggering the operating system's Auto-Repeat (which would endlessly open dozens of windows or print RRRRRRRR...). To solve this problem, my firmware is forced to close the cycle by always sending a subsequent &quot;zeroed&quot; HID Report (passing the &lt;code&gt;NULL&lt;/code&gt; array to TinyUSB). This tricks the PC, communicating that the key's physical contacts have finally been released (Key Release), allowing us to safely move on to the next letter.&lt;/p&gt;
&lt;h4 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Developing a low-level BadUSB emulator using &lt;code&gt;esp_tinyusb&lt;/code&gt; is an immensely more educational challenge compared to using &quot;pre-built&quot; libraries. It forces you to study the anatomy of USB packets, understand how operating systems handle polling, and deal with the physical limits of a microcontroller's memory.&lt;/p&gt;
&lt;p&gt;Now our digital &quot;Swiss Army Knife&quot; has its first real weapon, capable of uploading and launching payloads on the fly in total safety.&lt;/p&gt;
&lt;p&gt;But a multi-tool isn't just a keyboard. What if we used the ESP32's antenna to intercept the invisible data floating in the air? In the next article, I'll show you how I configured the network interface in promiscuous mode to turn the ESP32 into a Wi-Fi Sniffer, capable of capturing raw packets and saving them directly to the file system in &lt;code&gt;.pcap&lt;/code&gt; format, ready to be analyzed on Wireshark.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26983/exploiting-trust-by-default-how-i-transformed-an-esp32-s3-into-a-badusb</guid>
<pubDate>Sat, 12 Sep 2026 14:58:38 +0000</pubDate>
</item>
<item>
<title>From Arduino to ESP-IDF: The architecture behind my digital &quot;Swiss Army Knife&quot;</title>
<link>https://coderlegion.com/26982/from-arduino-to-esp-idf-the-architecture-behind-my-digital-swiss-army-knife</link>
<description>&lt;h3 id=&quot;1-why-build-another-multi-tool-&quot;&gt;1. Why build another multi-tool?&lt;/h3&gt;
&lt;p&gt;How many of you have often found yourselves wanting to buy a Flipper Zero? I thought about it many times, but there were always problems holding me back: stock is often limited, the price tag is quite high, and above all, you miss out on the thrill of building such a powerful tool literally from scratch.&lt;/p&gt;
&lt;p&gt;From these observations, my project was born: designing and developing a low-level &quot;Swiss Army Knife&quot;.&lt;/p&gt;
&lt;p&gt;It all started a few months ago. I was thinking about buying an M5Stick S3 after watching some videos online where people spoke very highly of it, especially for one major detail: unlike the Flipper, it has Wi-Fi and Bluetooth modules already built-in.&lt;/p&gt;
&lt;p&gt;Digging deeper, I quickly realized the advantages of the ESP32-S3 over the classic Arduino. The key features that convinced me were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dual-core processor: It opens the door to serious features, like managing firmware tasks separately.&lt;/li&gt;
&lt;li&gt;More RAM: It allows integrating very complex external libraries (like heavy graphical interfaces) without killing performance.&lt;/li&gt;
&lt;li&gt;Native USB HID: It allows emulating peripherals like keyboards or mice natively and quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, the hardware was decided. But why build a multi-tool?&lt;/p&gt;
&lt;p&gt;The main reason is to explore and understand the technical background of as many tools as possible. Lately, I feel there is a tendency to overlook the ingenuity of the mechanisms operating right in front of our eyes. We prefer having a ready-made tool, usable perhaps without even knowing the basics of computer science. I wanted to go in the opposite direction and understand exactly how these things work at the code level.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-fluid-graphics-and-multitasking-how-not-to-blow-up-an-esp32&quot;&gt;2. Fluid Graphics and Multitasking: How not to blow up an ESP32&lt;/h3&gt;
&lt;p&gt;A major problem when rendering a graphical interface on a microcontroller is that the CPU has to calculate and send every single pixel. Since this is a time-consuming operation, the entire device gets blocked until the whole interface is completely redrawn. In a multi-tool, if the ESP32 is stuck drawing buttons, it cannot sniff packets or execute attacks.&lt;/p&gt;
&lt;p&gt;To solve this problem and make the device truly responsive, I decided to use DMA (Direct Memory Access). DMA is a dedicated hardware component that allows peripherals (in this case the SPI display) to read data directly from RAM without involving the CPU, generating a single interrupt only when the transfer is complete.&lt;/p&gt;
&lt;p&gt;I initialized the SPI bus asking ESP-IDF to automatically assign a DMA channel:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;spi_bus_initialize(SPI2_HOST, &amp;amp;buscfg, SPI_DMA_CH_AUTO);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;the-ram-problem-and-chunk-rendering&quot;&gt;The RAM problem and Chunk Rendering&lt;/h4&gt;
&lt;p&gt;However, implementing DMA without the right precautions wouldn't have been enough. The device's screen resolution is 135x240 at 16 bits, which requires a total of 64.8 KB to be rendered. This leads to two major problems:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Wasting so many resources just for the interface is not sustainable for a microcontroller.&lt;/li&gt;
&lt;li&gt;It is virtually impossible to find 64.8 KB of contiguous memory inside the RAM once the system is running.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To bypass this issue, I decided to divide the screen into horizontal &quot;slices&quot; of 20 lines each (about 5.4 KB total). Using the advanced ESP-IDF APIs, I allocated this buffer in a memory area compatible with DMA:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;lv_color_t *buf1 = heap_caps_malloc(LCD_H_RES * 20 * sizeof(lv_color_t), MALLOC_CAP_DMA);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, we optimize RAM usage while still maintaining super fluid rendering.&lt;/p&gt;
&lt;p&gt;At this point, a question arises: how does LVGL (the library I use to manage the interface) know when the DMA has finished sending the bits for those 20 lines? Simply, with a Hardware Interrupt system that defines this flow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LVGL fills the buffer and calls the driver.&lt;/li&gt;
&lt;li&gt;The driver passes the buffer to the DMA controller.&lt;/li&gt;
&lt;li&gt;When the transmission process ends, the interrupt is triggered.&lt;/li&gt;
&lt;li&gt;The interrupt immediately calls a callback (&lt;code&gt;notify_lvgl_flush_ready&lt;/code&gt;) which orders LVGL to calculate the next slice of the interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;static bool notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) {
    lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
    lv_disp_flush_ready(disp_driver);
    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;semaphores-for-safety-thread-safety-&quot;&gt;Semaphores for Safety (Thread-Safety)&lt;/h4&gt;
&lt;p&gt;Since FreeRTOS is a system based on multi-threading, it is important to implement proper task management. A fundamental detail to know is that LVGL is not thread-safe by design. This means that the library has no internal mechanisms to protect itself: if two different tasks try to access or modify the graphical interface at the exact same moment, the system will crash.&lt;/p&gt;
&lt;p&gt;To prevent these race-conditions, which can lead to data inconsistency and deadlocks, I implemented a Semaphore (Mutex) system: every time an external task (other than the graphical one) wants to modify a GUI element, it must first &quot;take&quot; the semaphore.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;lvgl_mux = xSemaphoreCreateMutex();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the graphical task is already working, the network task (for example, the WiFi sniffer) waits in queue for a few milliseconds until the Mutex is released. This guarantees that LVGL's memory is read or written by only one thread at a time, making the firmware solid and crash-proof.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-data-security-and-crash-prevention&quot;&gt;3. Data Security and Crash Prevention&lt;/h3&gt;
&lt;p&gt;Having a responsive interface and fast loading times is not enough: we also must try to make the firmware as robust as possible against hardware failures.&lt;/p&gt;
&lt;p&gt;This justifies the choice of the file system: LittleFS. Unlike the classic FAT32 or SPIFFS (which would corrupt the disk leading to the loss of all data in case of sudden shutdown), LittleFS was specifically designed for battery-powered microcontrollers and can maintain its integrity even if the power is suddenly cut off.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;esp_vfs_littlefs_conf_t conf = {
    .base_path=&quot;/memoria&quot;,
    .partition_label=&quot;storage&quot;,
    .format_if_mount_failed = true, 
    .dont_mount = false,
};
esp_err_t ret = esp_vfs_littlefs_register(&amp;amp;conf);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A great engineering practice is using the &lt;code&gt;.format_if_mount_failed = true&lt;/code&gt; parameter, which allows handling the very first boot of the firmware on an empty memory by simply formatting it entirely on its own, without the user having to do anything.&lt;/p&gt;
&lt;h4 id=&quot;how-to-read-giant-payloads-without-running-out-of-ram&quot;&gt;How to read Giant Payloads without running out of RAM&lt;/h4&gt;
&lt;p&gt;The second major risk for stability is reading files. Many microcontrollers have very limited RAM that fills up in an instant if you try to open a file of several Megabytes all at once.&lt;/p&gt;
&lt;p&gt;To prevent the BadUSB tool from going into Out of Memory when executing complex payloads, I decided to implement a &quot;line-by-line&quot; reading. Think of it a bit like an e-reader: it doesn't load the whole book into memory, but only shows the small part of text you are reading, allowing you to scroll through the pages interacting with the interface.&lt;/p&gt;
&lt;p&gt;My parser dynamically loads only 256 bytes at a time:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;bool esegui_duckyscript(const char *path) {
    FILE *f = fopen(path, &quot;r&quot;);
    if (!f) return false;

    char line[256];

    // RAM consumption is capped at 256 bytes, regardless 
    // of the size of the file loaded by the user!
    while (fgets(line, sizeof(line), f)) {
        trim_trailing(line);
        if (strlen(line) == 0) continue;

        char *cmd = strtok(line, &quot; &quot;);
        // parsing and execution of the single command...
    }
    fclose(f);
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This is how I structured my project to guarantee its stability, responsiveness, and scalability, especially considering the large number of tools I am going to implement.&lt;/p&gt;
&lt;p&gt;In the next episode, we will get to the heart of the action: I will show you how I used the USB stack to make the computer believe that the ESP32 is a keyboard, in order to execute DuckyScript scripts (BadUSB).&lt;/p&gt;
&lt;p&gt;In the meantime, which pentesting tool would you like to see added to the project? Let me know in the comments!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26982/from-arduino-to-esp-idf-the-architecture-behind-my-digital-swiss-army-knife</guid>
<pubDate>Sat, 12 Sep 2026 14:58:13 +0000</pubDate>
</item>
<item>
<title>ESP32 ate the maker world - and audio projects beat smart home (May 2026 data)</title>
<link>https://coderlegion.com/18953/esp32-ate-the-maker-world-and-audio-projects-beat-smart-home-may-2026-data</link>
<description>&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z4d8axqnmvqwzi8cmvya.jpeg&quot; alt=&quot;State of Maker Projects - May 2026&quot;&gt;&lt;/p&gt;
&lt;p&gt;We pulled the numbers on &lt;strong&gt;662 maker projects from 367 makers&lt;/strong&gt; over the last three months. A few of the results genuinely surprised us - and most of them contradict what the hardware press keeps repeating.&lt;/p&gt;
&lt;h3 id=&quot;three-findings-worth-your-attention&quot;&gt;Three findings worth your attention&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. ESP32 isn't winning. It already won.&lt;/strong&gt;&lt;br&gt;
ESP32-family chips show up in &lt;strong&gt;59.8%&lt;/strong&gt; of new projects. Arduino AVR (mostly the Nano) holds &lt;strong&gt;21.5%&lt;/strong&gt;, almost entirely in non-connected builds — workshop tools, lighting, art. The &quot;Arduino + Wi-Fi shield&quot; pattern? Effectively zero. Dead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Raspberry Pi Pico flopped on the workbench.&lt;/strong&gt;&lt;br&gt;
Just &lt;strong&gt;0.6%&lt;/strong&gt; — 4 projects out of 662. Despite years of marketing push from Adafruit, Pimoroni and friends, makers aren't reaching for it when they actually sit down to build.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. The #1 project category is &lt;em&gt;audio&lt;/em&gt;, not smart home.&lt;/strong&gt;&lt;br&gt;
Audio and music projects (guitar tuners, visualizers, synths, effects) hit &lt;strong&gt;10.3%&lt;/strong&gt; of all originals - beating every &quot;IoT&quot; or &quot;smart home&quot; subcategory. The tech press narrative doesn't match what people are actually soldering.&lt;/p&gt;
&lt;h3 id=&quot;bonus-signal-offline-ai-is-quietly-happening&quot;&gt;Bonus signal: offline AI is quietly happening&lt;/h3&gt;
&lt;p&gt;Six independent makers shipped &lt;strong&gt;offline AI inference nodes&lt;/strong&gt; in two months. No coordination, no hype cycle - just people putting models on their own hardware. Worth watching.&lt;/p&gt;
&lt;h3 id=&quot;kit-economics&quot;&gt;Kit economics&lt;/h3&gt;
&lt;p&gt;Median project BOM: &lt;strong&gt;$139&lt;/strong&gt;. There's a sharp drop-off above $200 - a real ceiling for hobby builds, useful to know if you're designing a kit or product around this audience.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This is the short version. The full post has the methodology, all the data tables, the kit-cost distribution, and a downloadable dataset if you want to run your own cuts.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;Read the full breakdown:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://make-it.ai/blog/state-of-maker-projects-may-2026&quot;&gt;State of Maker Projects, May 2026&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What's your default MCU in 2026? Drop it in the comments - curious whether the ESP32 dominance holds in this audience too.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18953/esp32-ate-the-maker-world-and-audio-projects-beat-smart-home-may-2026-data</guid>
<pubDate>Tue, 26 May 2026 05:46:55 +0000</pubDate>
</item>
</channel>
</rss>