<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tutorials Archives | Teach Me Microcontrollers!</title>
	<atom:link href="https://www.teachmemicro.com/category/tutorials/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.teachmemicro.com/category/tutorials/</link>
	<description>Microcontroller Tutorials and Resources</description>
	<lastBuildDate>Wed, 21 Oct 2026 23:53:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://www.teachmemicro.com/wp-content/uploads/2019/04/blue-icon-65x65.png</url>
	<title>Tutorials Archives | Teach Me Microcontrollers!</title>
	<link>https://www.teachmemicro.com/category/tutorials/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>DFRobot FireBeetle 2 ESP32-UE: What Makes This ESP32 Board Different?</title>
		<link>https://www.teachmemicro.com/dfrobot-firebeetle-2-esp32-ue-what-makes-this-esp32-board-different/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 20 Oct 2026 23:24:33 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=13022</guid>

					<description><![CDATA[<p>Most ESP32 development boards are designed for prototyping. Connect a USB cable, upload a sketch, and start experimenting. But when you want to put an ESP32 in an enclosure or run it from a battery, the board's design becomes as important as the microcontroller itself. The DFRobot FireBeetle 2 ESP32-UE addresses several of those practical &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/dfrobot-firebeetle-2-esp32-ue-what-makes-this-esp32-board-different/">DFRobot FireBeetle 2 ESP32-UE: What Makes This ESP32 Board Different?</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Most ESP32 development boards are designed for prototyping. Connect a USB cable, upload a sketch, and start experimenting. But when you want to put an ESP32 in an enclosure or run it from a battery, the board's design becomes as important as the microcontroller itself.</p>
<p>The <strong>DFRobot FireBeetle 2 ESP32-UE</strong> addresses several of those practical requirements. It has an external antenna connector, built-in Li-ion battery charging, low-power hardware, 16 MB flash, and 2 MB PSRAM. It also includes a connector for compatible displays.</p>
<p>In this article, we'll cover what these features do, set up the board in Arduino IDE, and show how to check its Wi-Fi signal strength and sleep current.</p>
<p><span id="more-13022"></span></p>
<p><img data-dominant-color="dbdad9" data-has-transparency="false" style="--dominant-color: #dbdad9;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13125 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Development-Board-with-External-Antenna.avif" alt="ESP32 Development Board with External Antenna" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Development-Board-with-External-Antenna.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Development-Board-with-External-Antenna-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<h2><strong>What Is the FireBeetle 2 ESP32-UE?</strong></h2>
<p>The FireBeetle 2 ESP32-UE uses an <strong>ESP32-WROOM-32UE-N16R2</strong> module. This is based on the original dual-core ESP32, not a newer ESP32-S3 or ESP32-C6. You still get the familiar Wi-Fi, Bluetooth, PWM, ADC, DAC, touch sensing, UART, SPI, and I2C features.</p>
<p>Here are the main specifications:</p>
<ul>
<li>Dual-core Xtensa LX6 processor, up to 240 MHz</li>
<li>16 MB external flash and 2 MB PSRAM</li>
<li>520 KB internal SRAM</li>
<li>2.4 GHz Wi-Fi (802.11 b/g/n)</li>
<li>Bluetooth 4.2 Classic (BR/EDR) and BLE</li>
<li>USB-C for programming and power</li>
<li>Single-cell Li-ion battery connector and onboard charging</li>
<li>GDI display connector and onboard LEDs</li>
<li>Board dimensions: 25.4 × 60 mm</li>
</ul>
<p>DFRobot lists 17 digital pins and 11 analog-capable pins among the board's interfaces. Check the exact pins and their restrictions before wiring a project.</p>
<p><img data-dominant-color="b2b7b8" data-has-transparency="false" style="--dominant-color: #b2b7b8;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13137 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-Annotated-01-scaled.avif" alt="ESP32-UE Annotated" width="2212" height="2560" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-Annotated-01-scaled.avif 2212w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-Annotated-01-259x300.avif 259w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-Annotated-01-885x1024.avif 885w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-Annotated-01-768x889.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-Annotated-01-1327x1536.avif 1327w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-Annotated-01-1769x2048.avif 1769w" sizes="auto, (max-width: 2212px) 100vw, 2212px" /></p>
<p>The processor is familiar. The differences are mostly in how DFRobot has packaged it for projects that need to work away from a desk.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Why Use an External Antenna?</strong></h2>
<p>If you've used a regular ESP32-WROOM module, you've probably seen the exposed PCB antenna at one end. The ESP32-WROOM-32UE replaces that antenna with a small RF connector for an external 2.4 GHz Wi-Fi/Bluetooth antenna.</p>
<p>This matters when the ESP32 sits inside an enclosure. A nearby battery, display, ground plane, wiring, or metal surface can affect antenna performance. An external antenna lets you choose a better location without moving the entire circuit board.</p>
<p>Imagine an outdoor environmental monitor. The board and sensors sit inside a weather-resistant enclosure, while the antenna can be placed away from the crowded electronics or outside the enclosure using suitable weatherproof hardware.</p>
<p><img data-dominant-color="b4b1ae" data-has-transparency="false" style="--dominant-color: #b4b1ae;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13127 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Antenna-Setup-Comparison.avif" alt="ESP32 Antenna Setup Comparison" width="881" height="376" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Antenna-Setup-Comparison.avif 881w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Antenna-Setup-Comparison-300x128.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Antenna-Setup-Comparison-768x328.avif 768w" sizes="auto, (max-width: 881px) 100vw, 881px" /></p>
<p>That does not automatically make the FireBeetle a long-range ESP32. Signal strength also depends on antenna gain, orientation, cable loss, obstacles, interference, and the access point. An external antenna gives you <strong>more control over placement</strong>, not a guaranteed increase in range. We'll return to a simple Wi-Fi comparison after setting up the board.</p>
<div class="box info  "><div class="box-inner-block"><i class="fa tie-shortcode-boxicon"></i>
			Use a compatible 2.4 GHz antenna and connector assembly. Attach the antenna before performing wireless tests.
			</div></div>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Battery Power Without a Separate Charger Board</strong></h2>
<p>A better antenna location solves one problem for a remote sensor. The next is power.</p>
<p>The FireBeetle has a PH2.0 connector for a <strong>single-cell 3.7 V nominal Li-ion/LiPo battery</strong>. A fully charged cell can reach about 4.2 V. The board also includes charging circuitry, so you can connect USB power to charge the attached battery while you develop or service the device.</p>
<p>On a typical ESP32 prototype, you might need a separate battery charger and a suitable regulator. The FireBeetle already includes much of that hardware, which can reduce the number of extra modules and wires.</p>
<p><img data-dominant-color="cecac6" data-has-transparency="false" style="--dominant-color: #cecac6;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13128 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Board-and-LiPo-Battery-Flat-Lay.avif" alt="ESP32 Board and LiPo Battery Flat Lay" width="768" height="512" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Board-and-LiPo-Battery-Flat-Lay.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-Board-and-LiPo-Battery-Flat-Lay-300x200.avif 300w" sizes="auto, (max-width: 768px) 100vw, 768px" /></p>
<p>Before connecting a battery, check the connector polarity against the board documentation. PH2.0 connectors on different products do not always use the same wiring order. Use a suitable protected cell or battery pack and follow the manufacturer's charging and handling requirements.</p>
<p>If you're building a solar-powered device, you still need a charging and power-management arrangement suitable for the solar panel and battery. The battery connector alone is not a solar charge controller.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>How Low Can the FireBeetle Go?</strong></h2>
<p>Battery power becomes much more useful when the ESP32 can sleep between readings.</p>
<p>The ESP32 itself supports deep sleep, but a development board contains more than the ESP32. Its regulator, USB interface, LEDs, and other circuits may continue drawing current even while the processor sleeps.</p>
<p>DFRobot includes a <strong>low-power solder jumper</strong>, connected by default. According to DFRobot, disconnecting it reduces static consumption by approximately 500 µA, and the board can reach about <strong>13 µA in deep sleep</strong> under the manufacturer's stated conditions. Treat that number as a published specification rather than a measured result for your complete project.</p>
<p><img data-dominant-color="3a3634" data-has-transparency="false" style="--dominant-color: #3a3634;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13138 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/low-power-jumper-esp32-ue.avif" alt="Low-power jumper" width="869" height="1159" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/low-power-jumper-esp32-ue.avif 869w, https://www.teachmemicro.com/wp-content/uploads/2026/10/low-power-jumper-esp32-ue-225x300.avif 225w, https://www.teachmemicro.com/wp-content/uploads/2026/10/low-power-jumper-esp32-ue-768x1024.avif 768w" sizes="auto, (max-width: 869px) 100vw, 869px" /></p>
<p>There is a tradeoff. With the jumper disconnected, DFRobot says the onboard RGB LED can only be driven when USB power is present. If you don't need that LED in a battery-powered design, the reduced standby current may be worthwhile.</p>
<p>We'll use a short deep-sleep sketch later and measure current from the battery connection. The actual result will also depend on sensors, displays, pull-ups, and other connected hardware.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What Do 16 MB Flash and 2 MB PSRAM Add?</strong></h2>
<p>The ESP32-UE module carries <strong>16 MB flash</strong>, compared with the 4 MB found on many basic ESP32 development boards. That extra storage can make room for larger firmware images, OTA update partitions, web pages, fonts, and other files.</p>
<p>The amount available to a sketch or filesystem still depends on the selected flash-size settings and partition scheme. A 16 MB chip doesn't mean one Arduino sketch can automatically use all 16 MB.</p>
<p>PSRAM serves a different purpose. It adds 2 MB of external working memory, which can help when an application needs large buffers, such as a graphical interface, web response data, or image content. PSRAM is slower than internal RAM and is not a substitute for it in every application.</p>
<p>Also, <strong>having PSRAM on the module doesn't mean every allocation automatically uses it</strong>. The selected firmware configuration must initialize PSRAM, and applications may need PSRAM-aware allocations or libraries. We'll check shortly whether Arduino detects it.</p>
<h3><strong>The GDI Display Connector</strong></h3>
<p>The board also has DFRobot's GDI display interface. It uses an 18-pin FPC connection for compatible displays, helping reduce the jumper-wire clutter found in some display prototypes.</p>
<p><img data-dominant-color="abafb0" data-has-transparency="false" style="--dominant-color: #abafb0;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13129 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-GDI-Display.avif" alt="ESP32-UE GDI Display" width="673" height="570" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-GDI-Display.avif 673w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-GDI-Display-300x254.avif 300w" sizes="auto, (max-width: 673px) 100vw, 673px" /></p>
<p>For example, a sensor node could read temperature and humidity, update a local dashboard, and send the same measurements over Wi-Fi:</p>
<div class="tmm-flow-container tmm-flow-container--branched" role="img" aria-label="Flow diagram: Sensors to FireBeetle ESP32-UE; FireBeetle ESP32-UE to Compatible GDI Display; FireBeetle ESP32-UE to Wi-Fi; Wi-Fi to MQTT / Web Server."><div class="tmm-flow-canvas" data-tmm-flow-graph style="--tmm-flow-columns:4"><svg class="tmm-flow-lines" aria-hidden="true" focusable="false"><defs><marker id="tmm-flow-arrowhead-1" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto-start-reverse" markerUnits="strokeWidth"><path d="M0,0 L8,4 L0,8 Z"></path></marker></defs></svg><div class="tmm-flow-grid"><span class="tmm-flow-node" data-tmm-flow-node="0" style="--tmm-flow-column:1;--tmm-flow-row:1;--tmm-flow-order:1">Sensors</span><span class="tmm-flow-node" data-tmm-flow-node="1" style="--tmm-flow-column:2;--tmm-flow-row:1;--tmm-flow-order:2">FireBeetle ESP32-UE</span><span class="tmm-flow-node" data-tmm-flow-node="2" style="--tmm-flow-column:3;--tmm-flow-row:1;--tmm-flow-order:3">Compatible GDI Display</span><span class="tmm-flow-node" data-tmm-flow-node="3" style="--tmm-flow-column:3;--tmm-flow-row:2;--tmm-flow-order:4">Wi-Fi</span><span class="tmm-flow-node" data-tmm-flow-node="4" style="--tmm-flow-column:4;--tmm-flow-row:1;--tmm-flow-order:5">MQTT / Web Server</span></div><div class="tmm-flow-relations"><span class="tmm-flow-relation" data-tmm-flow-from="0" data-tmm-flow-to="1" data-tmm-flow-label="" data-tmm-flow-bidirectional="0">Sensors <span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span> FireBeetle ESP32-UE</span><span class="tmm-flow-relation" data-tmm-flow-from="1" data-tmm-flow-to="2" data-tmm-flow-label="" data-tmm-flow-bidirectional="0">FireBeetle ESP32-UE <span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span> Compatible GDI Display</span><span class="tmm-flow-relation" data-tmm-flow-from="1" data-tmm-flow-to="3" data-tmm-flow-label="" data-tmm-flow-bidirectional="0">FireBeetle ESP32-UE <span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span> Wi-Fi</span><span class="tmm-flow-relation" data-tmm-flow-from="3" data-tmm-flow-to="4" data-tmm-flow-label="" data-tmm-flow-bidirectional="0">Wi-Fi <span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span> MQTT / Web Server</span></div><span class="tmm-flow-marker-id" data-tmm-flow-marker="tmm-flow-arrowhead-1"></span></div></div>
<p>This is one reason the extra flash and PSRAM are useful together. They leave more room for assets and display buffers in projects using graphical libraries such as LVGL. Make sure the specific display and its library support the FireBeetle's GDI pin mapping; GDI isn't a universal connector for all displays.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Understanding the FireBeetle ESP32-UE Pinout</strong></h2>
<p>The pinout is mostly familiar, but DFRobot uses Arduino-style aliases alongside the underlying ESP32 GPIO numbers.</p>
<p>For example, the onboard indicator LED is on <strong>GPIO2</strong>, which DFRobot labels <strong>D9</strong> in its FireBeetle Arduino examples. The onboard WS2812 RGB LED uses GPIO5/D8, while the user button uses GPIO27/D4.</p>
<p><img data-dominant-color="d1d7d0" data-has-transparency="false" style="--dominant-color: #d1d7d0;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13130 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-pinout.avif" alt="ESP32-UE pinout diagram" width="1077" height="717" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-pinout.avif 1077w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-pinout-300x200.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-pinout-1024x682.avif 1024w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-pinout-768x511.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/10/ESP32-UE-pinout-310x205.avif 310w" sizes="auto, (max-width: 1077px) 100vw, 1077px" /></p>
<p>Check the actual GPIO number when adapting a generic ESP32 example. Some pins have boot-strapping functions, connect to onboard components, or are reserved for module memory or programming. The module's flash/PSRAM connections also mean you should not assume every GPIO number mentioned in a generic ESP32 tutorial is available for general use.</p>
<p>For analog sensors, one restriction is especially important. The original <span style="box-sizing: border-box; margin: 0px; padding: 0px;">ESP32 shares <strong>ADC2</strong></span><strong> with Wi-Fi</strong>. If your project needs Wi-Fi and analog readings at the same time, use an available ADC1 pin instead. On this board, <strong>GPIO34 and GPIO35</strong> are useful ADC1 options. Both are input-only and have no internal pull-up or pull-down resistors.</p>
<p>That small pin choice can save a lot of debugging in a Wi-Fi sensor project.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Getting Started with Arduino IDE</strong></h2>
<p>You can program the ESP32-UE with Arduino IDE, ESP-IDF, or MicroPython. We'll use Arduino here because the first tests need only a few lines of code.</p>
<p>To set up the board in Arduino IDE:</p>
<ol>
<li>Install the <strong>esp32 by Espressif Systems</strong> boards package through Boards Manager. If it isn't listed, add <code>https://espressif.github.io/arduino-esp32/package_esp32_index.json</code> under Additional Boards Manager URLs.</li>
<li>Go to <strong>Tools &gt; Board &gt; ESP32 Arduino &gt; FireBeetle ESP32-E</strong>. This is the selection shown in <a href="https://wiki.dfrobot.com/dfr1140/docs/23452">DFRobot's ESP32-UE getting-started guide</a>, despite the board itself being the UE antenna variant.</li>
<li>Connect the board with a USB-C data cable and select the correct serial port.</li>
<li>Upload the blink sketch below.</li>
</ol>
<pre><code>const int LED_PIN = D9;  // FireBeetle alias for GPIO2

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_PIN, HIGH);
  delay(500);
  digitalWrite(LED_PIN, LOW);
  delay(500);
}</code></pre>
<p>The small onboard LED should turn on for half a second and off for half a second. It's a useful first check that the board, USB connection, and Arduino settings are working.</p>
<h3><strong>Checking PSRAM</strong></h3>
<p>Next, upload this sketch and open Serial Monitor at 115200 baud:</p>
<pre><code>void setup() {
  Serial.begin(115200);
  delay(1000);

  if (psramFound()) {
    Serial.println("PSRAM detected");
    Serial.print("Total PSRAM: ");
    Serial.println(ESP.getPsramSize());
    Serial.print("Free PSRAM: ");
    Serial.println(ESP.getFreePsram());
  } else {
    Serial.println("PSRAM not detected");
  }
}

void loop() {
}</code></pre>
<p>A correctly initialized N16R2 module should report about 2 MB of total PSRAM, with some memory already in use.</p>
<figure id="attachment_13131" aria-describedby="caption-attachment-13131" style="width: 768px" class="wp-caption aligncenter"><img data-dominant-color="eaeef3" data-has-transparency="false" style="--dominant-color: #eaeef3;" loading="lazy" decoding="async" class="wp-image-13131 size-full not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon.avif" alt="" width="768" height="512" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon-300x200.avif 300w" sizes="auto, (max-width: 768px) 100vw, 768px" /><figcaption id="caption-attachment-13131" class="wp-caption-text">[Actual Arduino Serial Monitor output showing PSRAM detection on the FireBeetle ESP32-UE]</figcaption></figure>If you get "PSRAM not detected," check the board package, selected board, and PSRAM option under Tools if the installed core exposes one. PSRAM support depends on the build configuration, so the message alone does not prove the hardware lacks memory.</p>
<h3><strong>Connecting to Wi-Fi and Reading RSSI</strong></h3>
<p>Now for a test that relates directly to the external antenna. This sketch connects to a 2.4 GHz network and prints the received signal strength indicator (RSSI) every second.</p>
<pre><code>#include &lt;WiFi.h&gt;

const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  unsigned long start = millis();
  while (WiFi.status() != WL_CONNECTED &amp;&amp;
         millis() - start &lt; 15000) {
    delay(500);
    Serial.print(".");
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWi-Fi connected");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println("\nWi-Fi connection timed out");
  }
}

void loop() {
  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("RSSI: ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
  } else {
    Serial.println("Wi-Fi disconnected");
  }
  delay(1000);
}</code></pre>
<p>RSSI is expressed in dBm. -45 dBm indicates a stronger received signal than -80 dBm. The number will fluctuate as the radio environment changes.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Comparing Wi-Fi Signal Strength</strong></h2>
<p>To see what antenna placement changes in practice, compare the FireBeetle against a regular ESP32 board with a PCB antenna. Use the same access point, 2.4 GHz network, and test locations. Keep board and antenna orientation consistent, collect at least ten readings at each point, and calculate the average.</p>
<p>Record the external antenna model and any RF cable used. If possible, repeat the test with the FireBeetle antenna inside and outside the enclosure. That gives a more useful comparison than assuming an external antenna is always better.</p>
<figure id="attachment_13131-2" aria-describedby="caption-attachment-13131-2" style="width: 768px" class="wp-caption aligncenter"><img data-dominant-color="eaeef3" data-has-transparency="false" style="--dominant-color: #eaeef3;" loading="lazy" decoding="async" class="size-full wp-image-13131 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon.avif" alt="" width="768" height="512" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon-300x200.avif 300w" sizes="auto, (max-width: 768px) 100vw, 768px" /><figcaption id="caption-attachment-13131-2" class="wp-caption-text">[Photograph of the actual Wi-Fi comparison setup showing both ESP32 boards and the antenna placement]</figcaption></figure>Remember that RSSI measures how strongly the ESP32 receives the access point's signal. It does not measure both directions of the Wi-Fi link or prove the maximum communication range. Still, it's a practical first test of how the two antenna arrangements behave at your chosen locations.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Measuring Deep-Sleep Current</strong></h2>
<p>The other feature worth checking is power consumption. For a simple test, we can make the ESP32 sleep for 60 seconds, wake up, and then go back to sleep.</p>
<pre><code>#include "esp_sleep.h"

void setup() {
  Serial.begin(115200);
  delay(500);

  esp_sleep_enable_timer_wakeup(60ULL * 1000000ULL);
  Serial.println("Entering deep sleep for 60 seconds");
  Serial.flush();
  esp_deep_sleep_start();
}

void loop() {
}</code></pre>
<p>Use the Wi-Fi sketch from the previous section to record current while connected, then upload the deep-sleep sketch for the two sleep measurements. For current measurements, disconnect USB power and measure at the battery supply with a meter that can resolve microamp-level current. Keep the antenna connected but remove unnecessary external loads, such as a display or sensors, for the initial board-only comparison.</p>
<p>Measure with the low-power jumper intact, then repeat after disconnecting it, following DFRobot's instructions. Power the board down before changing the jumper, and note the meter and battery supply used. Wi-Fi active current changes rapidly, so a single instantaneous reading does not represent all operating conditions.</p>
<figure id="attachment_13131-3" aria-describedby="caption-attachment-13131-3" style="width: 768px" class="wp-caption aligncenter"><img data-dominant-color="eaeef3" data-has-transparency="false" style="--dominant-color: #eaeef3;" loading="lazy" decoding="async" class="size-full wp-image-13131 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon.avif" alt="" width="768" height="512" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/10/Image-in-Progress-Gear-Icon-300x200.avif 300w" sizes="auto, (max-width: 768px) 100vw, 768px" /><figcaption id="caption-attachment-13131-3" class="wp-caption-text">[Actual microamp-capable current meter connected at the FireBeetle battery supply during deep sleep]</figcaption></figure>For an environmental monitor that wakes only every few minutes, sleep current is important. But total battery life also depends on wake duration, Wi-Fi reconnection time, sensor consumption, and how often you transmit readings.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Is the FireBeetle 2 ESP32-UE Worth Choosing?</strong></h2>
<p>I wouldn't buy the FireBeetle just to blink LEDs or learn basic ESP32 programming. A cheaper, generic ESP32 board will do those jobs.</p>
<p>The FireBeetle becomes more interesting when several requirements appear together. Imagine a monitoring device in an enclosure that reads sensors every few minutes, reports data over Wi-Fi, and runs on a battery. In that case, you can position the antenna away from the electronics, use the onboard charger, and reduce standby current between readings. If you need a local dashboard, the extra memory and GDI interface are useful additions.</p>
<p>There are also reasons to choose something else. The FireBeetle uses the original ESP32 architecture. An ESP32-S3 board may be a better fit for newer processing features or native USB, while an ESP32-C6 is worth considering for Wi-Fi 6 or IEEE 802.15.4 support. Those boards have different peripheral and software tradeoffs, so the newer chip isn't automatically better for every project.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Final Thoughts</strong></h2>
<p>What makes the FireBeetle 2 ESP32-UE useful isn't a faster processor. It's the combination of an external antenna connection, battery-oriented power hardware, low-power options, and enough memory for a more complete IoT interface.</p>
<p>For a breadboard experiment, these details may not matter. But for an ESP32 that needs to sit inside a box, spend most of its time asleep, and communicate reliably, those details are exactly what I'd check before choosing a board.</p>
<p>Need board-specific pin mappings, power details, and reference drawings? See the <a href="https://wiki.dfrobot.com/dfr1140/">official DFRobot FireBeetle 2 ESP32-UE documentation</a>.</p>
<p>The post <a href="https://www.teachmemicro.com/dfrobot-firebeetle-2-esp32-ue-what-makes-this-esp32-board-different/">DFRobot FireBeetle 2 ESP32-UE: What Makes This ESP32 Board Different?</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ESP32 mmWave Human Presence Detection with DFRobot C4001</title>
		<link>https://www.teachmemicro.com/esp32-mmwave-human-presence-detection-with-dfrobot-c4001/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 13 Oct 2026 13:26:31 +0000</pubDate>
				<category><![CDATA[Sensor Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=13013</guid>

					<description><![CDATA[<p>A PIR sensor is a normal first choice when you need to detect a person in a room. However, there is an important limitation: PIR sensors are much better at detecting movement than determining whether someone is simply present. This becomes a problem for applications such as room occupancy detection. A person working at a &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-mmwave-human-presence-detection-with-dfrobot-c4001/">ESP32 mmWave Human Presence Detection with DFRobot C4001</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>A <a href="https://www.teachmemicro.com/pir-motion-sensor/">PIR sensor</a> is a normal first choice when you need to detect a person in a room. However, there is an important limitation: PIR sensors are much better at detecting movement than determining whether someone is simply present. This becomes a problem for applications such as room occupancy detection. A person working at a desk, watching television, or sleeping may remain relatively still for long periods.</p>
<p>The <a href="https://www.dfrobot.com/product-2795.html?tracking=UcPQpy"><strong>DFRobot C4001</strong></a> takes a different approach. Instead of detecting infrared radiation from the human body, it uses a 24 GHz radar signal. The SEN0610 version of the C4001 can detect human presence as well as measure the distance and speed of a moving target. In this tutorial, I will connect the C4001 to an ESP32 and see how well it works as a human presence sensor. More importantly, I'll test whether it can continue detecting me when I'm sitting relatively still.</p>
<p><span id="more-13013"></span></p>
<p><img data-dominant-color="847f75" data-has-transparency="false" style="--dominant-color: #847f75;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13078 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/dfrobot_c4001_mmWave_sensor.avif" alt="DFRobot C4001 mmWave Sensor" width="907" height="705" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/dfrobot_c4001_mmWave_sensor.avif 907w, https://www.teachmemicro.com/wp-content/uploads/2026/10/dfrobot_c4001_mmWave_sensor-300x233.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/10/dfrobot_c4001_mmWave_sensor-768x597.avif 768w" sizes="auto, (max-width: 907px) 100vw, 907px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>DFRobot C4001 SEN0610</strong></h2>
<p>The module I'm using is the <strong>DFRobot Gravity C4001 24 GHz mmWave Human Presence Detection Sensor</strong>, part number SEN0610. DFRobot offers different versions of the C4001, so this distinction matters. The SEN0610 used here is the 12-meter version.</p>
<p>Its main specifications are:</p>
<table>
<thead>
<tr>
<th>Specification</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Operating frequency</td>
<td>24 GHz</td>
</tr>
<tr>
<td>Radar technology</td>
<td>FMCW</td>
</tr>
<tr>
<td>Maximum detection range</td>
<td>12 m</td>
</tr>
<tr>
<td>Beam angle</td>
<td>100° × 80°</td>
</tr>
<tr>
<td>Supply voltage</td>
<td>3.3 V / 5 V</td>
</tr>
<tr>
<td>Communication</td>
<td>I2C or UART</td>
</tr>
<tr>
<td>I2C address</td>
<td>0x2A / 0x2B</td>
</tr>
<tr>
<td>UART baud rate</td>
<td>9600</td>
</tr>
</tbody>
</table>
<p>The board is quite small, measuring about 22 × 30 mm. My module also has a standard four-pin Gravity connector, so connecting it to another DFRobot board is straightforward. However, we can just as easily connect it to a regular ESP32 development board.</p>
<p><img data-dominant-color="a0a096" data-has-transparency="false" style="--dominant-color: #a0a096;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13079 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/Annotated-mmWave-Sensor-Board-Overview.avif" alt="" width="887" height="444" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/Annotated-mmWave-Sensor-Board-Overview.avif 887w, https://www.teachmemicro.com/wp-content/uploads/2026/10/Annotated-mmWave-Sensor-Board-Overview-300x150.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/10/Annotated-mmWave-Sensor-Board-Overview-768x384.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/10/Annotated-mmWave-Sensor-Board-Overview-660x330.avif 660w" sizes="auto, (max-width: 887px) 100vw, 887px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>How mmWave Human Presence Detection Works</strong></h2>
<p>The C4001 is called a <strong>mmWave sensor</strong> because it uses radio waves with wavelengths measured in millimeters. In this case, the sensor operates around 24 GHz, which corresponds to a wavelength of roughly 12.5 mm.</p>
<p>Unlike a PIR sensor, the C4001 actively transmits a radio signal. When this signal encounters a person or another object, some of the radio energy reflects toward the sensor.</p>
<p><img data-dominant-color="d8d7d7" data-has-transparency="false" style="--dominant-color: #d8d7d7;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13015 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-sensor-human-presence.avif" alt="Simple diagram showing the C4001 transmitting 24 GHz radio waves toward a person, with reflected waves returning to the sensor" width="887" height="444" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-sensor-human-presence.avif 887w, https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-sensor-human-presence-300x150.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-sensor-human-presence-768x384.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-sensor-human-presence-660x330.avif 660w" sizes="auto, (max-width: 887px) 100vw, 887px" /></p>
<p>The sensor then compares the transmitted signal with the reflected signal. Changes in the returned signal can reveal information about the target. This is why a radar-based sensor can do more than simply produce a HIGH or LOW output when somebody moves. Depending on its operating mode, the C4001 can determine <strong>human presence, target distance, and target speed</strong>.</p>
<h3><strong>Why Can mmWave Detect a Person Sitting Still?</strong></h3>
<p>This is one of the most interesting differences between mmWave and PIR sensing. A PIR sensor detects <strong>changes in infrared radiation</strong> within its field of view. Someone walking across the room creates an obvious change, so a PIR sensor can detect that movement easily. However, if the person stops moving, the infrared pattern becomes relatively constant, and the PIR sensor may eventually stop reporting motion.</p>
<p>A person who appears stationary isn't actually perfectly motionless. Breathing moves the chest. We shift our posture slightly. Our head and hands make small movements even while we're sitting at a desk. A sufficiently sensitive radar sensor can detect these small changes in the reflected radio signal. This allows the C4001 to detect slight human movement and makes it useful for <strong>presence detection</strong> rather than only conventional motion detection.</p>
<p><img data-dominant-color="dcdadc" data-has-transparency="false" style="--dominant-color: #dcdadc;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13016 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-vs-pir-sensor.avif" alt="Comparison diagram showing PIR losing detection when a seated person stops moving while the C4001 continues detecting small body movements" width="887" height="444" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-vs-pir-sensor.avif 887w, https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-vs-pir-sensor-300x150.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-vs-pir-sensor-768x384.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/09/mmwave-vs-pir-sensor-660x330.avif 660w" sizes="auto, (max-width: 887px) 100vw, 887px" /></p>
<p>This is also something I'll test later rather than simply taking the manufacturer's specification at face value.</p>
<h3><strong>How the C4001 Measures Distance</strong></h3>
<p>The C4001 uses <strong>FMCW</strong>, or Frequency-Modulated Continuous-Wave radar. Instead of continuously transmitting the same frequency, an FMCW radar changes its transmitted frequency over a defined interval. This changing signal is commonly called a <strong>chirp</strong>.</p>
<p><img data-dominant-color="f0f1f2" data-has-transparency="false" style="--dominant-color: #f0f1f2;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13017 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/fmcw-radar-signal.avif" alt="Simplified FMCW graph showing transmitted frequency increasing with time and a delayed reflected signal" width="887" height="444" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/fmcw-radar-signal.avif 887w, https://www.teachmemicro.com/wp-content/uploads/2026/09/fmcw-radar-signal-300x150.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/fmcw-radar-signal-768x384.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/09/fmcw-radar-signal-660x330.avif 660w" sizes="auto, (max-width: 887px) 100vw, 887px" /></p>
<p>The reflected signal has traveled from the radar to the target and back again, so it arrives slightly later than the signal currently being transmitted. The radar compares the transmitted and received signals. Because the transmitted frequency is changing with time, the delay produces a difference between their frequencies. This difference is usually called the <strong>beat frequency</strong>. The farther away the target is, the longer the round trip takes and therefore the greater this frequency difference becomes.</p>
<p>In simplified form:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_8cbef693be30cf42c5a74b9b151965e5.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="R = \frac{c f_b}{2S}" /></span><script type='math/tex'>R = \frac{c f_b}{2S}</script></p>
<p>where:</p>
<ul>
<li><em>R</em> is the distance to the target,</li>
<li><em>c</em> is the speed of light,</li>
<li><em>f<sub>b</sub></em> is the measured beat frequency, and</li>
<li><em>S</em> is the frequency sweep rate of the radar chirp.</li>
</ul>
<p>You don't need to perform this calculation on the ESP32. The C4001 handles the radar processing internally and makes the resulting target information available through its UART or I2C interface.</p>
<h3><strong>How the C4001 Measures Movement and Speed</strong></h3>
<p>Radar can also detect movement using the <strong>Doppler effect</strong>. If a person moves toward or away from the sensor, the reflected radio signal changes slightly because of that motion. The principle is similar to the familiar change in pitch of a vehicle siren as the vehicle approaches and then moves away, except here we're dealing with electromagnetic waves rather than sound.</p>
<p><img data-dominant-color="e1e0e0" data-has-transparency="false" style="--dominant-color: #e1e0e0;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13018 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/person-walking-toward-mmwave-sensor.avif" alt="Person walking toward the C4001 with arrows illustrating the change in the reflected radar signal" width="887" height="444" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/person-walking-toward-mmwave-sensor.avif 887w, https://www.teachmemicro.com/wp-content/uploads/2026/09/person-walking-toward-mmwave-sensor-300x150.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/person-walking-toward-mmwave-sensor-768x384.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/09/person-walking-toward-mmwave-sensor-660x330.avif 660w" sizes="auto, (max-width: 887px) 100vw, 887px" /></p>
<p>By analyzing these changes across its radar measurements, the C4001 can determine whether the target is moving and obtain information about its velocity. This gives us three useful pieces of information from one small module:</p>
<ul>
<li><strong>Presence</strong> – Is somebody there?</li>
<li><strong>Range</strong> – How far away are they?</li>
<li><strong>Velocity</strong> – How fast are they moving toward or away from the sensor?</li>
</ul>
<p>For an ESP32 project, most of the complicated radar processing therefore remains inside the C4001. From the ESP32 side, we're essentially communicating with a smart sensor and reading the processed results.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>C4001 Pinout</strong></h2>
<p>Despite what is happening inside the sensor, connecting the module is surprisingly simple.</p>
<p>The Gravity connector has only four pins:</p>
<table>
<thead>
<tr>
<th>Pin</th>
<th>I2C Function</th>
<th>UART Function</th>
</tr>
</thead>
<tbody>
<tr>
<td>+</td>
<td>Power</td>
<td>Power</td>
</tr>
<tr>
<td>-</td>
<td>Ground</td>
<td>Ground</td>
</tr>
<tr>
<td>C/R</td>
<td>SCL</td>
<td>RX</td>
</tr>
<tr>
<td>D/T</td>
<td>SDA</td>
<td>TX</td>
</tr>
</tbody>
</table>
<p>The same two signal pins therefore serve different functions depending on which communication interface is selected. Two DIP switches on the module configure the communication interface and I2C address.</p>
<p>&nbsp;</p>
<p>For this tutorial, I will use <strong>I2C</strong>. This only requires two signal wires and lets the ESP32 communicate with the sensor using its normal I2C peripheral.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Connecting the C4001 to ESP32</strong></h2>
<p>For my test, I'll use the following connection:</p>
<table>
<thead>
<tr>
<th>C4001</th>
<th>ESP32</th>
</tr>
</thead>
<tbody>
<tr>
<td>+</td>
<td>3.3 V</td>
</tr>
<tr>
<td>-</td>
<td>GND</td>
</tr>
<tr>
<td>C/R (SCL)</td>
<td>GPIO 22</td>
</tr>
<tr>
<td>D/T (SDA)</td>
<td>GPIO 21</td>
</tr>
</tbody>
</table>
<p><img data-dominant-color="cdcfcc" data-has-transparency="false" style="--dominant-color: #cdcfcc;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13074 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/C4001-Sensor-and-ESP32-Wiring-Diagram.avif" alt="C4001 Sensor and ESP32 Wiring Diagram" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/C4001-Sensor-and-ESP32-Wiring-Diagram.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/10/C4001-Sensor-and-ESP32-Wiring-Diagram-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<p>Before powering the circuit, the switches on the C4001 must also be placed in the appropriate I2C configuration. The C4001 supports two I2C addresses: <strong>0x2A and 0x2B</strong>. The DFRobot Arduino library identifies these as <em>DEVICE_ADDR_0</em> and <em>DEVICE_ADDR_1</em>.</p>
<p><img data-dominant-color="625e54" data-has-transparency="false" style="--dominant-color: #625e54;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13077 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/C4001_DIP_Switch_position-2.avif" alt="C4001 DIP Switch" width="767" height="738" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/C4001_DIP_Switch_position-2.avif 767w, https://www.teachmemicro.com/wp-content/uploads/2026/10/C4001_DIP_Switch_position-2-300x289.avif 300w" sizes="auto, (max-width: 767px) 100vw, 767px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Installing the DFRobot C4001 Arduino Library</strong></h2>
<p>Fortunately, we don't need to implement the C4001 communication protocol ourselves. DFRobot provides the <em>DFRobot_C4001</em> Arduino library.</p>
<p>Open the Arduino IDE Library Manager and search for <em>DFRobot_C4001</em>, then install the library. The library provides functions for presence and motion detection as well as obtaining target information such as range and speed.</p>
<p><strong><img data-dominant-color="e7eded" data-has-transparency="false" style="--dominant-color: #e7eded;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13019 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/dfrobot_c4001_library.avif" alt="DFRobot C4001 Library" width="268" height="289" /></strong></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Reading Distance and Speed with the ESP32</strong></h2>
<p>For our first test, we'll communicate with the sensor through I2C and print its readings to the Serial Monitor. The DFRobot library provides functions including:</p>
<pre><code>radar.motionDetection();
radar.getTargetNumber();
radar.getTargetSpeed();
radar.getTargetRange();
radar.getTargetEnergy();</code></pre>
<p>These functions allow the ESP32 to determine whether the sensor has detected a target and retrieve information about it.</p>
<pre><code>#include &lt;Wire.h&gt;
#include "DFRobot_C4001.h"

#define SDA_PIN 21
#define SCL_PIN 22

// C4001 I2C address:
// DEVICE_ADDR_0 = 0x2A
// DEVICE_ADDR_1 = 0x2B
DFRobot_C4001_I2C radar(&amp;Wire, DEVICE_ADDR_1);

void setup()
{
  Serial.begin(115200);

  // Initialize ESP32 I2C
  Wire.begin(SDA_PIN, SCL_PIN);

  // Connect to the C4001
  while (!radar.begin())
  {
    Serial.println("C4001 not detected.");
    delay(1000);
  }

  Serial.println("C4001 connected.");

  // Set the sensor to presence detection mode
  radar.setSensorMode(eExitMode);

  // Detection range: 30 cm to 10 m
  radar.setDetectionRange(30, 1000, 1000);

  // Sensitivity: 0 to 9
  radar.setTrigSensitivity(1);
  radar.setKeepSensitivity(2);

  // Trigger delay = 1 second (100 x 0.01 s)
  // Keep time = 2 seconds (4 x 0.5 s)
  radar.setDelay(100, 4);

  Serial.println("Presence detection started.");
}

void loop()
{
  if (radar.motionDetection())
  {
    Serial.println("Person detected");
  }
  else
  {
    Serial.println("No person detected");
  }

  delay(500);
}</code></pre>
<p>Once the code is running, we should be able to observe the C4001 readings directly from the Arduino Serial Monitor.</p>
<p><strong><img data-dominant-color="f7f8f8" data-has-transparency="false" style="--dominant-color: #f7f8f8;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13073 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/dfrobot_c4001_serial_monitor_output.avif" alt="Example sketch serial monitor output" width="630" height="214" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/dfrobot_c4001_serial_monitor_output.avif 630w, https://www.teachmemicro.com/wp-content/uploads/2026/10/dfrobot_c4001_serial_monitor_output-300x102.avif 300w" sizes="auto, (max-width: 630px) 100vw, 630px" /></strong></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Can the C4001 Detect a Person Sitting Still?</strong></h2>
<p>This is the test I'm most interested in. Detecting someone walking across a room isn't particularly difficult. Even an inexpensive PIR sensor can do that. The more useful question is:</p>
<p><strong>Can the C4001 tell that I'm still in the room when I'm barely moving?</strong></p>
<p>To find out, I positioned the sensor approximately <strong>1 meter</strong> away and performed three simple tests.</p>
<table>
<thead>
<tr>
<th>Test</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<td>Walking in front of sensor</td>
<td style="text-align: center;">detected</td>
</tr>
<tr>
<td>Sitting normally</td>
<td style="text-align: center;">detected</td>
</tr>
<tr>
<td>Sitting as still as possible</td>
<td style="text-align: center;">detected</td>
</tr>
</tbody>
</table>
<p>In all three tests, the sensor detected me almost instantly. Although there's a noticeable delay between detection and no detection when I am out of the sensor's range.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Testing the C4001 Detection Distance</strong></h2>
<p>DFRobot specifies a maximum detection range of 12 meters for the SEN0610. However, a maximum specification doesn't necessarily tell us what to expect inside an actual room. So I performed a simple real-world test. I placed the C4001 in a fixed position and moved away from it while monitoring its output.</p>
<table>
<thead>
<tr>
<th>Actual Distance</th>
<th>Presence Detected?</th>
<th>C4001 Measured Distance</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 m</td>
<td>Yes</td>
<td>0.99 m</td>
</tr>
<tr>
<td>3 m</td>
<td>Yes</td>
<td>2.99 m</td>
</tr>
<tr>
<td>5 m</td>
<td>Yes</td>
<td>4.99 m</td>
</tr>
<tr>
<td>Maximum tested distance (10 m)</td>
<td>Yes</td>
<td>10 m</td>
</tr>
</tbody>
</table>
<p>This isn't intended to be a laboratory characterization of the radar. Room geometry, sensor orientation, surrounding objects, and target movement can all affect radar measurements. Instead, the test gives us a better idea of what we can expect from the module in a normal indoor project.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Testing Target Speed</strong></h2>
<p>Since the C4001 can also report target velocity, I performed one more simple experiment. I walked toward the sensor and then away from it while watching the reported speed in the Serial Monitor.</p>
<table>
<thead>
<tr>
<th>Movement</th>
<th>Reported Speed</th>
<th>Observation</th>
</tr>
</thead>
<tbody>
<tr>
<td>Walking toward sensor</td>
<td>0.2</td>
<td style="text-align: center;">accurate</td>
</tr>
<tr>
<td>Walking away from sensor</td>
<td>-0.3</td>
<td style="text-align: center;">accurate</td>
</tr>
</tbody>
</table>
<p><img data-dominant-color="f8f9f9" data-has-transparency="false" style="--dominant-color: #f8f9f9;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13081 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/velocity-c4001-serial-plotter.avif" alt="" width="781" height="489" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/velocity-c4001-serial-plotter.avif 781w, https://www.teachmemicro.com/wp-content/uploads/2026/10/velocity-c4001-serial-plotter-300x188.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/10/velocity-c4001-serial-plotter-768x481.avif 768w" sizes="auto, (max-width: 781px) 100vw, 781px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Presence Detection vs Distance and Speed Measurement</strong></h2>
<p>There is an important distinction when experimenting with the C4001. The sensor provides operating modes intended for different applications. One is particularly useful for determining whether somebody is present, while another provides target information such as distance and speed. This matters because an occupancy sensor and a radar measurement sensor aren't necessarily doing the same job.</p>
<p>If I were building an automatic room-lighting system, for example, my main question would simply be "Is somebody still in the room?" For another project, such as detecting someone approaching a doorway, distance and speed become much more useful.</p>
<p>In presence detection mode (eExitMode), the C4001 focuses on determining whether someone is within the configured detection area. Rather than continuously reporting how far away a person is, the sensor provides a detection status. This makes it suitable for applications such as automatic lighting, where the important thing is whether someone is still occupying the space.</p>
<p>Another useful feature of this mode is the ability to adjust the trigger sensitivity, keep sensitivity, and detection delay. These settings help prevent the output from changing too quickly when a person briefly stops moving. For example, if someone is sitting at a desk, an occupancy sensor should ideally continue reporting their presence even when they are relatively still.</p>
<p>Distance and speed measurement mode (eSpeedMode) works differently. Here, the C4001 can report the target's distance in meters and speed in meters per second. This is particularly useful when tracking someone walking toward or away from the sensor. With the ESP32 connected to the Arduino Serial Plotter, we can visualize the speed readings as the person moves.</p>
<p>One limitation is that the measured speed mainly represents movement along the radar's line of sight. Someone walking directly toward the sensor produces a more useful radial-speed measurement than someone walking sideways across its field of view. Also, distance and speed measurements are not the same as reliable room-occupancy detection. A stationary person may be easier to monitor using the dedicated presence detection mode.</p>
<p>For practical applications, I would choose <strong>presence detection mode for occupancy-based automation and distance and speed measurement mode for projects that need information about a moving target.</strong> The distinction is worth remembering because switching modes changes not only the available measurements but also the configuration functions used in the Arduino sketch.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>C4001 mmWave vs PIR Sensor</strong></h2>
<p>The C4001 isn't necessarily a replacement for every PIR sensor. PIR sensors remain extremely simple and inexpensive. For detecting obvious movement, such as somebody walking past a doorway, they can be perfectly adequate. The C4001 becomes more interesting when the application needs to know whether somebody <strong>remains present</strong>.</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>C4001 mmWave</th>
<th>PIR</th>
</tr>
</thead>
<tbody>
<tr>
<td>Detect obvious movement</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Detect slight human movement</td>
<td>Yes</td>
<td>Limited</td>
</tr>
<tr>
<td>Presence detection</td>
<td>Yes</td>
<td>Primarily motion-based</td>
</tr>
<tr>
<td>Target distance</td>
<td>Available</td>
<td>No</td>
</tr>
<tr>
<td>Target velocity</td>
<td>Available</td>
<td>No</td>
</tr>
<tr>
<td>Active transmitter</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Microcontroller interface</td>
<td>I2C / UART</td>
<td>Usually digital output</td>
</tr>
<tr>
<td>Implementation complexity</td>
<td>Higher</td>
<td>Very low</td>
</tr>
</tbody>
</table>
<p>That makes mmWave especially attractive for occupancy-based automation where turning the lights off simply because someone hasn't moved enough for a PIR sensor is undesirable.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Where Can You Use the C4001?</strong></h2>
<p>The most obvious use for the C4001 is <strong>room occupancy detection</strong>. An ESP32 could use the C4001 to determine whether a room remains occupied and then control lights, ventilation, air conditioning, or other devices accordingly.</p>
<p>Other possible applications include:</p>
<ul>
<li>Smart lighting systems</li>
<li>Home automation</li>
<li>Office occupancy detection</li>
<li>Automatic HVAC control</li>
<li>Bathroom occupancy sensors</li>
<li>Security systems</li>
<li>Bedside presence detection</li>
<li>Automatic doors</li>
<li>Approach detection</li>
</ul>
<p>The distance information also opens up more interesting possibilities. Instead of simply detecting someone, an ESP32 could respond differently depending on how close that person is to a device.</p>
<p><img data-dominant-color="997e6a" data-has-transparency="false" style="--dominant-color: #997e6a;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13080 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/10/MmWave-Sensor-Smart-Living-Room-Demo.avif" alt="" width="836" height="471" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/10/MmWave-Sensor-Smart-Living-Room-Demo.avif 836w, https://www.teachmemicro.com/wp-content/uploads/2026/10/MmWave-Sensor-Smart-Living-Room-Demo-300x169.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/10/MmWave-Sensor-Smart-Living-Room-Demo-768x433.avif 768w" sizes="auto, (max-width: 836px) 100vw, 836px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Final Thoughts</strong></h2>
<p>At first glance, the DFRobot C4001 performs the same basic job as countless inexpensive motion sensors: detecting whether somebody is nearby. The important difference is <em>how</em> it does it. Using 24 GHz radar allows the C4001 to detect subtle movement that would be difficult for a conventional PIR sensor to recognize. At the same time, FMCW radar allows the module to provide information such as target distance and velocity. And from the ESP32 side, using all of this is surprisingly straightforward. The complicated radar processing happens inside the C4001; our microcontroller simply retrieves the processed information through I2C or UART.</p>
<p>During testing, one of the more interesting things to examine with the C4001 is how it responds when a person stops moving. Unlike a conventional PIR sensor, which primarily responds to changes in infrared radiation, the C4001 uses radar to detect movement, including smaller movements associated with someone remaining in place. However, detecting a person who is sitting quietly is still more challenging than detecting someone walking directly toward the sensor. The result depends on the sensor's sensitivity settings, mounting position, and surrounding environment.</p>
<p>The distance and speed measurement mode also adds another dimension to the experiment. Instead of simply indicating that a target has been detected, the sensor can provide measurements that we can observe through the Arduino Serial Monitor or Serial Plotter. Walking toward and away from the sensor is a useful way to demonstrate these capabilities, although the readings can fluctuate because of body movement, changes in target reflection, and the angle at which the person approaches.</p>
<p>Another consideration is the detection environment. Walls, furniture, and other reflective objects can affect radar measurements, while movements outside the intended detection area may produce unwanted detections. For this reason, sensitivity and detection-range adjustments are important, particularly when using the C4001 in a small room or near a doorway.</p>
<p>Overall, the C4001 offers more flexibility than a basic motion detector. Its presence detection mode is useful for occupancy-based applications, while its distance and speed measurement mode opens up possibilities for movement monitoring and interactive projects. The ESP32 handles both modes using relatively simple Arduino code, leaving the more complicated signal processing to the radar module.</p>
<p>For projects where you only need to detect somebody walking past a sensor, a PIR sensor may still be the simpler and cheaper solution. However, when the question changes from <em>"Did something move?"</em> to <em>"Is somebody still here?"</em>, a mmWave presence sensor such as the C4001 becomes much more interesting.</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-mmwave-human-presence-detection-with-dfrobot-c4001/">ESP32 mmWave Human Presence Detection with DFRobot C4001</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ESP32 AI Guide: What Can ESP32 and ESP32-S3 Actually Do?</title>
		<link>https://www.teachmemicro.com/esp32-ai/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Sat, 26 Sep 2026 10:47:23 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12627</guid>

					<description><![CDATA[<p>Artificial intelligence often brings to mind powerful GPUs, huge servers, and applications like ChatGPT. An ESP32, meanwhile, is a microcontroller with only a fraction of the processing power and memory of a modern computer. Surprisingly, these two worlds are becoming increasingly connected. An ESP32 can run small machine-learning models locally, recognize voice commands, classify sensor data, &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-ai/">ESP32 AI Guide: What Can ESP32 and ESP32-S3 Actually Do?</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Artificial intelligence often brings to mind powerful GPUs, huge servers, and applications like ChatGPT. An <em>ESP32</em>, meanwhile, is a microcontroller with only a fraction of the processing power and memory of a modern computer. Surprisingly, these two worlds are becoming increasingly connected.</p>
<p>An ESP32 can run small machine-learning models locally, recognize voice commands, classify sensor data, process images, and even communicate with much larger AI models through the Internet. Newer devices such as the <em>ESP32-S3</em> are especially interesting because Espressif added processor instructions that accelerate neural-network and signal-processing operations.</p>
<p>However, the term <em>ESP32 AI</em> can mean several different things. It may refer to TinyML running directly on the microcontroller, an ESP32 acting as a client for cloud-based AI, an AI camera project, or even the popular AI-Thinker ESP32-CAM board. In this guide, I will separate those concepts and look at what AI on an ESP32 can realistically do.</p>
<p><span id="more-12627"></span></p>
<p><img data-dominant-color="dfe2e5" data-has-transparency="false" style="--dominant-color: #dfe2e5;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13003 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-with-sensors.avif" alt="ESP32 with Sensors" width="836" height="471" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-with-sensors.avif 836w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-with-sensors-300x169.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-with-sensors-768x433.avif 768w" sizes="auto, (max-width: 836px) 100vw, 836px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What Does ESP32 AI Mean?</strong></h2>
<p>There isn't one specific technology called "ESP32 AI." Instead, there are several ways to combine artificial intelligence with an ESP32. The most important distinction is <em>where the AI model actually runs</em>.</p>
<h3><strong>1. AI Running Directly on the ESP32</strong></h3>
<p>In this setup, the trained machine-learning model is stored in the ESP32's flash memory and inference occurs directly on the microcontroller. For example, a microphone could continuously listen for the words "ON" and "OFF." Instead of sending every audio sample to a server, the ESP32 processes the audio locally and decides which word it heard. This is often called <em>edge AI</em>, <em>embedded machine learning</em>, or <em>TinyML</em>. I already have a complete <a href="/tinyml-with-esp32-tutorial/">TinyML with ESP32 tutorial</a> that covers the model-training and deployment process in detail. This article will focus more on what is possible and which approach makes sense for a particular project.</p>
<h3><strong>2. ESP32 Connecting to Cloud AI</strong></h3>
<p>The ESP32 does not necessarily have to run the AI model. Because it has Wi-Fi, the ESP32 can collect data and send it to an AI service running on a much more powerful server.</p>
<p>The basic architecture looks like this:<br />
<div class="tmm-flow-container tmm-flow-container--linear" role="img" aria-label="Flow diagram: Sensor / User to ESP32 - Wi-Fi; ESP32 - Wi-Fi to Cloud AI / LLM; Cloud AI / LLM to ESP32; ESP32 to Display / Relay / Speaker / Actuator."><div class="tmm-flow-row"><span class="tmm-flow-node">Sensor / User</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">ESP32 - Wi-Fi</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">Cloud AI / LLM</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">ESP32</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">Display / Relay / Speaker / Actuator</span></div></div>
I used this method in my <a href="/esp32-meets-ai-talking-to-large-language-models-via-openrouter/">ESP32 and LLM project using OpenRouter</a>. The ESP32 sends a prompt over Wi-Fi while the actual language model runs remotely.</p>
<h3><strong>3. ESP32 as Part of an AI System</strong></h3>
<p>There is also a middle ground. An ESP32 might preprocess sensor data locally and only send interesting events to a server. For example, it could detect unusual vibration using a small local model and then send the captured data to a more powerful AI service for further analysis. This hybrid approach can reduce bandwidth while still allowing the system to use large models.</p>
<p><img data-dominant-color="eaeeef" data-has-transparency="false" style="--dominant-color: #eaeeef;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13004 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/local-ai-vs-cloud-ai-vs-hybrid-ai.avif" alt="Local AI vs Cloud AI vs Hybrid AI" width="1004" height="565" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/local-ai-vs-cloud-ai-vs-hybrid-ai.avif 1004w, https://www.teachmemicro.com/wp-content/uploads/2026/09/local-ai-vs-cloud-ai-vs-hybrid-ai-300x169.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/local-ai-vs-cloud-ai-vs-hybrid-ai-768x432.avif 768w" sizes="auto, (max-width: 1004px) 100vw, 1004px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32 AI vs AI-Thinker ESP32-CAM</strong></h2>
<p>One source of confusion is worth clearing up early. Searching for "ESP32 AI" often returns results involving the <em>AI-Thinker ESP32-CAM</em>. Despite the name, <em>AI-Thinker is the manufacturer/brand associated with the module</em>. The "AI" in AI-Thinker does not mean that the ESP32-CAM itself contains a dedicated artificial-intelligence processor. The ESP32-CAM is still useful for AI projects because it combines an ESP32 with a camera. Images can either be processed locally with sufficiently small models or sent to another device or server for more demanding computer-vision tasks.</p>
<p>So:</p>
<table>
<thead>
<tr>
<th>Term</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>ESP32 AI</td>
<td>Using artificial intelligence or machine learning with an ESP32</td>
</tr>
<tr>
<td>AI-Thinker</td>
<td>A hardware/module manufacturer</td>
</tr>
<tr>
<td>AI-Thinker ESP32-CAM</td>
<td>An ESP32 camera board commonly used for vision projects</td>
</tr>
</tbody>
</table>
<p>The two concepts overlap, but they are not the same thing.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Can an ESP32 Really Run Artificial Intelligence?</strong></h2>
<p>Yes, but the important word is <em>inference</em>. Most ESP32 AI projects do not train their neural networks on the microcontroller. Training might require thousands or millions of calculations performed repeatedly over a large dataset. Instead, training normally takes place on a PC or cloud platform. Once training is finished, the much smaller resulting model is exported and placed on the ESP32.</p>
<p>The workflow therefore looks like this:<br />
<div class="tmm-flow-container tmm-flow-container--linear" role="img" aria-label="Flow diagram: Collect Data to Train Model on PC / Cloud; Train Model on PC / Cloud to Optimize / Quantize Model; Optimize / Quantize Model to Convert Model for ESP32; Convert Model for ESP32 to Flash Model to ESP32; Flash Model to ESP32 to Run Inference Locally."><div class="tmm-flow-row"><span class="tmm-flow-node">Collect Data</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">Train Model on PC / Cloud</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">Optimize / Quantize Model</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">Convert Model for ESP32</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">Flash Model to ESP32</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">Run Inference Locally</span></div></div>
Inference simply means giving an already-trained model some new input and asking it for a prediction.</p>
<p>For example:<br />
<div class="tmm-flow-container tmm-flow-container--linear" role="img" aria-label="Flow diagram: Microphone sample to Machine-learning model; Machine-learning model to &quot;ON&quot; = 92% &quot;OFF&quot; = 5% &quot;Other&quot; = 3%."><div class="tmm-flow-row"><span class="tmm-flow-node">Microphone sample</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">Machine-learning model</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">&quot;ON&quot; = 92% &quot;OFF&quot; = 5% &quot;Other&quot; = 3%</span></div></div>
This is a much smaller job than training the network from scratch.</p>
<p><img data-dominant-color="e9eaea" data-has-transparency="false" style="--dominant-color: #e9eaea;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13005 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/ai-model-training-for-esp32.avif" alt="AI Model training for ESP32" width="887" height="444" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/ai-model-training-for-esp32.avif 887w, https://www.teachmemicro.com/wp-content/uploads/2026/09/ai-model-training-for-esp32-300x150.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/ai-model-training-for-esp32-768x384.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/09/ai-model-training-for-esp32-660x330.avif 660w" sizes="auto, (max-width: 887px) 100vw, 887px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Why the ESP32-S3 Is Better for AI</strong></h2>
<p>The original ESP32 can run small machine-learning models, and that is exactly what I used in my earlier TinyML projects. However, if I were buying a board specifically for a new ESP32 AI project today, I would normally choose an <em>ESP32-S3</em>, preferably a version with PSRAM. The ESP32-S3 includes vector instructions that accelerate operations commonly found in neural networks and digital signal processing. It is important to describe this correctly: the ESP32-S3 does <em>not</em> have a large dedicated neural-processing unit comparable to the AI accelerator found in some application processors. Instead, its processor includes instructions that make certain mathematical operations considerably more efficient.</p>
<p>This matters because neural networks repeatedly perform operations such as multiplication and accumulation:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_f559a8bf877f0595eb85774956a64e05.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="y = \sum_{i=0}^{n} w_i x_i + b" /></span><script type='math/tex'>y = \sum_{i=0}^{n} w_i x_i + b</script></p>
<p>where:</p>
<ul>
<li><strong>x</strong> represents input values,</li>
<li><strong>w</strong> represents trained weights,</li>
<li><strong>b</strong> is the bias, and</li>
<li><strong>y</strong> is the resulting output.</li>
</ul>
<p>A neural network may perform these operations thousands or millions of times for a single inference. The ESP32-S3 can therefore execute appropriately optimized models faster than a classic ESP32.</p>
<h3><strong>PSRAM Also Matters</strong></h3>
<p>Processor speed isn't the only limitation. A machine-learning model needs memory for:</p>
<ul>
<li>model weights,</li>
<li>input data,</li>
<li>intermediate layer outputs,</li>
<li>the tensor arena or working memory, and</li>
<li>your normal application code and buffers.</li>
</ul>
<p>For very small sensor models, internal RAM may be enough. Camera and audio applications become much more demanding. An ESP32-S3 board with <em>external PSRAM</em> gives you considerably more room for frame buffers and larger models. That does not mean PSRAM automatically makes inference fast. External memory is generally slower than internal SRAM. However, having a slower place to store something is much better than not having enough memory to run the model at all.</p>
<p><img data-dominant-color="d5d2d5" data-has-transparency="false" style="--dominant-color: #d5d2d5;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13006 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-vs-esp32-s3.avif" alt="ESP32 vs ESP32-S3" width="887" height="444" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-vs-esp32-s3.avif 887w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-vs-esp32-s3-300x150.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-vs-esp32-s3-768x384.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-vs-esp32-s3-660x330.avif 660w" sizes="auto, (max-width: 887px) 100vw, 887px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What Kind of AI Can an ESP32 Run?</strong></h2>
<p>"AI" covers an enormous range of applications. Some are well suited to microcontrollers while others are completely unrealistic. Here are several applications that fit the ESP32 particularly well.</p>
<h3><strong>Sensor Classification</strong></h3>
<p>This is one of the easiest places to start. Suppose an accelerometer is attached to a machine. Instead of simply checking whether vibration exceeds a fixed threshold, a trained model could classify several operating states:</p>
<ul>
<li>normal operation,</li>
<li>idle,</li>
<li>unbalanced load,</li>
<li>excessive vibration,</li>
<li>possible fault.</li>
</ul>
<p>The input consists of relatively small numerical datasets, so the model can also remain small.</p>
<h3><strong>Anomaly Detection</strong></h3>
<p>Instead of teaching the ESP32 every possible fault condition, a model can learn what normal sensor readings look like and identify unusual patterns. Possible applications include:</p>
<ul>
<li>machine condition monitoring,</li>
<li>water pump monitoring,</li>
<li>battery monitoring,</li>
<li>unexpected power consumption, and</li>
<li>environmental sensors.</li>
</ul>
<p>This is an area where embedded AI can be more useful than simply putting a fixed threshold inside an <code>if()</code> statement.</p>
<h3><strong>Voice and Sound Recognition</strong></h3>
<p>Audio is another good match for the ESP32-S3. Instead of converting speech into arbitrary sentences, a small model might recognize a limited vocabulary such as: LIGHT, ON, OFF, FAN, OPEN, CLOSE</p>
<p>That task is much easier than full speech-to-text. I have a separate <a href="/voice-command-recognition-with-esp32-and-tinyml-using-edge-impulse/">ESP32 TinyML voice command project using Edge Impulse</a> for a practical example.</p>
<p>Other audio applications include:</p>
<ul>
<li>glass-breaking detection,</li>
<li>alarm detection,</li>
<li>machine noise classification,</li>
<li>clap or sound recognition, and</li>
<li>wake-word detection.</li>
</ul>
<h3><strong>Image Classification</strong></h3>
<p>A camera image can be reduced to a small resolution and passed through a lightweight neural network. The output could answer a question such as:</p>
<blockquote><p>Is this object:</p>
<p>Person 0.91 Cat 0.05 Background 0.04</p></blockquote>
<p>This is more realistic for an ESP32 than asking it to fully understand a high-resolution image.</p>
<h3><strong>Object Detection</strong></h3>
<p>Object detection is harder than simple image classification because the model must determine both <em>what</em> an object is and <em>where</em> it appears in the image. Optimized object-detection models can run on newer ESP32 hardware, but memory usage and inference time quickly become important. For a battery-operated camera expected to recognize one or two objects, an ESP32-S3 may make sense. For multi-object high-resolution detection at high frame rates, a Raspberry Pi or more powerful processor is normally a better choice.</p>
<h3><strong>Gesture Recognition</strong></h3>
<p>Gesture recognition can use either camera images or motion sensors. An accelerometer-based gesture recognizer is particularly attractive because the input data is tiny compared with an image. You could teach a device to recognize different movements and perform commands without buttons.</p>
<p><img data-dominant-color="dddedd" data-has-transparency="false" style="--dominant-color: #dddedd;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-13007 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/six-esp32-applications.avif" alt="ESP32 Applications" width="922" height="615" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/six-esp32-applications.avif 922w, https://www.teachmemicro.com/wp-content/uploads/2026/09/six-esp32-applications-300x200.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/six-esp32-applications-768x512.avif 768w" sizes="auto, (max-width: 922px) 100vw, 922px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>On-Device ESP32 AI vs Cloud AI</strong></h2>
<p>Running a model locally is not automatically better.</p>
<p>The right solution depends on what the project needs.</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>On-Device AI</th>
<th>Cloud AI</th>
</tr>
</thead>
<tbody>
<tr>
<td>Internet required</td>
<td>No</td>
<td>Usually yes</td>
</tr>
<tr>
<td>Latency</td>
<td>Very low for suitable models</td>
<td>Depends on network/server</td>
</tr>
<tr>
<td>Model size</td>
<td>Very limited</td>
<td>Can be enormous</td>
</tr>
<tr>
<td>Privacy</td>
<td>Data can remain local</td>
<td>Data normally leaves device</td>
</tr>
<tr>
<td>Operating cost</td>
<td>No per-request API fee</td>
<td>May have API charges</td>
</tr>
<tr>
<td>AI capability</td>
<td>Specialized tasks</td>
<td>Complex vision, speech and LLMs</td>
</tr>
<tr>
<td>Offline operation</td>
<td>Yes</td>
<td>No</td>
</tr>
</tbody>
</table>
<p>For example, imagine a smart door. A local ESP32 model could detect a particular sound or wake word without sending continuous microphone recordings anywhere.</p>
<p>However, if you want the user to ask:</p>
<blockquote><p><em>"Was anyone standing near the front door while I was away this afternoon?"</em></p></blockquote>
<p>that is a much more complicated task. Sending selected images or event data to a larger AI model would make more sense.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32 AI Frameworks and Tools</strong></h2>
<p>There are now several ways to deploy AI models on ESP32 devices.</p>
<h3><strong>TensorFlow Lite for Microcontrollers</strong></h3>
<p>TensorFlow Lite for Microcontrollers, commonly called TFLM, is designed to run neural-network inference on microcontrollers with limited RAM. It is useful when you want to understand the model deployment process more directly. I have a separate <a href="/getting-started-with-tensorflow-lite-on-esp32-with-voice-activity-detection-project/">TensorFlow Lite on ESP32 tutorial</a> with a voice activity detection example.</p>
<h3><strong>ESP-DL</strong></h3>
<p><em>ESP-DL</em> is Espressif's neural-network inference framework for ESP devices. Its current toolchain can take trained models, quantize them, and convert them into a format optimized for supported Espressif chips. This is particularly interesting for ESP32-S3 projects because the framework can make use of optimized kernels for the chip. If I were developing a new performance-sensitive ESP32-S3 AI project, ESP-DL would be one of the first frameworks I would investigate.</p>
<h3><strong>ESP-WHO</strong></h3>
<p><em>ESP-WHO</em> is aimed more specifically at computer-vision applications.</p>
<p>Typical applications include:</p>
<ul>
<li>face detection,</li>
<li>face recognition,</li>
<li>camera-based classification, and</li>
<li>other embedded vision projects.</li>
</ul>
<p>It is therefore worth checking when your ESP32 AI project starts with a camera rather than a microphone or sensor.</p>
<h3><strong>ESP-SR</strong></h3>
<p>For audio applications, Espressif also provides <em>ESP-SR</em>. It contains components for functions such as:</p>
<ul>
<li>audio front-end processing,</li>
<li>wake-word detection, and</li>
<li>speech-command recognition.</li>
</ul>
<p>For a product that only needs to understand a limited command vocabulary, this is much more realistic than running a general speech-to-text model.</p>
<h3><strong>Edge Impulse</strong></h3>
<p>Edge Impulse provides a higher-level workflow for collecting datasets, extracting features, training models, and deploying them to embedded devices. It is especially convenient when experimenting with:</p>
<ul>
<li>accelerometers,</li>
<li>microphones,</li>
<li>environmental sensors, and</li>
<li>small image datasets.</li>
</ul>
<p>It can save a lot of setup time when your main goal is testing whether machine learning improves the project rather than building the ML toolchain yourself.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>How Much Faster Is ESP32-S3 for AI?</strong></h2>
<p>This is something I prefer to measure rather than answer only from specifications. A useful experiment is to run the <em>same small neural-network model</em> on a classic ESP32 and an ESP32-S3. The model from my existing TinyML tutorial can be reused so that the network itself remains constant.</p>
<p>The important measurements are:</p>
<ul>
<li>model size,</li>
<li>inference time,</li>
<li>available heap before loading the model,</li>
<li>available heap while the model is running, and</li>
<li>whether PSRAM is required.</li>
</ul>
<p>Inference time can be measured around the model's <code>Invoke()</code> function:</p>
<pre><code>unsigned long startTime = micros();

TfLiteStatus invokeStatus = interpreter-&gt;Invoke();

unsigned long inferenceTime = micros() - startTime;

Serial.print("Inference time: ");
Serial.print(inferenceTime);
Serial.println(" us");
</code></pre>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Can an ESP32 Run an LLM?</strong></h2>
<p>Technically, extremely small experimental language models can be squeezed into surprisingly limited hardware. However, this needs some perspective. The language models people normally associate with AI assistants contain millions or billions of parameters and require far more memory and processing power than a normal ESP32 can provide.</p>
<p>So if the goal is something like this:</p>
<blockquote><p>User: Should I water my tomato plants today?</p>
<p>ESP32: Based on the soil moisture, temperature, weather forecast and your previous watering schedule, I recommend waiting until tomorrow.</p></blockquote>
<p>the practical architecture is usually:</p>
<div class="tmm-flow-container tmm-flow-container--linear" role="img" aria-label="Flow diagram: ESP32 sensors to ESP32; ESP32 to Wi-Fi and Wi-Fi to ESP32; Wi-Fi to LLM API and LLM API to Wi-Fi."><div class="tmm-flow-row"><span class="tmm-flow-node">ESP32 sensors</span><span class="tmm-flow-edge"><span class="tmm-flow-arrow" aria-hidden="true">&#8594;</span></span><span class="tmm-flow-node">ESP32</span><span class="tmm-flow-edge tmm-flow-edge--bidirectional"><span class="tmm-flow-arrow" aria-hidden="true">&#8596;</span></span><span class="tmm-flow-node">Wi-Fi</span><span class="tmm-flow-edge tmm-flow-edge--bidirectional"><span class="tmm-flow-arrow" aria-hidden="true">&#8596;</span></span><span class="tmm-flow-node">LLM API</span></div></div>
<p>The ESP32 handles sensors, actuators and networking while the LLM handles natural-language reasoning. That is the approach demonstrated in my <a href="/esp32-meets-ai-talking-to-large-language-models-via-openrouter/">ESP32 OpenRouter AI project</a>. There are experimental reasons to run tiny language models locally, but I would not choose an ESP32 for a project that actually needs ChatGPT-like conversation.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>When Should You Use AI Instead of Normal ESP32 Code?</strong></h2>
<p>Not every project benefits from machine learning. Suppose a soil moisture sensor reads between 0 and 4095 and you simply want to switch a pump on below a fixed threshold.</p>
<p>Normal code is better:</p>
<pre><code>if (moisture &lt; threshold) {
    startPump();
}
</code></pre>
<p>A neural network would add complexity without solving a real problem. AI becomes more useful when the input contains a <em>pattern that is difficult to describe using simple rules<em>.</em></em></p>
<p>Consider vibration monitoring. A normal program might say:</p>
<pre><code>if (vibration &gt; 500) {
    fault = true;
}
</code></pre>
<p>But suppose a healthy motor sometimes exceeds 500 during startup while a failing bearing creates a particular frequency pattern even though its peak vibration remains below 500. Now the problem is no longer just a threshold. A trained model may be able to distinguish those patterns.</p>
<p>A simple rule I use is:</p>
<div class="box info  "><div class="box-inner-block"><i class="fa tie-shortcode-boxicon"></i>
			<strong>If you can describe the decision reliably using a few conditions, use normal code. If you need to recognize a pattern from many measurements, machine learning becomes much more interesting.</strong>
			</div></div>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Which ESP32 Should You Use for AI?</strong></h2>
<p>For most new projects, this is how I would choose:</p>
<table>
<thead>
<tr>
<th>Project</th>
<th>Recommended Hardware</th>
<th>Approach</th>
</tr>
</thead>
<tbody>
<tr>
<td>Simple sensor classification</td>
<td>ESP32 or ESP32-S3</td>
<td>Local TinyML</td>
</tr>
<tr>
<td>Vibration anomaly detection</td>
<td>ESP32-S3</td>
<td>Local model</td>
</tr>
<tr>
<td>Wake-word detection</td>
<td>ESP32-S3</td>
<td>Local audio AI</td>
</tr>
<tr>
<td>Voice commands</td>
<td>ESP32-S3</td>
<td>Local TinyML / ESP-SR</td>
</tr>
<tr>
<td>Small image classifier</td>
<td>ESP32-S3 with PSRAM</td>
<td>Local vision model</td>
</tr>
<tr>
<td>Camera object detection</td>
<td>ESP32-S3 with PSRAM</td>
<td>Optimized local model or hybrid</td>
</tr>
<tr>
<td>Natural-language assistant</td>
<td>Any Wi-Fi ESP32</td>
<td>Cloud LLM</td>
</tr>
<tr>
<td>Complex image understanding</td>
<td>ESP32 camera + server</td>
<td>Cloud/server AI</td>
</tr>
</tbody>
</table>
<p>The interesting part is that the most powerful AI does not necessarily require the most powerful ESP32. If the microcontroller only needs to send a JSON request to an online LLM, even a classic ESP32 may be perfectly adequate. It is <em>local inference</em> where the ESP32-S3's extra capabilities become important.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32 AI Project Ideas</strong></h2>
<p>If you want to experiment with ESP32 AI, here are some projects that are realistic enough to build:</p>
<ul>
<li><strong>Machine vibration classifier</strong> — use an accelerometer to distinguish normal and abnormal operation.</li>
<li><strong>Offline voice-controlled switch</strong> — recognize a handful of commands locally without sending audio to the cloud.</li>
<li><strong>Sound classifier</strong> — distinguish alarms, claps, machinery or other sounds using an I2S microphone.</li>
<li><strong>Smart camera</strong> — detect whether a person or object is present before saving or transmitting an image.</li>
<li><strong>Gesture controller</strong> — classify accelerometer movements and use them to control a device.</li>
<li><strong>Sensor anomaly detector</strong> — identify unusual temperature, current, pressure or vibration patterns.</li>
<li><strong>AI sensor assistant</strong> — collect sensor readings on the ESP32 and send them to an LLM to generate a human-readable explanation.</li>
</ul>
<p>The first five can potentially operate entirely offline. The final example is better suited to a cloud AI service.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32 AI Frequently Asked Questions</strong></h2>
<h3><strong>Is the ESP32 powerful enough for AI?</strong></h3>
<p>Yes, if the model and task are small enough. Sensor classification, anomaly detection, wake words, simple audio classification and lightweight vision applications can all be appropriate. Large generative AI models are not.</p>
<h3><strong>Do I need an ESP32-S3 for machine learning?</strong></h3>
<p>No. The original ESP32 can run small TinyML models. However, the ESP32-S3 is generally a better choice for new on-device AI projects because of its vector instructions and the availability of boards with substantial PSRAM.</p>
<h3><strong>Does the ESP32-S3 have an AI accelerator?</strong></h3>
<p>It has processor extensions designed to accelerate neural-network and signal-processing calculations. It is better to think of these as <em>AI-friendly vector instructions</em> rather than a separate high-performance NPU.</p>
<h3><strong>Do I need PSRAM?</strong></h3>
<p>Not always. Very small sensor models can run entirely from internal memory. PSRAM becomes increasingly useful for camera frames, audio buffers and larger neural networks.</p>
<h3><strong>Can ESP32 run ChatGPT locally?</strong></h3>
<p>Not in the sense most people expect from ChatGPT. Tiny experimental language models are possible, but practical conversational AI is better handled by having the ESP32 communicate with an online LLM.</p>
<h3><strong>Is ESP32-CAM an AI board?</strong></h3>
<p>The camera makes it useful for AI applications, but the "AI" in AI-Thinker ESP32-CAM refers to the module manufacturer rather than a dedicated AI processor.</p>
<h3><strong>Should I use TensorFlow Lite, ESP-DL or Edge Impulse?</strong></h3>
<p>TensorFlow Lite for Microcontrollers is useful for understanding conventional embedded ML deployment. ESP-DL is attractive when targeting Espressif hardware and taking advantage of optimized implementations. Edge Impulse provides a convenient end-to-end platform for collecting data, training a model and deploying it to the ESP32.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Final Thoughts</strong></h2>
<p>The ESP32 is not going to replace a GPU, Raspberry Pi, or cloud AI server. That is also not what makes <em>ESP32 AI</em> interesting. Microcontrollers operate directly beside sensors and actuators. If a tiny model can recognize the pattern you need, an ESP32 can make a decision locally within a small, inexpensive, and low-power device.</p>
<p>For simple machine-learning experiments, the classic ESP32 is still usable. For new projects that require local audio, vision, or larger models, the <em>ESP32-S3 is the much more interesting platform</em>, particularly when paired with PSRAM. And if the AI task is simply too large for a microcontroller, Wi-Fi gives us another option: let the ESP32 handle the physical world while a cloud model handles the heavy computation.</p>
<p>If you want to build your first model rather than just explore the possibilities, continue with my <a href="/tinyml-with-esp32-tutorial/">TinyML with ESP32 Tutorial</a>. For voice projects, see my <a href="/voice-command-recognition-with-esp32-and-tinyml-using-edge-impulse/">ESP32 voice command recognition tutorial</a>. And if your goal is to connect an ESP32 to a large language model, see my <a href="/esp32-meets-ai-talking-to-large-language-models-via-openrouter/">ESP32 LLM/OpenRouter project</a>.</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-ai/">ESP32 AI Guide: What Can ESP32 and ESP32-S3 Actually Do?</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ESP32-S31 Explained: Specs, RISC-V, Wi-Fi 6 and ESP32-S3 Comparison</title>
		<link>https://www.teachmemicro.com/esp32-s31-explained-specs-risc-v-wi-fi-6-and-esp32-s3-comparison/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 22 Sep 2026 22:01:47 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12867</guid>

					<description><![CDATA[<p>The ESP32 family has changed quite a bit since the first appearance of the original ESP32. We have had the ESP32-S2, S3, C3, C6 and several other variants aimed at specific applications. Now Espressif has introduced another major member of the family: the ESP32-S31. At first glance, the name makes it sound like a small &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-s31-explained-specs-risc-v-wi-fi-6-and-esp32-s3-comparison/">ESP32-S31 Explained: Specs, RISC-V, Wi-Fi 6 and ESP32-S3 Comparison</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The ESP32 family has changed quite a bit since the first appearance of the original ESP32. We have had the ESP32-S2, S3, C3, C6 and several other variants aimed at specific applications. Now Espressif has introduced another major member of the family: the ESP32-S31. At first glance, the name makes it sound like a small update to the <a href="https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/">ESP32-S3</a>. It isn't.</p>
<p>The ESP32-S31 moves to a dual-core RISC-V processor running at up to 320 MHz, adds <a href="https://en.wikipedia.org/wiki/Wi-Fi_6">Wi-Fi 6</a>, brings back Bluetooth Classic, includes an IEEE 802.15.4 radio for Zigbee and Thread, and adds considerably more hardware for displays, cameras, audio and edge AI.</p>
<p>Espressif announced the ESP32-S31 in March 2026 and moved the device into mass production in July. So unlike some chips that remain development announcements for months, the S31 is already moving into development boards and production modules.</p>
<p><span id="more-12867"></span></p>
<p><img data-dominant-color="bababa" data-has-transparency="false" style="--dominant-color: #bababa;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12981 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-vs-esp32-s3.avif" alt="ESP32-S31 vs ESP32-S3" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-vs-esp32-s3.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-vs-esp32-s3-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What is the ESP32-S31?</strong></h2>
<p>The ESP32-S31 is a high-performance wireless microcontroller from Espressif intended for connected devices that need more processing power and more interfaces than a typical ESP32 application. Its two main processor cores use the 32-bit RISC-V architecture and can operate at up to 320 MHz. One of those cores includes a 128-bit data path and SIMD instructions intended to accelerate operations commonly found in signal processing, image processing and machine-learning workloads.</p>
<p>That already makes the architecture quite different from the ESP32-S3. The S3 uses two Xtensa LX7 cores running at up to 240 MHz.</p>
<p>The ESP32-S31 also provides 512 KB of internal SRAM, while external or in-package high-speed PSRAM can provide much larger working memory for applications such as graphical interfaces, cameras and AI models. Espressif currently lists ESP32-S31 chip variants with 16 MB or 32 MB of in-package Octal SPI PSRAM.</p>
<p>The important specifications can be summarized as follows:</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>ESP32-S31</th>
</tr>
</thead>
<tbody>
<tr>
<td>CPU</td>
<td>Dual-core 32-bit RISC-V</td>
</tr>
<tr>
<td>Maximum CPU Clock</td>
<td>320 MHz</td>
</tr>
<tr>
<td>Internal SRAM</td>
<td>512 KB</td>
</tr>
<tr>
<td>External/In-package PSRAM</td>
<td>High-speed 8-bit DDR PSRAM, including 16 MB and 32 MB chip variants</td>
</tr>
<tr>
<td>Wi-Fi</td>
<td>2.4 GHz Wi-Fi 6 / 802.11ax</td>
</tr>
<tr>
<td>Bluetooth</td>
<td>Bluetooth 5.4 LE + Bluetooth Classic</td>
</tr>
<tr>
<td>802.15.4</td>
<td>Yes, for Zigbee and Thread</td>
</tr>
<tr>
<td>GPIO</td>
<td>Up to 60 programmable GPIOs</td>
</tr>
<tr>
<td>USB</td>
<td>USB 2.0 High-Speed OTG + USB Serial/JTAG</td>
</tr>
<tr>
<td>Ethernet</td>
<td>Gigabit Ethernet MAC</td>
</tr>
<tr>
<td>Camera</td>
<td>8- to 16-bit DVP</td>
</tr>
<tr>
<td>Image Acceleration</td>
<td>JPEG codec, PPA and 2D-DMA</td>
</tr>
</tbody>
</table>
<p>Those specifications put the ESP32-S31 somewhere between the traditional ESP32 microcontrollers and Espressif's more application-processor-like devices.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S31 Uses RISC-V</strong></h2>
<p>One of the biggest changes is the processor architecture. ESP32-S3 uses Espressif's familiar Xtensa LX7 cores. ESP32-S31 instead uses RISC-V, continuing Espressif's shift toward the open instruction-set architecture already found in chips such as the <a href="https://www.teachmemicro.com/esp32-c3-super-mini-getting-started-guide-pinout-arduino-ide-setup-and-built-in-led/">ESP32-C3</a> and ESP32-C6.</p>
<p>The two high-performance cores run at up to 320 MHz. Espressif also includes a 40 MHz low-power coprocessor, which allows some monitoring and low-power tasks to continue while the main processors are inactive. The increased clock frequency alone does not explain all of the performance improvement. One CPU core provides a 128-bit data path with SIMD instructions. SIMD, or Single Instruction Multiple Data, allows one instruction to operate on several pieces of data at the same time.</p>
<p>Consider image processing. If an algorithm needs to perform the same mathematical operation on thousands of pixels, processing several pixel values simultaneously can be much faster than processing each one individually. This is also useful for DSP operations, audio processing and neural-network inference. The S31 therefore isn't simply a faster ESP32 for blinking LEDs or reading sensors. Much of its additional hardware makes more sense once the application starts handling large streams of data.</p>
<p><img data-dominant-color="e9f1f4" data-has-transparency="false" style="--dominant-color: #e9f1f4;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12983 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-block-diagram.avif" alt="ESP32-S31 Block Diagram" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-block-diagram.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-block-diagram-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Wi-Fi 6, Bluetooth and 802.15.4</strong></h2>
<p>Connectivity is probably the area where the ESP32-S31 separates itself most clearly from the ESP32-S3. The S31 supports 2.4 GHz Wi-Fi 6, or IEEE 802.11ax. Note the 2.4 GHz specification here: Wi-Fi 6 support does not mean that the ESP32-S31 suddenly becomes a 5 GHz Wi-Fi device. Its integrated radio operates in the 2.4 GHz band.</p>
<p>Bluetooth support is also considerably broader. ESP32-S31 supports both Bluetooth 5.4 Low Energy and Bluetooth Classic BR/EDR. This is interesting because Bluetooth Classic disappeared from several newer ESP32 variants, including the ESP32-S3. Bringing it back makes the S31 more useful for applications such as Bluetooth speakers and other devices that still need compatibility with traditional Bluetooth audio. Bluetooth LE support also extends to LE Audio, including the LC3 codec and multi-stream audio capabilities. Espressif specifically highlights audio as one of the intended application areas for the chip.</p>
<p>There is also an IEEE 802.15.4 radio, which provides the hardware foundation for protocols such as Zigbee and Thread. That means one ESP32-S31 can potentially act as a much more capable smart-home controller than an S3. For example, a gateway could communicate with Wi-Fi devices, Bluetooth devices and Thread or Zigbee sensors without requiring separate wireless microcontrollers for every protocol. This also makes the chip particularly interesting for Matter gateways and border-router-style devices.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Gigabit Ethernet</strong></h2>
<p>Another surprising addition is a 1000 Mbps Ethernet MAC. The important word here is MAC. The ESP32-S31 does not magically provide an RJ45 Ethernet connection directly from the chip. A design still needs the appropriate external Ethernet PHY and magnetics. Espressif's own ESP32-S31-Function-CoreBoard-1 demonstrates this approach. The board connects the S31's RGMII interface to an external Ethernet PHY and provides an RJ45 connector capable of 10/100/1000 Mbps auto-negotiation. Still, having a Gigabit-capable Ethernet MAC inside an ESP32-class SoC is significant. It makes applications such as network gateways, industrial controllers and high-bandwidth edge devices much more practical.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S31 for Displays and Cameras</strong></h2>
<p>The S31 is clearly designed for products with more sophisticated user interfaces. It includes a DVP camera interface supporting 8- to 16-bit data, along with several LCD interfaces. These include parallel RGB displays up to 24 bits as well as I8080 and MOTO6800-style interfaces. Hardware can convert between formats including RGB565 and several YUV formats. Espressif also provides dedicated image-processing hardware such as a JPEG codec, Pixel Processing Accelerator (PPA) and 2D-DMA.</p>
<p>Why does that matter? Without dedicated hardware, operations such as moving a framebuffer, resizing an image or handling camera data can consume a large percentage of CPU time. The ESP32-S31 can offload some of those jobs. That gives the main processors more time for application logic, networking or AI inference. Up to 14 capacitive-touch channels are also available, making the chip suitable for touch panels and appliances where physical buttons are being replaced by interactive displays. I can see this being particularly useful for smart control panels, video doorbells, small instrument displays and voice-enabled appliances.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Edge AI on the ESP32-S31</strong></h2>
<p>Espressif has been promoting AI capabilities since the ESP32-S3, so we should be careful about what "AI" means here. The ESP32-S31 is not intended to compete with a desktop GPU running a large language model. Instead, its hardware acceleration makes it suitable for smaller edge AI inference tasks where data must be processed directly on the device. Examples include keyword recognition, wake-word detection, image classification, object detection, anomaly detection and signal classification.</p>
<p>The combination of SIMD instructions, a 128-bit data path, hardware image processing, fast PSRAM and dedicated mathematical acceleration makes these workloads considerably more practical than they were on earlier ESP32 devices. Espressif also lists CORDIC acceleration among the S31's dedicated hardware. This also explains the amount of PSRAM available. A simple sensor-reading program doesn't need 16 MB or 32 MB of RAM. Camera frame buffers, graphical interfaces, neural-network tensors and audio buffers certainly can.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Peripheral Interfaces</strong></h2>
<p>Despite its emphasis on multimedia, the ESP32-S31 is still a microcontroller. The chip provides traditional interfaces such as SPI, I2C, UART, I2S, PWM and GPIO alongside newer or higher-performance peripherals. It includes four main-system UART controllers plus a low-power UART. SPI interfaces support normal SPI as well as Dual, Quad and, on some controllers, Octal operation. There are also two high-performance I2C controllers plus a low-power I2C interface.</p>
<p>USB receives a major upgrade to USB 2.0 High-Speed OTG. High-Speed USB is a substantial improvement over the USB Full-Speed interface found on the ESP32-S3. The S31 also includes interfaces for CAN FD, SD/MMC, motor-control PWM, LED PWM, RMT, pulse counting and parallel I/O. On the analog side, the documentation lists ADC, DAC, a temperature sensor, touch sensing and an analog voltage comparator. With up to 60 programmable GPIOs, larger designs are less likely to run into the pin limitations that sometimes appear when an ESP32 project starts combining a display, camera, external memory and several peripherals.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S31 vs ESP32-S3</strong></h2>
<p>The S31 may look like the natural replacement for the ESP32-S3, but that doesn't mean every S3 project needs one.</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>ESP32-S31</th>
<th>ESP32-S3</th>
</tr>
</thead>
<tbody>
<tr>
<td>CPU</td>
<td>Dual-core RISC-V</td>
<td>Dual-core Xtensa LX7</td>
</tr>
<tr>
<td>Maximum Clock</td>
<td>320 MHz</td>
<td>240 MHz</td>
</tr>
<tr>
<td>Internal SRAM</td>
<td>512 KB</td>
<td>512 KB</td>
</tr>
<tr>
<td>Wi-Fi</td>
<td>2.4 GHz Wi-Fi 6</td>
<td>2.4 GHz Wi-Fi 4</td>
</tr>
<tr>
<td>Bluetooth LE</td>
<td>Bluetooth 5.4</td>
<td>Bluetooth 5</td>
</tr>
<tr>
<td>Bluetooth Classic</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>Zigbee / Thread Radio</td>
<td>Yes</td>
<td>No</td>
</tr>
<tr>
<td>GPIO</td>
<td>Up to 60</td>
<td>Up to 45</td>
</tr>
<tr>
<td>USB</td>
<td>USB 2.0 High-Speed</td>
<td>USB 2.0 Full-Speed</td>
</tr>
<tr>
<td>Ethernet MAC</td>
<td>Gigabit</td>
<td>No equivalent integrated Gigabit MAC</td>
</tr>
<tr>
<td>AI / Multimedia Hardware</td>
<td>Expanded SIMD, JPEG, PPA, 2D-DMA and other accelerators</td>
<td>SIMD/vector acceleration</td>
</tr>
</tbody>
</table>
<p>Espressif itself recommends the S31 for gateways, bridges and AI multimedia devices where multi-protocol connectivity or higher processing performance is important. The company still recommends the S3 for more cost-sensitive applications that don't need these capabilities. That seems like the sensible way to look at the two chips.</p>
<p>If your project is reading an I2C sensor and uploading a value to an MQTT server, an ESP32-S31 would probably be excessive. If your project combines a camera, display, voice interface, Wi-Fi, Bluetooth and Thread, then the advantages start to become obvious.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S31 Modules and Development Boards</strong></h2>
<p>Espressif is already offering modules around the new chip. The ESP32-S31-WROOM-1 family is listed with different flash and PSRAM capacities, while the ESP32-S31-WROOM-3 is being used on Espressif's multimedia and function development boards.</p>
<p>One interesting example is the ESP32-S31-Korvo-1. This development board is clearly aimed at showing what the chip can do beyond ordinary IoT sensing. It includes audio hardware, dual microphones, speaker outputs, microSD support, USB High-Speed connectivity and connectors for an LCD and camera. A current WROOM-3 configuration used by the board includes 16 MB flash and 16 MB PSRAM. Espressif also provides the ESP32-S31-Function-CoreBoard-1, which adds Gigabit Ethernet and audio hardware while breaking out the important GPIO connections.</p>
<figure id="attachment_12980" aria-describedby="caption-attachment-12980" style="width: 724px" class="wp-caption aligncenter"><img data-dominant-color="cacbcb" data-has-transparency="false" style="--dominant-color: #cacbcb;" loading="lazy" decoding="async" class="wp-image-12980 size-full not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-development-boards.avif" alt="ESP32-S31 Development Boards" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-development-boards.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s31-development-boards-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /><figcaption id="caption-attachment-12980" class="wp-caption-text"><em>Left: ESP32-S31 Function Coreboard; Right: ESP32-S31 Korvo-1</em></figcaption></figure>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Programming the ESP32-S31</strong></h2>
<p>For now, ESP-IDF is the safest development environment for the ESP32-S31. Espressif says S31 support is available in the ESP-IDF master and v6.1 branches, with official tagged support planned from ESP-IDF 6.1.1 onward. Arduino support is a different story.</p>
<p>As of September 2026, the stable Arduino-ESP32 documentation still does not list the S31 among officially supported SoCs. Espressif developers have been working on S31 support for Arduino 4.x, which is based on ESP-IDF 6.1, but the Arduino team's current roadmap places the 4.0 release in November 2026. So if you purchase an ESP32-S31 board today expecting exactly the same Arduino IDE experience as an ESP32-S3 DevKit, expect some rough edges.</p>
<p>For developers already comfortable with ESP-IDF, however, the S31 can already be explored properly. This situation should improve as the software ecosystem catches up with the hardware.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Security Features</strong></h2>
<p>The ESP32-S31 also includes considerably more security hardware than is necessary for the average hobby project, but that becomes important in commercial products. The chip supports secure boot, flash and PSRAM encryption, hardware cryptographic acceleration and protected key handling. Hardware support includes AES-128/256, RSA, ECDSA and ECC operations. Espressif also includes TRNG and a RAM-based physical unclonable function, or PUF, as part of its security architecture.</p>
<p>Flash encryption uses XTS-AES-256, while secure boot can verify signed firmware before it executes. Together, these mechanisms make it more difficult to extract proprietary firmware or replace legitimate software with unauthorized code. These features may not matter while experimenting with a development board, but they become very important once an ESP32-based product leaves the lab.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Who is the ESP32-S31 For?</strong></h2>
<p>The ESP32-S31 makes the most sense when several previously separate requirements start appearing in the same project. A smart-home gateway is one example. It may need Wi-Fi for the local network, Thread or Zigbee for sensors, Bluetooth for commissioning, Ethernet for reliable wired connectivity and enough processing power to run local automation logic.</p>
<p>A smart display is another. Now the device may need LCD graphics, touch input, audio, Wi-Fi, Bluetooth and perhaps a camera.  The same applies to voice assistants, industrial gateways, security panels and camera-based edge devices. For those applications, the ESP32-S31 can potentially replace several chips with one highly integrated SoC.</p>
<p>For basic sensor nodes and conventional ESP32 projects, however, the S31 is unlikely to make the ESP32-S3, C3 or C6 obsolete. Those devices are cheaper, mature and already have a large software ecosystem.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Final Thoughts</strong></h2>
<p>The ESP32-S31 feels less like another variation of the ESP32 and more like Espressif stretching the definition of what an ESP32-class microcontroller can handle. You still get familiar microcontroller peripherals such as GPIO, SPI, I2C, UART and PWM. But those now sit beside a 320 MHz dual-core RISC-V processor, high-speed PSRAM, Wi-Fi 6, Bluetooth Classic and LE, Zigbee/Thread connectivity, High-Speed USB, Gigabit Ethernet, camera and display interfaces, and dedicated multimedia accelerators.</p>
<p>The hardware is impressive. The main limitation right now is software maturity. ESP-IDF is already the primary path for development, while Arduino support is still catching up. For developers who depend heavily on mature Arduino libraries, the ESP32-S3 remains easier to use today. But once the ecosystem matures, the ESP32-S31 could become one of Espressif's most interesting devices for smart displays, gateways, audio products and edge-AI projects. And considering how many interfaces Espressif has managed to put into a single microcontroller, there should be plenty of projects worth testing on it.</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-s31-explained-specs-risc-v-wi-fi-6-and-esp32-s3-comparison/">ESP32-S31 Explained: Specs, RISC-V, Wi-Fi 6 and ESP32-S3 Comparison</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ESP32-S3 PSRAM: How to Enable, Detect and Use It in Arduino</title>
		<link>https://www.teachmemicro.com/esp32-s3-psram-how-to-enable-detect-and-use-it-in-arduino/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Sun, 20 Sep 2026 02:18:19 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12838</guid>

					<description><![CDATA[<p>One of the reasons I like the ESP32-S3 is that you can get a surprising amount of memory on a very small development board. Some variants come with 2 MB of PSRAM, while others have 8 MB. That is much more memory than the ESP32-S3 has internally. However, the advertised PSRAM size does not tell &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-s3-psram-how-to-enable-detect-and-use-it-in-arduino/">ESP32-S3 PSRAM: How to Enable, Detect and Use It in Arduino</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>One of the reasons I like the ESP32-S3 is that you can get a surprising amount of memory on a very small development board. Some variants come with 2 MB of PSRAM, while others have 8 MB. That is much more memory than the ESP32-S3 has internally. However, the advertised PSRAM size does not tell you how much of it your program can actually use.</p>
<p>I ran into this while testing an <a href="https://www.teachmemicro.com/esp32-s3-mini-getting-started-guide/">ESP32-S3 Mini</a>. I knew the chip on my board was supposed to have 2 MB of PSRAM. One of my first tests successfully allocated a 1 MB buffer; however, it was surprisingly easy to read that result as if the board only had 1 MB available. Another memory test showed the actual 2 MB.</p>
<p>The important part was figuring out what each PSRAM value actually represented.</p>
<p>There is the total PSRAM physically available to the chip. Then there is the amount currently free. There is also the largest continuous block that your program can allocate. Those numbers are related, but they are not necessarily equal. In this tutorial, I'll show you how to enable PSRAM in the Arduino IDE, confirm that the ESP32-S3 actually detects it, allocate buffers inside it and check how much usable memory remains.</p>
<p><span id="more-12838"></span></p>
<p><img data-dominant-color="dbddde" data-has-transparency="false" style="--dominant-color: #dbddde;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12851 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s3-psram-hero.avif" alt="ESP32-S3 PSRAM Hero" width="836" height="471" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s3-psram-hero.avif 836w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s3-psram-hero-300x169.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s3-psram-hero-768x433.avif 768w" sizes="auto, (max-width: 836px) 100vw, 836px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What is PSRAM on the ESP32-S3?</strong></h2>
<p>The ESP32-S3 already contains internal <a href="https://en.wikipedia.org/wiki/Static_random-access_memory">SRAM</a>. This is where your program keeps variables, task stacks, temporary buffers, and other runtime data. Internal RAM becomes limited once the project starts using large buffers. A small sensor program will probably never care about PSRAM. This becomes noticeable in projects that:</p>
<ul>
<li>Store camera frame buffers</li>
<li>Run TinyML models</li>
<li>Build large JSON documents</li>
<li>Process audio samples</li>
<li>Buffer data before writing to an SD card</li>
<li>Store images for a display</li>
<li>Receive large network packets or files</li>
</ul>
<p>These applications can quickly consume the available internal SRAM. <strong>PSRAM</strong>, or pseudo-static RAM, gives the ESP32-S3 an additional external memory space. Depending on the hardware configuration, the PSRAM can be connected through Quad SPI or Octal SPI.</p>
<p>Once initialized, PSRAM can be allocated much like normal RAM. However, it is still external memory and not simply another block of internal SRAM. Because it is external memory, it is slower and has some restrictions that internal SRAM does not.</p>
<p><img data-dominant-color="f1f1f2" data-has-transparency="false" style="--dominant-color: #f1f1f2;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12852 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s3-memory.avif" alt="ESP32-S3 Memory" width="718" height="349" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s3-memory.avif 718w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-s3-memory-300x146.avif 300w" sizes="auto, (max-width: 718px) 100vw, 718px" /></p>
<p>If you are also deciding which ESP32-S3 pins are available for a project, see my <a href="https://www.teachmemicro.com/esp32-s3-pinout/">ESP32-S3 pinout guide</a>. Some GPIO pins are used internally by flash and PSRAM on certain modules.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Does Your ESP32-S3 Actually Have PSRAM?</strong></h2>
<p>Not every ESP32-S3 board has PSRAM. Two boards sold as "ESP32-S3" can have different flash and PSRAM configurations. For Espressif modules, the suffix printed on the chip or module often gives us a clue.</p>
<table>
<thead>
<tr>
<th>Marking</th>
<th>Flash</th>
<th>PSRAM</th>
<th>PSRAM Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>N4R2</td>
<td>4 MB</td>
<td>2 MB</td>
<td>QSPI</td>
</tr>
<tr>
<td>N8R2</td>
<td>8 MB</td>
<td>2 MB</td>
<td>QSPI</td>
</tr>
<tr>
<td>N16R2</td>
<td>16 MB</td>
<td>2 MB</td>
<td>QSPI</td>
</tr>
<tr>
<td>N4R8</td>
<td>4 MB</td>
<td>8 MB</td>
<td>OPI</td>
</tr>
<tr>
<td>N8R8</td>
<td>8 MB</td>
<td>8 MB</td>
<td>OPI</td>
</tr>
<tr>
<td>N16R8</td>
<td>16 MB</td>
<td>8 MB</td>
<td>OPI</td>
</tr>
</tbody>
</table>
<p>For PSRAM, look at the <strong>R</strong> suffix. <strong>R2</strong> indicates 2 MB PSRAM, while <strong>R8</strong> indicates 8 MB PSRAM. There are also ESP32-S3 variants without PSRAM, so don't assume every board includes it. On generic boards such as the ESP32-S3 Super Mini, I would not trust the product listing alone. Check the actual marking on the chip or module if possible. This is what I did with my own board. On the image below, you can see "F<strong>N4R2</strong>P415070," which means this board has 2MB of PSRAM. I trust the device marking more than the seller's listing.</p>
<p><img data-dominant-color="464342" data-has-transparency="false" style="--dominant-color: #464342;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12645 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cpu-closeup.avif" alt="ESP32-S3 Super Mini CPU Close Up" width="652" height="869" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cpu-closeup.avif 652w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cpu-closeup-225x300.avif 225w" sizes="auto, (max-width: 652px) 100vw, 652px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Enabling ESP32-S3 PSRAM in Arduino IDE</strong></h2>
<p>The Arduino configuration also has to match the installed PSRAM. If you are using the generic ESP32-S3 board definition, select:</p>
<pre><code>Tools &gt; Board &gt; ESP32 Arduino &gt; ESP32S3 Dev Module</code></pre>
<p><img data-dominant-color="ebf0f0" data-has-transparency="false" style="--dominant-color: #ebf0f0;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12949 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/arduino-tools-esp32-board.avif" alt="" width="329" height="321" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/arduino-tools-esp32-board.avif 329w, https://www.teachmemicro.com/wp-content/uploads/2026/09/arduino-tools-esp32-board-300x293.avif 300w" sizes="auto, (max-width: 329px) 100vw, 329px" /></p>
<p>Then look for:</p>
<pre><code>Tools &gt; PSRAM</code></pre>
<p><img data-dominant-color="f2f2f3" data-has-transparency="false" style="--dominant-color: #f2f2f3;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12950 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/arduino-tools-psram.avif" alt="" width="638" height="117" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/arduino-tools-psram.avif 638w, https://www.teachmemicro.com/wp-content/uploads/2026/09/arduino-tools-psram-300x55.avif 300w" sizes="auto, (max-width: 638px) 100vw, 638px" /></p>
<p>Depending on the Arduino-ESP32 version and selected board, you may see options such as:</p>
<ul>
<li>Disabled</li>
<li>QSPI PSRAM</li>
<li>OPI PSRAM</li>
</ul>
<p>For a board with <strong>R2</strong> PSRAM, select:</p>
<pre><code>QSPI PSRAM</code></pre>
<p>Then, for a board with <strong>R8</strong> PSRAM, select:</p>
<pre><code>OPI PSRAM</code></pre>
<p>For example, if the device marking indicates an R2 configuration, I would select:</p>
<pre><code>Tools &gt; PSRAM &gt; QSPI PSRAM</code></pre>
<p>A wrong PSRAM or flash setting can also prevent the board from booting correctly. So if an ESP32-S3 starts rebooting after uploading even a simple sketch, the PSRAM setting is one of the first things I would check.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Detecting PSRAM from an Arduino Sketch</strong></h2>
<p>These are my Arduino settings for this article, so make sure you have the same before uploading and running the sketches here:</p>
<ul>
<li><strong>Board:</strong> <em>ESP32S3 Dev Module</em> if your specific Mini board isn't listed</li>
<li><strong>USB CDC On Boot:</strong> <em>Enabled</em></li>
<li><strong>Upload Mode:</strong> <em>UART0 / Hardware CDC</em></li>
<li><strong>USB Mode:</strong> <em>Hardware CDC and JTAG</em> if that option is available</li>
<li><strong>Port:</strong> <em>&lt;your ESP32-S3's COM Port&gt;</em></li>
<li><strong>Serial Monitor:</strong> <em>115200</em></li>
</ul>
<p>Before allocating PSRAM, first check whether the Arduino core detects it. The simplest test is <em>psramFound()</em>:</p>
<pre><code>void setup() {
  Serial.begin(115200);
  delay(2000);

  if (psramFound()) {
    Serial.println("PSRAM detected!");
  } else {
    Serial.println("PSRAM not found.");
  }
}

void loop() {
}</code></pre>
<p>If everything is configured correctly, you should see:</p>
<pre><code>PSRAM detected!</code></pre>
<p>&nbsp;</p>
<p><img data-dominant-color="f9fafa" data-has-transparency="false" style="--dominant-color: #f9fafa;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12953 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-detected-1.avif" alt="" width="616" height="225" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-detected-1.avif 616w, https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-detected-1-300x110.avif 300w" sizes="auto, (max-width: 616px) 100vw, 616px" /></p>
<p>If you get:</p>
<pre><code>PSRAM not found.</code></pre>
<p>A failed detection test can also be caused by the board or PSRAM configuration.</p>
<p>Check these first:</p>
<ul>
<li>The exact ESP32-S3 chip or module variant</li>
<li>The board selected in Arduino IDE</li>
<li>The QSPI or OPI PSRAM setting</li>
<li>The memory specification supplied by the board manufacturer</li>
</ul>
<p>Only after those checks would I start suspecting a hardware problem.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Checking ESP32-S3 PSRAM Size</strong></h2>
<p>The <em>ESP</em> object reports the total, free, minimum-free, and maximum allocatable PSRAM.</p>
<p>Try this sketch:</p>
<pre><code>void setup() {
  Serial.begin(115200);
  delay(2000);

  Serial.println("ESP32-S3 PSRAM Test");
  Serial.println("-------------------");

  if (!psramFound()) {
    Serial.println("PSRAM not detected.");
    return;
  }

  Serial.println("PSRAM detected: YES");

  Serial.print("Total PSRAM: ");
  Serial.print(ESP.getPsramSize() / 1024.0 / 1024.0, 2);
  Serial.println(" MB");

  Serial.print("Free PSRAM: ");
  Serial.print(ESP.getFreePsram() / 1024.0);
  Serial.println(" KB");

  Serial.print("Minimum free PSRAM: ");
  Serial.print(ESP.getMinFreePsram() / 1024.0);
  Serial.println(" KB");

  Serial.print("Largest free block: ");
  Serial.print(ESP.getMaxAllocPsram() / 1024.0);
  Serial.println(" KB");
}

void loop() {
}</code></pre>
<p>On a 2 MB ESP32-S3, the important result should be close to:</p>
<pre><code>PSRAM detected: YES
Total PSRAM: 2.00 MB</code></pre>
<p>The other numbers will vary depending on the Arduino core, libraries and anything your program has already allocated. The total PSRAM value alone is not enough when debugging memory use.</p>
<p><img data-dominant-color="dfe9ea" data-has-transparency="false" style="--dominant-color: #dfe9ea;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12954 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-allocation.avif" alt="" width="607" height="253" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-allocation.avif 607w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-allocation-300x125.avif 300w" sizes="auto, (max-width: 607px) 100vw, 607px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Total PSRAM Is Not the Same as Free PSRAM</strong></h2>
<p>There are four useful numbers in the previous program:</p>
<table>
<thead>
<tr>
<th>Function</th>
<th>What It Tells You</th>
</tr>
</thead>
<tbody>
<tr>
<td><em>ESP.getPsramSize()</em></td>
<td>Total PSRAM detected by the ESP32-S3</td>
</tr>
<tr>
<td><em>ESP.getFreePsram()</em></td>
<td>PSRAM currently unused</td>
</tr>
<tr>
<td><em>ESP.getMinFreePsram()</em></td>
<td>Lowest free-PSRAM level reached since boot</td>
</tr>
<tr>
<td><em>ESP.getMaxAllocPsram()</em></td>
<td>Largest single contiguous block that can currently be allocated</td>
</tr>
</tbody>
</table>
<p><em>ESP.getMaxAllocPsram()</em> is especially important when allocating large buffers.</p>
<p>For example, suppose the ESP32 reports:</p>
<pre><code>Free PSRAM:          1200 KB
Largest free block:   700 KB</code></pre>
<p>You have 1.2 MB free in total, but that does <strong>not</strong> mean you can allocate a single 1 MB buffer. The free memory may be split into several smaller regions. The heap is <strong>fragmented</strong>: the free memory exists, but it is split into smaller blocks. Free PSRAM and maximum allocatable PSRAM therefore need to be checked separately. This is what confused my earlier 1 MB allocation test. A successful 1 MB allocation tells me that a 1 MB block was available. It does not tell me that the board physically contains only 1 MB of PSRAM.</p>
<p><img data-dominant-color="cddcce" data-has-transparency="false" style="--dominant-color: #cddcce;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12940 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/before-after-fragmentation.avif" alt="Before and after fragmentation" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/before-after-fragmentation.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/before-after-fragmentation-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Allocating Memory with ps_malloc()</strong></h2>
<p>Next, we can allocate a buffer directly in PSRAM. Arduino-ESP32 provides <em>ps_malloc()</em>, which works much like the normal C <em>malloc()</em> function except that the allocation is specifically requested from PSRAM.</p>
<p>Here's a simple example that tries to allocate 1 MB:</p>
<pre><code>void setup() {
  Serial.begin(115200);
  delay(2000);

  if (!psramFound()) {
    Serial.println("PSRAM not available.");
    return;
  }

  const size_t bufferSize = 1024 * 1024;

  uint8_t *buffer = (uint8_t *)ps_malloc(bufferSize);

  if (buffer == NULL) {
    Serial.println("PSRAM allocation failed!");
    return;
  }

  Serial.println("1 MB PSRAM allocation successful.");

  free(buffer);
  Serial.println("Buffer released.");
}

void loop() {
}</code></pre>
<p>Always check whether the returned pointer is <em>NULL</em>. Large allocations can fail even when PSRAM is enabled. Also notice that we release the PSRAM buffer using:</p>
<pre><code>free(buffer);</code></pre>
<p>You don't need a special <em>ps_free()</em> function.</p>
<p><img data-dominant-color="ebece9" data-has-transparency="false" style="--dominant-color: #ebece9;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12941 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/ps_malloc_workflow.avif" alt="PS_Malloc workflow" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/ps_malloc_workflow.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/ps_malloc_workflow-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Actually Writing Data to PSRAM</strong></h2>
<p>I also wanted to verify that the allocated memory could be written and read back correctly. Let's allocate 1 MB, fill it with data and then verify the values.</p>
<pre><code>void setup() {
  Serial.begin(115200);
  delay(2000);

  if (!psramFound()) {
    Serial.println("PSRAM not detected.");
    return;
  }

  const size_t bufferSize = 1024 * 1024;

  uint8_t *buffer = (uint8_t *)ps_malloc(bufferSize);

  if (buffer == NULL) {
    Serial.println("Could not allocate 1 MB in PSRAM.");
    return;
  }

  Serial.println("1 MB allocated successfully.");

  // Fill the buffer with a repeating pattern
  for (size_t i = 0; i &lt; bufferSize; i++) {
    buffer[i] = i &amp; 0xFF;
  }

  // Verify the contents
  bool passed = true;

  for (size_t i = 0; i &lt; bufferSize; i++) {
    if (buffer[i] != (i &amp; 0xFF)) {
      passed = false;
      break;
    }
  }

  if (passed) {
    Serial.println("PSRAM read/write test PASSED.");
  } else {
    Serial.println("PSRAM read/write test FAILED.");
  }

  free(buffer);
}

void loop() {
}</code></pre>
<p>This verifies more than allocation alone:</p>
<ul>
<li>Confirms PSRAM initialization</li>
<li>Confirms that a large contiguous block is available</li>
<li>Writes across the allocated region</li>
<li>Reads the data back</li>
<li>Checks that the contents were preserved</li>
</ul>
<p>A successful result should look like:</p>
<pre><code>1 MB allocated successfully.
PSRAM read/write test PASSED.</code></pre>
<p><img data-dominant-color="e0eaea" data-has-transparency="false" style="--dominant-color: #e0eaea;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12955 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-alloc-test.avif" alt="" width="612" height="255" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-alloc-test.avif 612w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-alloc-test-300x125.avif 300w" sizes="auto, (max-width: 612px) 100vw, 612px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ps_malloc() vs malloc() vs heap_caps_malloc()</strong></h2>
<p><em>ps_malloc()</em> is not the only allocator that can use PSRAM. ESP32 applications also use <em>malloc()</em>. With PSRAM enabled, the ESP32 heap allocator can also place some ordinary allocations in external RAM. That means ordinary calls to <em>malloc()</em> may also use PSRAM depending on the allocation size, available memory, and configuration.</p>
<p>This lets existing libraries use the larger heap without calling <em>ps_malloc()</em> directly. For memory tests, I use <em>ps_malloc()</em> so I know the buffer is coming from PSRAM:</p>
<pre><code>uint8_t *buffer = (uint8_t *)ps_malloc(bufferSize);</code></pre>
<p>For even more control, we can use the ESP32 capability-based allocator:</p>
<pre><code>#include "esp_heap_caps.h"

uint8_t *buffer = (uint8_t *)heap_caps_malloc(
    bufferSize,
    MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT
);</code></pre>
<p>The <em>MALLOC_CAP_SPIRAM</em> flag specifically requests external PSRAM. For most Arduino projects, <em>ps_malloc()</em> is easier to read. I would reach for <em>heap_caps_malloc()</em> when I need more specific control over the type of memory being requested.</p>
<p><img data-dominant-color="e4e5e5" data-has-transparency="false" style="--dominant-color: #e4e5e5;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12932 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32_malloc.avif" alt="ESP32 PSRAM malloc()" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32_malloc.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32_malloc-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>When Should You Use PSRAM?</strong></h2>
<p>PSRAM is best suited to <strong>large data where the lowest possible access latency is not required</strong>.</p>
<p>Good candidates include:</p>
<ul>
<li>Camera frames</li>
<li>Image buffers</li>
<li>Audio sample buffers</li>
<li>Large arrays</li>
<li>Machine-learning tensor arenas</li>
<li>Large strings</li>
<li>JSON documents</li>
<li>Network receive buffers</li>
<li>Temporary file buffers</li>
</ul>
<p>For example, an ESP32-S3 used for <a href="https://www.teachmemicro.com/tinyml-with-esp32-tutorial/">TinyML</a> may need hundreds of kilobytes just for model tensors and intermediate data. Moving this kind of bulk data to PSRAM leaves more internal SRAM available for the rest of the application. A 300 KB buffer might consume an uncomfortable amount of internal RAM. Put the same bulk data in PSRAM and you preserve internal memory for the operating system, Wi-Fi, task stacks, and peripherals.</p>
<p><img data-dominant-color="d6dfe5" data-has-transparency="false" style="--dominant-color: #d6dfe5;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12933 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-use-case.avif" alt="PSRAM use case" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-use-case.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-use-case-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>PSRAM Is Not Just More Internal SRAM</strong></h2>
<p>An ESP32-S3 with 8 MB of PSRAM does not behave like a microcontroller with 8 MB of internal SRAM. External PSRAM sits behind the ESP32-S3's memory interface and cache. Frequently accessed data can perform reasonably well, but large accesses eventually exceed what the cache can hide. Use PSRAM when <strong>capacity</strong> matters. Keep frequently accessed or timing-sensitive data in internal SRAM. There is little benefit in moving small variables to PSRAM just because it is available. Use PSRAM for the large objects that actually justify it.</p>
<p><img data-dominant-color="cbd2d0" data-has-transparency="false" style="--dominant-color: #cbd2d0;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12934 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/sram-vs-psram.avif" alt="SRAM vs PSRAM" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/sram-vs-psram.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/sram-vs-psram-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Some Data Still Needs Internal RAM</strong></h2>
<p>Some data cannot or should not be placed in PSRAM. Peripheral and DMA operations can have additional memory requirements. Some hardware structures must remain in internal memory even if the main data buffer lives in PSRAM. FreeRTOS task stacks are also normally kept in internal RAM.</p>
<p>PSRAM and external flash also share parts of the ESP32-S3 external-memory and cache system. This becomes relevant if you are writing lower-level code, performing flash operations, or debugging difficult timing-related crashes. This usually does not matter for a simple image or data buffer, but it matters more in drivers, networking, DMA, flash operations, and real-time applications.</p>
<p>A practical rule is:</p>
<div class="box info  aligncenter"><div class="box-inner-block"><i class="fa tie-shortcode-boxicon"></i>
			
<p><strong>Use PSRAM for bulk storage. Keep timing-critical and hardware-sensitive data in internal RAM unless you have a specific reason not to.</strong></p>

			</div></div>
<figure id="attachment_12935" aria-describedby="caption-attachment-12935" style="width: 724px" class="wp-caption aligncenter"><img data-dominant-color="cdd4d7" data-has-transparency="false" style="--dominant-color: #cdd4d7;" loading="lazy" decoding="async" class="wp-image-12935 size-full not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-applications.avif" alt="PSRAM applications" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-applications.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-applications-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /><figcaption id="caption-attachment-12935" class="wp-caption-text"><em>Left: PSRAM Applications: images, audio, large arrays, ML tensors; Right: Internal SRAM Applications: task stacks, timing-critical variables, hardware/DMA control structures</em></figcaption></figure>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Monitoring PSRAM While Your Program Runs</strong></h2>
<p>A memory leak or fragmentation problem may not become visible until the program has been running for a while. For debugging, I find it useful to periodically print both free PSRAM and the largest allocatable block.</p>
<pre><code>void printPsramStatus() {
  Serial.print("Free PSRAM: ");
  Serial.print(ESP.getFreePsram() / 1024);
  Serial.println(" KB");

  Serial.print("Largest block: ");
  Serial.print(ESP.getMaxAllocPsram() / 1024);
  Serial.println(" KB");

  Serial.print("Minimum free PSRAM: ");
  Serial.print(ESP.getMinFreePsram() / 1024);
  Serial.println(" KB");
}</code></pre>
<p>Call this at different stages of your program.</p>
<p>For example:</p>
<pre><code>printPsramStatus();

camera.begin();

printPsramStatus();

loadModel();

printPsramStatus();</code></pre>
<p>Comparing these readings shows which initialization step consumed the PSRAM. <em>getMinFreePsram()</em> records the lowest free-PSRAM level reached since boot. A temporary allocation might have already been released by the time you check the Serial Monitor, but the minimum value can reveal that memory usage briefly spiked.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Why Can an Allocation Fail When PSRAM Is Still Free?</strong></h2>
<p>Suppose your Serial Monitor says:</p>
<pre><code>Free PSRAM: 1300 KB</code></pre>
<p>Then this fails:</p>
<pre><code>ps_malloc(1024 * 1024);</code></pre>
<p>The reason becomes clear when you check the largest allocatable block.</p>
<p>Check:</p>
<pre><code>ESP.getMaxAllocPsram()</code></pre>
<p>You may discover something like:</p>
<pre><code>Free PSRAM:       1300 KB
Largest block:     800 KB</code></pre>
<p>The allocator has 1.3 MB available overall, but there is no single 1 MB continuous region. This can happen after many differently sized allocations and frees. If your application depends on a very large buffer, allocate it early during startup rather than waiting until the heap has been used heavily. If your application needs one large buffer, allocate it early before repeated allocations fragment the heap.</p>
<p><img data-dominant-color="cbddcf" data-has-transparency="false" style="--dominant-color: #cbddcf;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12936 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-memory-fragmentation.avif" alt="ESP32 Memory Fragmentation" width="887" height="444" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-memory-fragmentation.avif 887w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-memory-fragmentation-300x150.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-memory-fragmentation-768x384.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-memory-fragmentation-660x330.avif 660w" sizes="auto, (max-width: 887px) 100vw, 887px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S3 PSRAM Troubleshooting</strong></h2>
<h3><strong>PSRAM Size Shows 0 Bytes</strong></h3>
<p>First check:</p>
<pre><code>psramFound()</code></pre>
<p>If that also returns false, verify the Arduino IDE PSRAM configuration.</p>
<p>For R2 devices:</p>
<pre><code>Tools &gt; PSRAM &gt; QSPI PSRAM</code></pre>
<p>For R8 devices:</p>
<pre><code>Tools &gt; PSRAM &gt; OPI PSRAM</code></pre>
<p>Also confirm that your ESP32-S3 variant actually contains PSRAM.</p>
<h3><strong>The Board Keeps Rebooting</strong></h3>
<p>An incorrect flash or PSRAM mode can prevent some ESP32-S3 boards from starting correctly. Check the exact chip or module marking rather than guessing based on the development-board name.</p>
<h3><strong>ps_malloc() Returns NULL</strong></h3>
<p>Print:</p>
<pre><code>ESP.getFreePsram()
ESP.getMaxAllocPsram()</code></pre>
<p>The largest available block may be smaller than the buffer you are requesting. Also make sure the result of every allocation is checked before using the pointer.</p>
<h3><strong>My 2 MB Board Shows Slightly Less Than 2 MB Free</strong></h3>
<p>That is normal. <em>ESP.getPsramSize()</em> reports the total detected PSRAM, while <em>ESP.getFreePsram()</em> tells you how much remains available after system initialization and allocations. Don't use the free-memory value to identify how much memory is physically there.</p>
<h3><strong>I Allocated 1 MB. Does That Mean My Board Has 1 MB PSRAM?</strong></h3>
<p>No.</p>
<p>It only proves that the allocator successfully found a 1 MB continuous block.</p>
<p>Use:</p>
<pre><code>ESP.getPsramSize()</code></pre>
<p>to determine the total detected PSRAM.</p>
<p><img data-dominant-color="e8edee" data-has-transparency="false" style="--dominant-color: #e8edee;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12937 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-troubleshooting.avif" alt="ESP32 PSRAM troubleshooting" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-troubleshooting.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/psram-troubleshooting-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Putting It All Together</strong></h2>
<p>I use the following sketch as a quick PSRAM check on a new ESP32-S3 board:</p>
<pre><code>void setup() {
  Serial.begin(115200);
  delay(2000);

  Serial.println();
  Serial.println("ESP32-S3 PSRAM Check");
  Serial.println("====================");

  if (!psramFound()) {
    Serial.println("PSRAM: NOT DETECTED");
    return;
  }

  Serial.println("PSRAM: DETECTED");

  Serial.printf(
    "Total PSRAM: %.2f MB\n",
    ESP.getPsramSize() / 1024.0 / 1024.0
  );

  Serial.printf(
    "Free PSRAM: %.2f MB\n",
    ESP.getFreePsram() / 1024.0 / 1024.0
  );

  Serial.printf(
    "Largest block: %.2f MB\n",
    ESP.getMaxAllocPsram() / 1024.0 / 1024.0
  );

  const size_t testSize = 1024 * 1024;

  uint8_t *testBuffer = (uint8_t *)ps_malloc(testSize);

  if (testBuffer != NULL) {
    Serial.println("1 MB allocation: PASS");
    memset(testBuffer, 0x55, testSize);
    Serial.println("1 MB write: PASS");
   free(testBuffer);
  } else {
    Serial.println("1 MB allocation: FAIL");
  }
}

void loop() {
}</code></pre>
<p>From this output I can verify that PSRAM is detected, see the total and available memory, check the largest block, and confirm that a reasonably large buffer can actually be allocated.</p>
<p><img data-dominant-color="e2ecec" data-has-transparency="false" style="--dominant-color: #e2ecec;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12956 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-check.avif" alt="" width="614" height="247" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-check.avif 614w, https://www.teachmemicro.com/wp-content/uploads/2026/09/esp32-psram-check-300x121.avif 300w" sizes="auto, (max-width: 614px) 100vw, 614px" /></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Conclusion</strong></h2>
<p>PSRAM is useful when an ESP32-S3 project needs large image, audio, machine-learning, or network buffers. It should not be treated the same as internal SRAM. First, make sure your particular ESP32-S3 actually contains PSRAM. Then configure the correct QSPI or OPI mode in Arduino IDE and verify it using <em>psramFound()</em> and <em>ESP.getPsramSize()</em>. After that, functions such as <em>ps_malloc()</em> make it straightforward to place large buffers in external memory.</p>
<p>My testing showed why <strong>total memory, free memory, and largest allocatable memory are three different things</strong>. Checking all three usually makes allocation problems much easier to trace.</p>
<p>In the next experiment, I will take this a step further and compare <strong>ESP32-S3 internal SRAM against PSRAM</strong>, including allocation size, read/write speed and what happens as the working buffer becomes larger.</p>
<p><img data-dominant-color="d5e1e3" data-has-transparency="false" style="--dominant-color: #d5e1e3;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12938 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/09/internal-sram-vs-psram-benchmark.avif" alt="ESP32 Internal SRAM vs PSRAM benchmark" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/09/internal-sram-vs-psram-benchmark.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/09/internal-sram-vs-psram-benchmark-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<p>The post <a href="https://www.teachmemicro.com/esp32-s3-psram-how-to-enable-detect-and-use-it-in-arduino/">ESP32-S3 PSRAM: How to Enable, Detect and Use It in Arduino</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Program a PIC Microcontroller with PICkit and MPLAB IPE</title>
		<link>https://www.teachmemicro.com/program-pic-microcontroller-pickit/</link>
					<comments>https://www.teachmemicro.com/program-pic-microcontroller-pickit/#respond</comments>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Mon, 24 Aug 2026 23:00:31 +0000</pubDate>
				<category><![CDATA[PIC Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=2683</guid>

					<description><![CDATA[<p>We are done with a few basics on PIC microcontrollers. Now it's time to learn how to load a program to the device itself. There are, in fact, plenty of circuits that can load a HEX file to a PIC microcontroller. Older programmers used the classical RS-232 and parallel ports, while newer programmers communicate through USB. &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/program-pic-microcontroller-pickit/">How to Program a PIC Microcontroller with PICkit and MPLAB IPE</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>We are done with a few <a href="https://www.teachmemicro.com/pic-microcontroller-tutorials/">basics on PIC microcontrollers</a>. Now it's time to learn how to load a program to the device itself. There are, in fact, plenty of circuits that can load a HEX file to a PIC microcontroller. Older programmers used the classical RS-232 and parallel ports, while newer programmers communicate through USB. Of course, you will have a hard time looking for computers with RS-232 and parallel ports these days, so USB is now the practical choice.</p>
<p>For PIC microcontrollers, one of the easiest ways to program a device is through Microchip's <em>PICkit programmer/debugger</em>.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>PICkit Versions</strong></h2>
<p>Microchip has released several generations of the PICkit programmer. The original version of this article stopped at PICkit 4, which was released in 2018. Since then, Microchip has introduced <em>PICkit 5</em>, which is now the current version.</p>
<h3><strong>PICkit 1</strong></h3>
<p><img data-dominant-color="706e6d" data-has-transparency="false" style="--dominant-color: #706e6d;" loading="lazy" decoding="async" class="wp-image-12636 alignnone not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2027/11/pickit-1.avif" alt="" width="431" height="284" srcset="https://www.teachmemicro.com/wp-content/uploads/2027/11/pickit-1.avif 615w, https://www.teachmemicro.com/wp-content/uploads/2027/11/pickit-1-300x198.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2027/11/pickit-1-310x205.avif 310w" sizes="auto, (max-width: 431px) 100vw, 431px" /></p>
<p>The original <a href="https://www.microchip.com/en-us/development-tool/dv164101"><em>PICkit 1 Flash Starter Kit</em></a> appeared in the early 2000s and was aimed mainly at beginners working with Microchip's small 8-pin and 14-pin Flash PIC microcontrollers. It consisted of a development board connected to a computer through USB. Compared to current programmers, device support was very limited, but the important thing was that it provided an inexpensive USB-based way to start programming PIC devices. Today, PICkit 1 is mainly of historical interest. Microchip no longer recommends it for new development.</p>
<h3><strong>PICkit 2</strong></h3>
<p><img loading="lazy" decoding="async" class="" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRhvHolxpdbtL14jSNbPoQyIFJm1gOF3HE1exoGfsxw_yzHNUiZhtV1WdU&amp;s=10" alt="PICkit 2 PICKIT2 Development Programmer/Debugger - ขาย Arduino อุปกรณ์ฯ ,ESR meter 18650 : Inspired by LnwShop.com (v2)" width="348" height="348" /></p>
<p><a href="https://www.microchip.com/en-us/development-tool/pg164120"><em>PICkit 2</em></a> was a major improvement and became very popular among PIC hobbyists. Unlike the first PICkit, it could program a much wider variety of PIC microcontrollers using <em>ICSP or In-Circuit Serial Programming</em>. One useful feature was its standalone PICkit 2 Programmer software. You could select a HEX file, connect a target PIC and program it without opening an entire MPLAB project. PICkit 2 also included some useful extra tools, including a UART terminal. However, both its software and supported-device list are now considered legacy. Microchip keeps the old PICkit 2 software in its development-tool archive.</p>
<h3><strong>PICkit 3</strong></h3>
<p><img loading="lazy" decoding="async" class="" src="https://cdn.velleman.eu/images/products/7/pickit3.jpg" alt="PICKIT3: USB PIC 'IN-CIRCUIT' PROGRAMMER AND DEBUGGER – Velleman – Wholesaler and developer of electronics" width="387" height="290" /></p>
<p><a href="https://www.microchip.com/en-us/development-tool/pg164130"><em>PICkit 3</em></a> replaced PICkit 2 and provided closer integration with MPLAB and later <em>MPLAB X IDE</em>. PICkit 3 can both <em>program and debug</em> compatible PIC microcontrollers. Debugging is particularly useful because you can stop your program at a breakpoint, inspect registers and variables, execute instructions step by step, and then continue running the program. You will still find plenty of PICkit 3 programmers and inexpensive clones for sale. They work well with many older PIC devices, including microcontrollers such as the PIC16F84A and PIC16F877A. However, Microchip stopped adding new device support to PICkit 3 on <em>June 1, 2019</em>, so I would not recommend buying one for a new development setup unless you specifically need it for older PICs.</p>
<h3><strong>PICkit 4</strong></h3>
<p><img decoding="async" src="https://media.microchip.com/assets/7/7/d/4/858593bf831c147314a8c3414bbc.l.png" /></p>
<p>Microchip introduced the <em>MPLAB <a href="https://www.microchip.com/en-us/development-tool/pg164140">PICkit 4</a></em> on February 27, 2018, as the replacement for PICkit 3. The PICkit 4 increased programming speed considerably, supported a wider target-voltage range and added support for more programming and debugging interfaces. Microchip stated that it could program devices up to five times faster than PICkit 3. PICkit 4 was an excellent programmer and remains perfectly usable if you already own one. However, Microchip now lists it as an archived programmer that is no longer available as its current development tool.</p>
<h3><strong>PICkit 5</strong></h3>
<p><img decoding="async" src="https://media.microchip.com/assets/7/1/6/4/a108f758319b079b4bc396785fc1.l.png" /></p>
<p>The current version is the <em>MPLAB PICkit 5 In-Circuit Debugger/Programmer</em>, released in May 2023. <a href="https://www.microchip.com/en-us/development-tool/pg164150">PICkit 5</a> supports not only PIC and dsPIC devices but also Microchip's AVR, SAM, and Arm-based devices. It connects to the computer through USB Type-C and works with MPLAB X IDE and MPLAB IPE. Another interesting feature is its Programmer-to-Go capability. PICkit 5 can work with Microchip's mobile Programmer-to-Go application through Bluetooth Low Energy, making it possible to program devices in the field without keeping the programmer connected to a PC.</p>
<p>Here's a quick comparison:</p>
<table>
<thead>
<tr>
<th>PICkit</th>
<th>Best Description Today</th>
<th>Recommended for New Projects?</th>
</tr>
</thead>
<tbody>
<tr>
<td>PICkit 1</td>
<td>Early USB starter programmer</td>
<td>No</td>
</tr>
<tr>
<td>PICkit 2</td>
<td>Popular legacy PIC programmer</td>
<td>No</td>
</tr>
<tr>
<td>PICkit 3</td>
<td>Legacy programmer/debugger</td>
<td>No</td>
</tr>
<tr>
<td>PICkit 4</td>
<td>Still capable but archived</td>
<td>Only if you already own one</td>
</tr>
<tr>
<td>PICkit 5</td>
<td>Current programmer/debugger</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<p>For a new setup today, <em>PICkit 5 is the obvious choice</em>. If you already have a PICkit 3 or PICkit 4 and your PIC microcontroller is supported, however, there is no reason you can't continue using it.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>How PICkit Programs a PIC Microcontroller</strong></h2>
<p>PICkit programmers normally use <em>ICSP</em>, which stands for <em>In-Circuit Serial Programming</em>.</p>
<p>The advantage of ICSP is right there in the name: you can program the microcontroller while it is already installed in your circuit. You don't need to remove the PIC and put it into a separate programming socket every time you change your program.</p>
<p>For most traditional PIC microcontrollers, five important connections are involved:</p>
<table>
<thead>
<tr>
<th>PICkit Pin</th>
<th>Signal</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>MCLR/VPP</td>
<td>Reset and programming-voltage signal</td>
</tr>
<tr>
<td>2</td>
<td>VDD</td>
<td>Target supply-voltage sense/power</td>
</tr>
<tr>
<td>3</td>
<td>VSS</td>
<td>Ground</td>
</tr>
<tr>
<td>4</td>
<td>PGD / ICSPDAT</td>
<td>Programming data</td>
</tr>
<tr>
<td>5</td>
<td>PGC / ICSPCLK</td>
<td>Programming clock</td>
</tr>
</tbody>
</table>
<p>The exact pins on the PIC microcontroller vary according to the device, so always check its datasheet before connecting your programmer. Microchip recommends a typical pull-up resistor of around <em>10 kΩ between MCLR and VDD</em>. You should also avoid putting large capacitors or other circuits directly on the PGC and PGD lines because they may interfere with programming signals.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Connecting PICkit to a PIC16F84A</strong></h2>
<p>Let's use the <em>PIC16F84A</em> from our previous tutorials as an example. For the 18-pin PDIP version of the PIC16F84A, the ICSP connections are:</p>
<table>
<thead>
<tr>
<th>PICkit Signal</th>
<th>PIC16F84A Pin</th>
</tr>
</thead>
<tbody>
<tr>
<td>MCLR/VPP</td>
<td>Pin 4 - MCLR</td>
</tr>
<tr>
<td>VDD</td>
<td>Pin 14 - VDD</td>
</tr>
<tr>
<td>VSS</td>
<td>Pin 5 - VSS</td>
</tr>
<tr>
<td>PGD / ICSPDAT</td>
<td>Pin 13 - RB7</td>
</tr>
<tr>
<td>PGC / ICSPCLK</td>
<td>Pin 12 - RB6</td>
</tr>
</tbody>
</table>
<p>The PIC16F84A supports ICSP through RB6 and RB7, so the device can remain installed on your breadboard or PCB while you program it.</p>
<p><img data-dominant-color="d9ded7" data-has-transparency="false" style="--dominant-color: #d9ded7;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12634 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2027/11/pickit-to-pic16f84a.avif" alt="PICKIT to PIC16F84A" width="827" height="476" srcset="https://www.teachmemicro.com/wp-content/uploads/2027/11/pickit-to-pic16f84a.avif 827w, https://www.teachmemicro.com/wp-content/uploads/2027/11/pickit-to-pic16f84a-300x173.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2027/11/pickit-to-pic16f84a-768x442.avif 768w" sizes="auto, (max-width: 827px) 100vw, 827px" /></p>
<p>Make sure you also connect the normal power and oscillator circuitry required by the PIC16F84A. One detail that often confuses beginners is target power. The PIC can either be powered by your circuit or, depending on the programmer and configuration, supplied from the programmer. For normal development, I prefer powering the target circuit from its own regulated supply and allowing the PICkit to sense that voltage. Microchip also recommends external target power for its normal debugger connection.</p>
<p>Most importantly, <em>never apply a supply voltage higher than the maximum allowed by your particular PIC microcontroller</em>.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What is a HEX File?</strong></h2>
<p>When you write a PIC program in assembly language or C, the microcontroller doesn't directly understand your source code.</p>
<p>The assembler or compiler first converts your program into machine instructions. The build process then generates a <em>HEX file</em> containing the data that will eventually be written to the PIC's program memory.</p>
<p>For example, if your MPLAB project is called:</p>
<pre><code>blink.X</code></pre>
<p>building that project may produce a file similar to:</p>
<pre><code>blink.X.production.hex</code></pre>
<p>You don't normally need to open or edit this file manually. You simply give it to the programmer.</p>
<p>The PICkit then transfers the contents of the HEX file into the microcontroller's Flash program memory.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Loading a HEX File Using MPLAB IPE</strong></h2>
<p>For programming an existing HEX file, the easiest current method is <em>MPLAB IPE</em>, or Integrated Programming Environment. MPLAB IPE installs together with MPLAB X IDE. Unlike the full IDE, IPE concentrates on programming devices rather than writing and compiling programs. Microchip itself recommends IPE when you already have a HEX file that you simply want to program into a device.</p>
<h4><strong>Here's the complete process:</strong></h4>
<p>1. <em>Install MPLAB X IDE and MPLAB IPE.</em> Download the current <a href="https://www.microchip.com/en-us/tools-resources/develop/mplab-x-ide">MPLAB X installer</a> from Microchip and make sure MPLAB IPE is selected during installation. Connect your PICkit to the computer afterward.</p>
<p>2. <em>Connect the PICkit to your target PIC</em>. Wire MCLR/VPP, VDD, VSS, PGD, and PGC correctly. Pay particular attention to the Pin 1 marker on the PICkit connector.</p>
<p>3. <em>Power the target circuit</em>. If your board uses its own power supply, turn it on before attempting to connect. The PICkit needs to detect the target supply voltage.</p>
<p>4. <em>Open MPLAB IPE</em>. Select the correct device family and then select the exact PIC microcontroller. For our example, this would be <em>PIC16F84A</em>.</p>
<p>5. <em>Select the programmer</em>. Choose the connected PICkit from the Tool field and click <em>Connect</em>. The Output window should indicate that the programmer has connected and the target has been detected.</p>
<p>6. <em>Load the HEX file</em>. Select <em>File &gt; Import &gt; Hex</em>, browse to your compiled `.hex` file and open it. IPE should display a message indicating that the HEX file loaded successfully.</p>
<p>7. <em>Click Program</em>. MPLAB IPE erases the required memory locations, writes your program and verifies the contents afterward.</p>
<p>8. <em>Check the result</em>. A successful operation produces a <em>Programming/Verify complete</em> message in the Output window.</p>
<p>9. <em>Run your circuit</em>. Disconnect the programmer if necessary or simply reset/power-cycle the PIC. Your program should now execute from Flash memory.</p>
<p>&nbsp;</p>
<p>If you're using the LED-blinking program from our earlier PIC16F84A tutorial, the LED should now start blinking exactly as it did in the simulator—except this time the code is running on a real PIC microcontroller.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Programming Directly from MPLAB X IDE</strong></h2>
<p>If you are actively writing the program yourself, you don't actually have to export the HEX file and manually open MPLAB IPE every time. MPLAB X IDE can build and program the device directly.  After connecting the PICkit, open your project properties and select the PICkit as your hardware tool. Build the project and use the <em>Make and Program Device</em> command. MPLAB X will compile the project, create the HEX file internally, and immediately send it to the PICkit.</p>
<p>This gives us two slightly different workflows:</p>
<table>
<thead>
<tr>
<th>Situation</th>
<th>Recommended Tool</th>
</tr>
</thead>
<tbody>
<tr>
<td>Writing and debugging your own PIC code</td>
<td>MPLAB X IDE</td>
</tr>
<tr>
<td>You already have a HEX file</td>
<td>MPLAB IPE</td>
</tr>
<tr>
<td>Repeated production programming</td>
<td>MPLAB IPE</td>
</tr>
</tbody>
</table>
<p>For beginners learning PIC assembly or XC8 programming, you will probably spend most of your time in MPLAB X and only use IPE when you need to program an existing HEX file.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Common PICkit Programming Problems</strong></h2>
<h3><strong>Target Device Not Detected</strong></h3>
<p>This is probably the most common PICkit error. First, make sure the PIC has power. Then verify the VSS connection and check that the PICkit's Pin 1 is connected to MCLR/VPP rather than accidentally reversing the connector. Also verify that you selected the correct PIC model in MPLAB.</p>
<h3><strong>Failed to Program Device</strong></h3>
<p>Check the PGC and PGD wiring. These are the two lines carrying the actual programming clock and data. Circuits connected to these pins can also interfere with ICSP. Large capacitors are particularly problematic because they slow the edges of the programming signals.</p>
<h3><strong>Wrong Device ID</strong></h3>
<p>If MPLAB reports a device ID different from what it expects, confirm that the selected PIC model matches the physical device on your breadboard. For example, don't select PIC16F84A if you actually installed a PIC16F628A.</p>
<h3><strong>PIC Works in the Programmer but Not After Disconnecting It</strong></h3>
<p>If programming succeeds but your application doesn't run afterward, check the rest of the target circuit. The PIC may need an oscillator, proper configuration bits, MCLR pull-up, power-supply decoupling, and other supporting components depending on the device. The programmer only guarantees that your program was written correctly. It cannot fix an incorrectly wired target circuit.</p>
<h3><strong>PICkit 3 Cannot Program a Newer PIC</strong></h3>
<p>This can simply be a device-support issue. PICkit 3 hasn't received support for new devices since 2019. If you are working with a newer Microchip device, use PICkit 5 or another currently supported Microchip programmer instead.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Which PICkit Should You Buy?</strong></h2>
<p>If you are buying a programmer today, I recommend <em>PICkit 5</em>. It provides current device support and works with the latest Microchip development tools. It also gives you debugging capabilities, which become increasingly valuable as your programs grow more complicated.</p>
<p>If you already have a genuine PICkit 4, keep using it as long as your target device remains supported.</p>
<p>PICkit 3 is still useful for learning with older devices such as the PIC16F84A, PIC16F628A, and PIC16F877A, especially if you already own one. However, I wouldn't purchase PICkit 3 specifically for a new collection of Microchip devices because its supported-device list is effectively frozen.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Conclusion</strong></h2>
<p>At this point, we have completed the whole basic PIC development cycle. We write our program in assembly or C, compile it into machine code, generate a HEX file, and finally use a programmer such as PICkit to transfer that program into the microcontroller. The important part to remember is that modern PIC programmers use <em>ICSP</em>, allowing you to program the PIC without removing it from your circuit. Once you have MCLR/VPP, VDD, VSS, PGD and PGC wired correctly, programming becomes a straightforward process.</p>
<p>For an existing HEX file, use <em>MPLAB IPE</em>. If you're developing the program yourself, you can program the device directly from <em>MPLAB X IDE</em>. From here, the PIC is finally ready to leave the simulator and start controlling real hardware. If you're following my PIC16F84A tutorials, the next step is to start writing more useful programs and learn how the PIC executes assembly-language instructions.</p>
<p>The post <a href="https://www.teachmemicro.com/program-pic-microcontroller-pickit/">How to Program a PIC Microcontroller with PICkit and MPLAB IPE</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.teachmemicro.com/program-pic-microcontroller-pickit/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ESP32-S3 Mini Getting Started Guide: Super Mini Pinout, Arduino IDE Setup, PSRAM and RGB LED</title>
		<link>https://www.teachmemicro.com/esp32-s3-mini-getting-started-guide/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Mon, 24 Aug 2026 04:33:59 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12625</guid>

					<description><![CDATA[<p>The ESP32-S3 Mini, often sold as the ESP32-S3 Super Mini, packs a surprisingly powerful ESP32-S3 microcontroller into a board only slightly larger than its USB-C connector. Despite its size, the board gives you a dual-core processor, Wi-Fi, Bluetooth Low Energy, native USB, PSRAM on some versions, and enough GPIO pins for many embedded projects. There &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-s3-mini-getting-started-guide/">ESP32-S3 Mini Getting Started Guide: Super Mini Pinout, Arduino IDE Setup, PSRAM and RGB LED</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The <strong>ESP32-S3 Mini</strong>, often sold as the <strong>ESP32-S3 Super Mini</strong>, packs a surprisingly powerful ESP32-S3 microcontroller into a board only slightly larger than its USB-C connector. Despite its size, the board gives you a dual-core processor, Wi-Fi, Bluetooth Low Energy, native USB, PSRAM on some versions, and enough GPIO pins for many embedded projects.</p>
<p>There is one problem: documentation for these generic Super Mini boards is often incomplete. Different online sellers sometimes show different pin diagrams, memory specifications, and even different onboard LED connections.</p>
<p>In this guide, I will show you how to set up the ESP32-S3 Super Mini in the Arduino IDE, identify the hardware you actually received, test its flash and PSRAM, and control the onboard RGB LED. We will also look at the pinout and some common upload problems.</p>
<p><span id="more-12625"></span></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What is the ESP32-S3 Mini?</strong></h2>
<p><img data-dominant-color="c3c3c3" data-has-transparency="false" style="--dominant-color: #c3c3c3;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12644 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cover.avif" alt="ESP32-S3 Super Mini Hero" width="597" height="517" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cover.avif 597w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cover-300x260.avif 300w" sizes="auto, (max-width: 597px) 100vw, 597px" /></p>
<p>The ESP32-S3 Super Mini is a small third-party development board built around the <strong>ESP32-S3</strong> microcontroller. The ESP32-S3 itself uses two Xtensa LX7 CPU cores running at up to 240 MHz and includes 2.4 GHz Wi-Fi and Bluetooth Low Energy.</p>
<p>The particular Super Mini version commonly sold online uses an <strong>ESP32-S3FH4R2</strong>. The letters at the end are important:</p>
<ul>
<li><strong>F</strong> - the chip contains flash memory.</li>
<li><strong>H4</strong> - 4 MB of flash is integrated into the package.</li>
<li><strong>R2</strong> - 2 MB of PSRAM is integrated into the package.</li>
</ul>
<p>This makes the small board considerably more capable than its physical size suggests. In particular, the PSRAM can be useful for frame buffers, image processing, web applications, and other programs that need more working memory than the ESP32-S3's internal RAM can comfortably provide.</p>
<p>However, not every board advertised as an "ESP32-S3 Mini" necessarily contains the same chip. Before relying on the memory specifications printed by a seller, I recommend checking the marking on the ESP32-S3 itself and then verifying the memory through software.</p>
<p><img data-dominant-color="464342" data-has-transparency="false" style="--dominant-color: #464342;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12645 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cpu-closeup.avif" alt="ESP32-S3 Super Mini CPU Close Up" width="652" height="869" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cpu-closeup.avif 652w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cpu-closeup-225x300.avif 225w" sizes="auto, (max-width: 652px) 100vw, 652px" /></p>
<h3><strong>ESP32-S3 Mini vs ESP32-S3-MINI-1</strong></h3>
<p>There is also an official Espressif product named the <strong>ESP32-S3-MINI-1</strong>. Do not confuse this with the ESP32-S3 Super Mini development board shown in this tutorial.</p>
<p>The ESP32-S3-MINI-1 is an Espressif module intended to be soldered into another PCB. The ESP32-S3 Super Mini, meanwhile, is a complete development board with a USB-C connector, voltage regulator, buttons, and exposed pins.</p>
<p>Online stores sometimes shorten both names to "ESP32-S3 Mini", which is why the terminology can be confusing.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S3 Super Mini Specifications</strong></h2>
<p>For the common ESP32-S3FH4R2-based version, the important specifications are:</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>ESP32-S3 Super Mini</th>
</tr>
</thead>
<tbody>
<tr>
<td>Microcontroller</td>
<td>ESP32-S3</td>
</tr>
<tr>
<td>CPU</td>
<td>Dual-core Xtensa LX7</td>
</tr>
<tr>
<td>Maximum CPU Frequency</td>
<td>240 MHz</td>
</tr>
<tr>
<td>Internal SRAM</td>
<td>512 KB</td>
</tr>
<tr>
<td>Flash</td>
<td>4 MB on ESP32-S3FH4R2 version</td>
</tr>
<tr>
<td>PSRAM</td>
<td>2 MB QSPI on ESP32-S3FH4R2 version</td>
</tr>
<tr>
<td>Wi-Fi</td>
<td>2.4 GHz 802.11 b/g/n</td>
</tr>
<tr>
<td>Bluetooth</td>
<td>Bluetooth Low Energy</td>
</tr>
<tr>
<td>USB</td>
<td>Native USB through USB-C</td>
</tr>
<tr>
<td>Logic Voltage</td>
<td>3.3 V</td>
</tr>
<tr>
<td>Onboard RGB LED</td>
<td>Typically WS2812 on GPIO48</td>
</tr>
</tbody>
</table>
<p>The <strong>3.3 V logic level</strong> is worth remembering. Do not apply 5 V directly to an ESP32-S3 GPIO pin.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S3 Super Mini Pinout</strong></h2>
<p>The compact Super Mini board exposes fewer pins than a full-size ESP32-S3 development board. This is one of the compromises required to make the board so small.</p>
<p>The version used for this guide has GPIO1 through GPIO13 together with the default UART TX and RX pins.</p>
<p><img data-dominant-color="dfdbd6" data-has-transparency="false" style="--dominant-color: #dfdbd6;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12646 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-pinout.avif" alt="" width="1920" height="2106" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-pinout.avif 1920w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-pinout-274x300.avif 274w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-pinout-934x1024.avif 934w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-pinout-768x842.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-pinout-1400x1536.avif 1400w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-pinout-1867x2048.avif 1867w" sizes="auto, (max-width: 1920px) 100vw, 1920px" /></p>
<table>
<thead>
<tr>
<th>Board Label</th>
<th>ESP32-S3 GPIO</th>
<th>Useful Functions</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>TX</td>
<td>GPIO43</td>
<td>UART TX, digital I/O</td>
<td>Default UART0 transmit pin</td>
</tr>
<tr>
<td>RX</td>
<td>GPIO44</td>
<td>UART RX, digital I/O</td>
<td>Default UART0 receive pin</td>
</tr>
<tr>
<td>1</td>
<td>GPIO1</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Good general-purpose pin</td>
</tr>
<tr>
<td>2</td>
<td>GPIO2</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Good general-purpose pin</td>
</tr>
<tr>
<td>3</td>
<td>GPIO3</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Strapping pin; use with some care at boot</td>
</tr>
<tr>
<td>4</td>
<td>GPIO4</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Good general-purpose pin</td>
</tr>
<tr>
<td>5</td>
<td>GPIO5</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Good general-purpose pin</td>
</tr>
<tr>
<td>6</td>
<td>GPIO6</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Good general-purpose pin</td>
</tr>
<tr>
<td>7</td>
<td>GPIO7</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Good general-purpose pin</td>
</tr>
<tr>
<td>8</td>
<td>GPIO8</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Useful for I2C</td>
</tr>
<tr>
<td>9</td>
<td>GPIO9</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>Useful for I2C</td>
</tr>
<tr>
<td>10</td>
<td>GPIO10</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>General-purpose</td>
</tr>
<tr>
<td>11</td>
<td>GPIO11</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>ADC2 channel</td>
</tr>
<tr>
<td>12</td>
<td>GPIO12</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>ADC2 channel</td>
</tr>
<tr>
<td>13</td>
<td>GPIO13</td>
<td>ADC, touch, PWM, digital I/O</td>
<td>ADC2 channel</td>
</tr>
<tr>
<td>RGB</td>
<td>GPIO48*</td>
<td>WS2812 RGB LED</td>
<td>Onboard; not normally exposed</td>
</tr>
</tbody>
</table>
<p><em>*GPIO48 is commonly used for the RGB LED on this Super Mini layout. Generic boards can vary, so check your board if the RGB example later in this guide does not work.</em></p>
<h3><strong>Where are GPIO19 and GPIO20?</strong></h3>
<p>The ESP32-S3 has native USB support. GPIO19 and GPIO20 can serve as the USB D- and D+ signals, respectively. On this board, the USB-C connector already uses the ESP32-S3's USB interface, so these pins are not presented like ordinary header pins.</p>
<p>This native USB connection is one of the reasons the Super Mini can be made so small: it does not necessarily need a separate CH340, CP2102, or other USB-to-UART converter.</p>
<h3><strong>Which Pins Should Beginners Use?</strong></h3>
<p>For initial projects, I would start with GPIO1, GPIO2, GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12 or GPIO13.</p>
<p>GPIO3 is usable, but it is also one of the ESP32-S3's strapping pins. A circuit that forces an inappropriate logic level on a strapping pin during reset can change the way the microcontroller boots.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Installing ESP32-S3 Support in Arduino IDE</strong></h2>
<p>If you have already programmed another ESP32 board using the Arduino IDE, you may already have Espressif's Arduino core installed. Otherwise, install the ESP32 package using the Arduino IDE's Boards Manager.</p>
<ol>
<li>Open the <strong>Arduino IDE</strong>.</li>
<li>Open <strong>Boards Manager</strong>.</li>
<li>Search for <strong>esp32</strong>.</li>
<li>Install the package published by <strong>Espressif Systems</strong>.</li>
<li>Restart the Arduino IDE if necessary.</li>
</ol>
<p><img data-dominant-color="edf1f1" data-has-transparency="false" style="--dominant-color: #edf1f1;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12647 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-library.avif" alt="" width="447" height="182" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-library.avif 447w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-library-300x122.avif 300w" sizes="auto, (max-width: 447px) 100vw, 447px" /></p>
<p>The generic Super Mini normally does not have its own entry in the Arduino board list. Instead, select:</p>
<pre><code>Tools &gt; Board &gt; ESP32 Arduino &gt; ESP32S3 Dev Module</code></pre>
<p>This gives us access to the configuration options needed for the S3.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Recommended Arduino IDE Settings</strong></h2>
<p>For a common ESP32-S3FH4R2 Super Mini, I recommend starting with these settings:</p>
<table>
<thead>
<tr>
<th>Arduino IDE Setting</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Board</td>
<td>ESP32S3 Dev Module</td>
</tr>
<tr>
<td>CPU Frequency</td>
<td>240MHz</td>
</tr>
<tr>
<td>USB CDC On Boot</td>
<td>Enabled</td>
</tr>
<tr>
<td>Flash Size</td>
<td>4MB (32Mb)</td>
</tr>
<tr>
<td>Flash Mode</td>
<td>QIO</td>
</tr>
<tr>
<td>PSRAM</td>
<td>QSPI PSRAM</td>
</tr>
<tr>
<td>Partition Scheme</td>
<td>Default 4MB with SPIFFS</td>
</tr>
<tr>
<td>Upload Mode</td>
<td>UART0 / Hardware CDC</td>
</tr>
</tbody>
</table>
<p><strong>QSPI PSRAM is especially important.</strong> The ESP32-S3FH4R2 contains 2 MB of Quad SPI PSRAM. Selecting the wrong PSRAM type can prevent the board from booting correctly or cause the PSRAM test to fail.</p>
<p><img data-dominant-color="f8f8f8" data-has-transparency="false" style="--dominant-color: #f8f8f8;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12648 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/arduino-settings-esp32-s3.avif" alt="" width="515" height="751" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/arduino-settings-esp32-s3.avif 515w, https://www.teachmemicro.com/wp-content/uploads/2026/08/arduino-settings-esp32-s3-206x300.avif 206w" sizes="auto, (max-width: 515px) 100vw, 515px" /></p>
<h3><strong>Why Enable USB CDC On Boot?</strong></h3>
<p>On many older ESP32 boards, a separate USB-to-serial IC creates the COM port. The ESP32-S3 can instead expose a serial interface directly through its internal USB hardware.</p>
<p>With <strong>USB CDC On Boot = Enabled</strong>, calls such as:</p>
<pre><code>Serial.println("Hello");</code></pre>
<p>can appear directly in the Arduino Serial Monitor through the USB-C connection.</p>
<p>If your sketch uploads correctly but the Serial Monitor remains completely blank, this setting is one of the first things I would check.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>First Test: Serial Output</strong></h2>
<p>Before testing anything more complicated, let's confirm that the microcontroller can run a sketch and communicate with the computer.</p>
<pre><code>void setup() {
  Serial.begin(115200);

  delay(2000);

  Serial.println();
  Serial.println("ESP32-S3 Super Mini is running!");
}

void loop() {
  Serial.println("Hello from ESP32-S3");
  delay(1000);
}</code></pre>
<p>Upload the program and open the Serial Monitor at <strong>115200 baud</strong>.</p>
<p>You should see something similar to:</p>
<pre><code>ESP32-S3 Super Mini is running!
Hello from ESP32-S3
Hello from ESP32-S3
Hello from ESP32-S3</code></pre>
<p><img data-dominant-color="e8f0f0" data-has-transparency="false" style="--dominant-color: #e8f0f0;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12650 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/hello-esp32-s3.avif" alt="" width="662" height="747" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/hello-esp32-s3.avif 662w, https://www.teachmemicro.com/wp-content/uploads/2026/08/hello-esp32-s3-266x300.avif 266w" sizes="auto, (max-width: 662px) 100vw, 662px" /></p>
<p>If this works, we have already confirmed three things: the ESP32-S3 is running, USB programming works and USB serial output works.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Testing the ESP32-S3 Mini Flash and PSRAM</strong></h2>
<p>One specification I would definitely verify on a generic ESP32-S3 Super Mini is its memory. Fortunately, Arduino gives us functions that can report the flash and PSRAM sizes directly.</p>
<p>Upload the following program:</p>
<pre><code>#include &lt;Arduino.h&gt;

void setup() {
  Serial.begin(115200);
  delay(2000);

  Serial.println("ESP32-S3 Hardware Test");
  Serial.println("----------------------");

  Serial.print("Chip model: ");
  Serial.println(ESP.getChipModel());

  Serial.print("CPU cores: ");
  Serial.println(ESP.getChipCores());

  Serial.print("CPU frequency: ");
  Serial.print(ESP.getCpuFreqMHz());
  Serial.println(" MHz");

  Serial.print("Flash size: ");
  Serial.print(ESP.getFlashChipSize() / 1024.0 / 1024.0, 2);
  Serial.println(" MB");

  Serial.print("PSRAM detected: ");
  Serial.println(psramFound() ? "YES" : "NO");

  Serial.print("PSRAM size: ");
  Serial.print(ESP.getPsramSize() / 1024.0 / 1024.0, 2);
  Serial.println(" MB");

  Serial.print("Free PSRAM: ");
  Serial.print(ESP.getFreePsram() / 1024);
  Serial.println(" KB");
}

void loop() {
}</code></pre>
<h3><strong>Expected Result for the ESP32-S3FH4R2 Version</strong></h3>
<p>On a Super Mini that really contains an ESP32-S3FH4R2, the important parts of the result should look approximately like this:</p>
<pre><code>ESP32-S3 Hardware Test
----------------------
Chip model: ESP32-S3
CPU cores: 2
CPU frequency: 240 MHz
Flash size: 4.00 MB
PSRAM detected: YES
PSRAM size: 2.00 MB
Free PSRAM: 2045 KB</code></pre>
<p><em><img data-dominant-color="f3f7f7" data-has-transparency="false" style="--dominant-color: #f3f7f7;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12658 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-ram-1.avif" alt="" width="612" height="560" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-ram-1.avif 612w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-ram-1-300x275.avif 300w" sizes="auto, (max-width: 612px) 100vw, 612px" /></em></p>
<p>If the program reports <strong>PSRAM size: 0 MB</strong>, this does not immediately mean your board lacks PSRAM. First verify that:</p>
<pre><code>Tools &gt; PSRAM &gt; QSPI PSRAM</code></pre>
<p>is selected.</p>
<p>If PSRAM still does not appear, inspect the marking on the chip. A different ESP32-S3 variant may have been fitted to your board.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Experiment: Actually Allocating Data in PSRAM</strong></h2>
<p>Detecting PSRAM is useful, but I also wanted a test that actually writes data to it. The following experiment allocates a 1 MB buffer using PSRAM, fills it with data, and then checks whether the allocation succeeded.</p>
<pre><code>#include &lt;Arduino.h&gt;

const size_t BUFFER_SIZE = 1024 * 1024;

void setup() {
  Serial.begin(115200);
  delay(2000);

  if (!psramFound()) {
    Serial.println("PSRAM not found.");
    return;
  }

  Serial.print("Free PSRAM before allocation: ");
  Serial.print(ESP.getFreePsram());
  Serial.println(" bytes");

  uint8_t *buffer = (uint8_t *)ps_malloc(BUFFER_SIZE);

  if (buffer == NULL) {
    Serial.println("1 MB PSRAM allocation FAILED.");
    return;
  }

  Serial.println("1 MB PSRAM allocation successful.");

  for (size_t i = 0; i &lt; BUFFER_SIZE; i++) {
    buffer[i] = i &amp; 0xFF;
  }

  bool passed = true;

  for (size_t i = 0; i &lt; BUFFER_SIZE; i++) {
    if (buffer[i] != (i &amp; 0xFF)) {
      passed = false;
      break;
    }
  }

  Serial.println(passed ?
                 "PSRAM read/write test PASSED." :
                 "PSRAM read/write test FAILED.");

  Serial.print("Free PSRAM after allocation: ");
  Serial.print(ESP.getFreePsram());
  Serial.println(" bytes");

  free(buffer);
}

void loop() {
}</code></pre>
<p>For a working 2 MB PSRAM device, the 1 MB allocation should succeed. More importantly, the final verification confirms that the data written to the buffer can also be read back correctly.</p>
<p>A successful test should produce something similar to:</p>
<pre><code>Free PSRAM before allocation: 2095103 bytes
1 MB PSRAM allocation successful.
PSRAM read/write test PASSED.
Free PSRAM after allocation: 1046511 bytes</code></pre>
<p>This experiment also demonstrates why PSRAM is useful. A one-megabyte buffer would consume far too much of the ESP32-S3's normal internal RAM, while the external/in-package PSRAM gives memory-hungry applications much more room.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Using the ESP32-S3 Super Mini RGB LED</strong></h2>
<p>Another nice feature of many ESP32-S3 Super Mini boards is the onboard addressable RGB LED. On the version covered by this guide, the LED is a WS2812-compatible device connected to <strong>GPIO48</strong>.</p>
<p>An addressable RGB LED is different from three ordinary LEDs. Instead of supplying separate red, green, and blue signals, we send a digital data stream containing the required color.</p>
<h3><img data-dominant-color="656768" data-has-transparency="false" style="--dominant-color: #656768;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12653 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-rgb-led-closeup.avif" alt="ESP32-S3 Super Mini RGB LED Close Up" width="651" height="477" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-rgb-led-closeup.avif 651w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-rgb-led-closeup-300x220.avif 300w" sizes="auto, (max-width: 651px) 100vw, 651px" /></h3>
<h3><strong>Installing the NeoPixel Library</strong></h3>
<p>Open the Arduino Library Manager and install the <strong>Adafruit NeoPixel</strong> library.</p>
<p>Then upload this example:</p>
<pre><code>#include &lt;Adafruit_NeoPixel.h&gt;

#define RGB_PIN 48
#define NUM_PIXELS 1

Adafruit_NeoPixel pixel(
  NUM_PIXELS,
  RGB_PIN,
  NEO_GRB + NEO_KHZ800
);

void setup() {
  pixel.begin();
  pixel.clear();
  pixel.show();
}

void loop() {
  // Red
  pixel.setPixelColor(0, pixel.Color(50, 0, 0));
  pixel.show();
  delay(1000);

  // Green
  pixel.setPixelColor(0, pixel.Color(0, 50, 0));
  pixel.show();
  delay(1000);

  // Blue
  pixel.setPixelColor(0, pixel.Color(0, 0, 50));
  pixel.show();
  delay(1000);

  // Off
  pixel.clear();
  pixel.show();
  delay(1000);
}</code></pre>
<p>I deliberately limited the color values to 50 instead of the maximum 255. Full-brightness WS2812 LEDs can be surprisingly bright, and there is no reason to run the LED at maximum power for a simple test.</p>
<h3><strong>Expected RGB Test Result</strong></h3>
<p>The onboard LED should cycle through:</p>
<ol>
<li>Red for one second</li>
<li>Green for one second</li>
<li>Blue for one second</li>
<li>Off for one second</li>
</ol>
<h3><img data-dominant-color="d2d2d1" data-has-transparency="false" style="--dominant-color: #d2d2d1;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12654 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/ESP32-S3-RGB-LED.avif" alt="ESP32-S3 RGB LED" width="964" height="380" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/ESP32-S3-RGB-LED.avif 964w, https://www.teachmemicro.com/wp-content/uploads/2026/08/ESP32-S3-RGB-LED-300x118.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/08/ESP32-S3-RGB-LED-768x303.avif 768w" sizes="auto, (max-width: 964px) 100vw, 964px" /></h3>
<h3><strong>What if GPIO48 Does Not Work?</strong></h3>
<p>The "Super Mini" name is used by multiple manufacturers, and not all board revisions are identical. Some ESP32-S3 boards use another GPIO for the addressable LED.</p>
<p>If the code runs but the LED remains dark:</p>
<ul>
<li>Check the seller's pinout for your exact board.</li>
<li>Inspect the board for a WS2812-style LED.</li>
<li>Try the GPIO number specified by the board manufacturer.</li>
<li>Do not assume that every ESP32-S3 board uses GPIO48.</li>
</ul>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Testing Wi-Fi</strong></h2>
<p>Since the ESP32-S3 includes Wi-Fi, a quick network scan is another useful hardware test. It verifies that the radio and antenna are operating without requiring your Wi-Fi password.</p>
<pre><code>#include &lt;WiFi.h&gt;

void setup() {
  Serial.begin(115200);
  delay(2000);

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);

  Serial.println("Scanning Wi-Fi networks...");

  int networks = WiFi.scanNetworks();

  if (networks == 0) {
    Serial.println("No Wi-Fi networks found.");
  } else {
    Serial.print(networks);
    Serial.println(" networks found:");

    for (int i = 0; i &lt; networks; i++) {
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print(" (");
      Serial.print(WiFi.RSSI(i));
      Serial.println(" dBm)");
    }
  }
}

void loop() {
}</code></pre>
<p>The output will depend entirely on your location. A typical result might look like:</p>
<pre><code>Scanning Wi-Fi networks...
13 networks found:
1: Stacey's_Wifi_2.4 (-65 dBm)
2: Stacey's_Wifi_2.4_EXT (-71 dBm)
3: innengarten_Guest (-82 dBm)
...
</code></pre>
<p><img data-dominant-color="ecf1f2" data-has-transparency="false" style="--dominant-color: #ecf1f2;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12655 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/ESP32-S3-WiFi-Scan.avif" alt="" width="615" height="409" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/ESP32-S3-WiFi-Scan.avif 615w, https://www.teachmemicro.com/wp-content/uploads/2026/08/ESP32-S3-WiFi-Scan-300x200.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/08/ESP32-S3-WiFi-Scan-310x205.avif 310w" sizes="auto, (max-width: 615px) 100vw, 615px" /><br />
If the board can reliably find nearby 2.4 GHz networks, we now know that the basic radio system and onboard antenna are working.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S3 Super Mini BOOT and RESET Buttons</strong></h2>
<p>The board normally provides two small buttons: <strong>BOOT</strong> and <strong>RESET</strong>.</p>
<p>The RESET button resets the ESP32-S3. The BOOT button is associated with the bootstrapping circuitry and is useful when manually forcing the chip into its firmware download mode.</p>
<p>Most uploads should work without touching either button. However, these buttons become useful if a program crashes early during startup, disables USB functionality or otherwise makes the normal upload process unreliable.</p>
<p><img data-dominant-color="6e6d6a" data-has-transparency="false" style="--dominant-color: #6e6d6a;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12656 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-buttons-closeup.avif" alt="" width="652" height="869" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-buttons-closeup.avif 652w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-buttons-closeup-225x300.avif 225w" sizes="auto, (max-width: 652px) 100vw, 652px" /></p>
<h3><strong>Manually Entering Download Mode</strong></h3>
<p>If the Arduino IDE cannot upload to the board, try the following:</p>
<ol>
<li>Press and hold <strong>BOOT</strong>.</li>
<li>Press and release <strong>RESET</strong>.</li>
<li>Release <strong>BOOT</strong>.</li>
<li>Check the Arduino IDE's Port menu again.</li>
<li>Select the newly detected port.</li>
<li>Upload the sketch.</li>
</ol>
<p>You can also try holding BOOT while connecting the USB cable.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Troubleshooting the ESP32-S3 Super Mini</strong></h2>
<h3><strong>No COM Port Appears</strong></h3>
<p>First try another USB cable. Some USB-C cables are intended only for charging and contain no data connections.</p>
<p>If the cable is good, manually enter download mode using the BOOT and RESET buttons. Then check whether a new serial device appears.</p>
<h3><strong>Sketch Uploads but Serial Monitor is Blank</strong></h3>
<p>Verify:</p>
<pre><code>Tools &gt; USB CDC On Boot &gt; Enabled</code></pre>
<p>Also make sure the Serial Monitor baud rate matches the value in:</p>
<pre><code>Serial.begin(115200);</code></pre>
<p>After changing USB-related settings, upload the sketch again.</p>
<h3><strong>PSRAM Reports 0 Bytes</strong></h3>
<p>Check:</p>
<pre><code>Tools &gt; PSRAM &gt; QSPI PSRAM</code></pre>
<p>Then rerun the memory test.</p>
<p>If PSRAM is still unavailable, read the ESP32-S3 chip marking. Your board may use a different variant than the common ESP32-S3FH4R2.</p>
<h3><strong>Board Resets After Enabling PSRAM</strong></h3>
<p>Make sure you did not select <strong>OPI PSRAM</strong> for an FH4R2 board. Its integrated 2 MB PSRAM uses Quad SPI, so select <strong>QSPI PSRAM</strong>.</p>
<h3><strong>RGB LED Does Not Light</strong></h3>
<p>GPIO48 is common for the ESP32-S3 Super Mini RGB LED, but board revisions differ. Verify the pinout supplied with your particular board.</p>
<h3><strong>Upload Suddenly Stops Working After Running a Sketch</strong></h3>
<p>Because the board uses the ESP32-S3's native USB interface, the USB connection is partly under the control of the microcontroller itself. A crashing application can therefore make the normal serial port disappear.</p>
<p>Use the BOOT/RESET procedure to enter the ESP32-S3 ROM downloader and recover the board.</p>
<h3><strong>Uploads Fail at High Speed</strong></h3>
<p>If uploads are unreliable, lower the upload speed. A lower setting takes slightly longer but can be more reliable with some computers, USB hubs and cables.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S3 Super Mini Test Summary</strong></h2>
<p>Before using a generic development board in a larger project, I like to verify its important features instead of relying entirely on the seller's specifications. For the ESP32-S3 Super Mini, the tests in this guide cover most of the board's important hardware.</p>
<table>
<thead>
<tr>
<th>Test</th>
<th>Expected Result for Common FH4R2 Board</th>
<th>Actual Result</th>
</tr>
</thead>
<tbody>
<tr>
<td>CPU identification</td>
<td>ESP32-S3, 2 cores, 240 MHz</td>
<td> ESP32-S3, 2 cores, 240 MHz</td>
</tr>
<tr>
<td>Flash memory</td>
<td>4 MB</td>
<td>4 MB</td>
</tr>
<tr>
<td>PSRAM detection</td>
<td>2 MB</td>
<td>1 MB</td>
</tr>
<tr>
<td>1 MB PSRAM allocation</td>
<td>Pass</td>
<td>Pass</td>
</tr>
<tr>
<td>USB Serial</td>
<td>Pass</td>
<td>Pass</td>
</tr>
<tr>
<td>RGB LED</td>
<td>Red, green and blue on GPIO48</td>
<td>Red, green and blue on GPIO48</td>
</tr>
<tr>
<td>Wi-Fi scan</td>
<td>Nearby 2.4 GHz networks detected</td>
<td>Pass</td>
</tr>
</tbody>
</table>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Is the ESP32-S3 Super Mini Worth Using?</strong></h2>
<p>The ESP32-S3 Super Mini is an interesting board because it gives you most of the important ESP32-S3 features without the size of a conventional development board.</p>
<p>Compared with smaller single-core ESP32 variants, the S3 gives you considerably more processing power. The common ESP32-S3FH4R2 version also includes 2 MB of PSRAM, which makes it particularly attractive for projects involving displays, large buffers, web interfaces, and other memory-intensive applications.</p>
<p>Native USB is another advantage. Besides normal serial communication, the ESP32-S3's USB hardware makes the chip suitable for projects involving USB devices. That creates possibilities beyond the usual Wi-Fi sensor and web-server projects.</p>
<p>The main disadvantage is documentation. "ESP32-S3 Super Mini" is not one tightly controlled board design, so a pinout or memory specification found online may not describe every version being sold.</p>
<p>Fortunately, we can verify most of the important details ourselves. Check the chip marking, run the flash and PSRAM test, confirm the RGB LED GPIO and keep a copy of the pinout for the exact board you own. Once those details are known, the ESP32-S3 Super Mini becomes a very capable compact development board for embedded and IoT projects.</p>
<aside class="tmm-product-card tmm-product-card--align-center tmm-product-card--layout-default" style="--tmm-product-card-title-lines:3;--tmm-product-card-desc-lines:5;" aria-label="Product recommendation: ESP32-S3 Super Mini">
							<div class="tmm-product-card__media">
					<img width="150" height="150" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-s3-super-mini-cover-image-150x150.avif" class="tmm-product-card__image not-transparent wp-post-image" alt="ESP32-S3 Super Mini" loading="lazy" decoding="async" data-has-transparency="false" data-dominant-color="d5d4d4" style="--dominant-color: #d5d4d4;" />				</div>
			
			<div class="tmm-product-card__content">
				<div class="tmm-product-card__title">ESP32-S3 Super Mini</div>

									<p class="tmm-product-card__description">ESP32S3SuperMini is positioned as a high-performance, low-power, cost-effective IoT mini development board for low-power IoT applications and wireless wearable applications.</p>
							</div>

							<div class="tmm-product-card__actions">
					<a class="button tmm-product-card__button" href="https://amzn.to/4gn4NZV" target="_blank" rel="noopener" aria-label="Check Price for ESP32-S3 Super Mini">
						Check Price					</a>
				</div>
					</aside>
<p>The post <a href="https://www.teachmemicro.com/esp32-s3-mini-getting-started-guide/">ESP32-S3 Mini Getting Started Guide: Super Mini Pinout, Arduino IDE Setup, PSRAM and RGB LED</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Raspberry Pi Pico vs Raspberry Pi Pico 2: What&#039;s the Difference?</title>
		<link>https://www.teachmemicro.com/raspberry-pi-pico-vs-raspberry-pi-pico-2/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 18 Aug 2026 23:00:02 +0000</pubDate>
				<category><![CDATA[Raspberry Pi Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12506</guid>

					<description><![CDATA[<p>The Raspberry Pi Pico 2 looks almost identical to the original Raspberry Pi Pico, but underneath is a significantly more capable microcontroller. The original Pico uses Raspberry Pi's RP2040, while the Pico 2 replaces it with the newer RP2350. The change brings faster processor cores, more RAM, twice the flash storage, hardware floating-point support, more &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/raspberry-pi-pico-vs-raspberry-pi-pico-2/">Raspberry Pi Pico vs Raspberry Pi Pico 2: What&#039;s the Difference?</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The <strong>Raspberry Pi Pico 2</strong> looks almost identical to the original <strong>Raspberry Pi Pico</strong>, but underneath is a significantly more capable microcontroller.</p>
<p>The original Pico uses Raspberry Pi's <strong>RP2040</strong>, while the Pico 2 replaces it with the newer <strong>RP2350</strong>. The change brings faster processor cores, more RAM, twice the flash storage, hardware floating-point support, more Programmable I/O resources, optional RISC-V processing, and a considerably more advanced security architecture.</p>
<p>At the same time, Raspberry Pi intentionally kept the Pico 2 largely compatible with the original Pico. The physical board size and 40-pin layout remain familiar, which makes moving an existing project to the newer board relatively straightforward.</p>
<p>Let's take a closer look at the Raspberry Pi Pico vs Pico 2 and determine when the newer board is actually worth using.</p>
<h2><strong>Raspberry Pi Pico vs Pico 2 Specifications</strong></h2>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Raspberry Pi Pico</th>
<th>Raspberry Pi Pico 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Microcontroller</td>
<td>RP2040</td>
<td>RP2350</td>
</tr>
<tr>
<td>Main CPU</td>
<td>Dual Cortex-M0+</td>
<td>Dual Cortex-M33</td>
</tr>
<tr>
<td>Alternative CPU</td>
<td>None</td>
<td>Dual Hazard3 RISC-V</td>
</tr>
<tr>
<td>Maximum Clock</td>
<td>133 MHz</td>
<td>150 MHz</td>
</tr>
<tr>
<td>SRAM</td>
<td>264 kB</td>
<td>520 kB</td>
</tr>
<tr>
<td>Onboard Flash</td>
<td>2 MB</td>
<td>4 MB</td>
</tr>
<tr>
<td>Hardware Floating Point</td>
<td>No dedicated FPU</td>
<td>Yes</td>
</tr>
<tr>
<td>Exposed GPIO</td>
<td>26</td>
<td>26</td>
</tr>
<tr>
<td>Exposed ADC Inputs</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>UART</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>SPI</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>I2C</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>PWM Channels</td>
<td>16</td>
<td>16</td>
</tr>
<tr>
<td>PIO State Machines</td>
<td>8</td>
<td>12</td>
</tr>
<tr>
<td>USB</td>
<td>USB 1.1 host/device</td>
<td>USB 1.1 host/device</td>
</tr>
</tbody>
</table>
<p>The conventional peripheral count has not changed very much. Both boards still provide two UART controllers, two SPI controllers, two I2C controllers, three exposed ADC inputs, PWM, USB, and 26 multifunction GPIO pins.</p>
<p>The major improvements are inside the microcontroller.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>RP2040 vs RP2350</strong></h2>
<p><img data-dominant-color="cfdcd4" data-has-transparency="false" style="--dominant-color: #cfdcd4;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12567 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/raspberry-pi-pico-vs-pico-2-chip-comparison.avif" alt="" width="1014" height="761" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/raspberry-pi-pico-vs-pico-2-chip-comparison.avif 1014w, https://www.teachmemicro.com/wp-content/uploads/2026/08/raspberry-pi-pico-vs-pico-2-chip-comparison-300x225.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/08/raspberry-pi-pico-vs-pico-2-chip-comparison-768x576.avif 768w" sizes="auto, (max-width: 1014px) 100vw, 1014px" /></p>
<p>The most important difference between the two boards is the microcontroller.</p>
<p>The original Raspberry Pi Pico uses the <strong>RP2040</strong>, Raspberry Pi's first microcontroller. It contains two Arm Cortex-M0+ processor cores running at up to 133 MHz, along with 264 kB of SRAM. The Raspberry Pi Pico 2 uses the newer <strong>RP2350</strong>. Its primary processors are two Arm Cortex-M33 cores running at up to 150 MHz, and the chip contains 520 kB of SRAM.</p>
<p>So the improvement isn't simply:</p>
<pre><code>133 MHz → 150 MHz</code></pre>
<p>The Cortex-M33 is also a much more capable processor architecture than the Cortex-M0+. This becomes important for applications involving signal processing, mathematical calculations, control algorithms, audio processing, encryption, and other CPU-intensive tasks.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Cortex-M0+ vs Cortex-M33</strong></h2>
<p>The Cortex-M0+ used by the RP2040 was designed as a small and efficient processor for embedded applications. It works extremely well for jobs such as:</p>
<ul>
<li>Reading sensors</li>
<li>Driving displays</li>
<li>Controlling motors</li>
<li>Handling GPIO</li>
<li>Running communication protocols</li>
<li>General microcontroller projects</li>
</ul>
<p>The Cortex-M33 used by the RP2350 belongs to a more advanced Arm architecture. Among other improvements, it provides DSP instructions, hardware floating-point support, and Arm TrustZone security features.</p>
<p>This means the Pico 2 can perform more useful work per clock cycle in many applications. The performance improvement is therefore greater than the relatively small increase from 133 MHz to 150 MHz might suggest.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Hardware Floating-Point Support</strong></h2>
<p>One particularly useful improvement is the addition of hardware floating-point support.</p>
<p>The RP2040 does not contain a dedicated floating-point unit. It can certainly perform floating-point calculations, but these operations rely on software routines and optimized functions provided by the platform.</p>
<p>The Cortex-M33 processors in the RP2350 include hardware support for single-precision floating-point calculations as well as DSP instructions.</p>
<p>This can matter when your program performs calculations involving:</p>
<pre><code>sin()
cos()
sqrt()
PID calculations
digital filters
FFT
sensor fusion</code></pre>
<p>For example, a simple sensor calculation such as:</p>
<pre><code>temperature = adc_voltage * calibration_factor;</code></pre>
<p>will work perfectly well on either board. However, an application continuously processing hundreds or thousands of floating-point calculations can benefit from the Pico 2's newer processor.</p>
<p>This makes the Pico 2 particularly interesting for robotics, audio processing, digital filtering, motion processing, and advanced control systems.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Almost Twice the RAM</strong></h2>
<p><img data-dominant-color="bfd9c9" data-has-transparency="false" style="--dominant-color: #bfd9c9;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12568 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/pico-vs-pico-2-ram-comparison.avif" alt="" width="970" height="432" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/pico-vs-pico-2-ram-comparison.avif 970w, https://www.teachmemicro.com/wp-content/uploads/2026/08/pico-vs-pico-2-ram-comparison-300x134.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/08/pico-vs-pico-2-ram-comparison-768x342.avif 768w" sizes="auto, (max-width: 970px) 100vw, 970px" /></p>
<p>The original Pico contains <strong>264 kB of SRAM</strong>. The Pico 2 increases this to <strong>520 kB</strong>. That's close to twice as much working memory.</p>
<p>RAM becomes important when your application needs to hold large amounts of temporary data such as:</p>
<ul>
<li>Display framebuffers</li>
<li>Audio samples</li>
<li>Sensor histories</li>
<li>Image data</li>
<li>Large arrays</li>
<li>Filesystem buffers</li>
<li>Large MicroPython objects</li>
</ul>
<p>For a blinking LED or basic temperature sensor, 264 kB is already far more than necessary. For graphics, data acquisition, audio processing, or larger MicroPython applications, however, the additional RAM can make a noticeable difference.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Twice the Flash Storage</strong></h2>
<p>Raspberry Pi also doubled the onboard flash memory.</p>
<p>The original Pico comes with <strong>2 MB of QSPI flash</strong>, while the Pico 2 provides <strong>4 MB</strong>.</p>
<p>Flash stores your program together with other data included with the firmware.</p>
<p>Two megabytes is already generous for many embedded applications, but flash consumption can increase quickly when a project contains:</p>
<ul>
<li>Fonts</li>
<li>Images</li>
<li>Audio samples</li>
<li>Lookup tables</li>
<li>MicroPython modules</li>
<li>Large application resources</li>
</ul>
<p>The Pico 2 therefore gives considerably more room for an application to grow.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Pico 2 Can Also Run RISC-V</strong></h2>
<p>One of the most unusual features of the RP2350 is that it contains processors based on <strong>two different instruction-set architectures</strong>.</p>
<p>Alongside its two Cortex-M33 processors are two <strong>Hazard3 RISC-V cores</strong>.</p>
<p>The RP2350 can therefore be configured to operate using:</p>
<pre><code>Dual Arm Cortex-M33

or

Dual Hazard3 RISC-V</code></pre>
<p>This does not normally mean that you have four general-purpose processor cores available to your application at the same time. Instead, the RP2350 allows the system to use either the Arm pair or the RISC-V pair.</p>
<p>For most Pico 2 users, the Cortex-M33 processors will remain the normal choice.</p>
<p>For embedded developers interested in experimenting with <strong>RISC-V</strong>, however, the Pico 2 provides a particularly interesting development platform.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>More Programmable I/O</strong></h2>
<p><img data-dominant-color="c9ddcf" data-has-transparency="false" style="--dominant-color: #c9ddcf;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12570 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/pico-vs-pico-2-pio-comparison.avif" alt="" width="943" height="407" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/pico-vs-pico-2-pio-comparison.avif 943w, https://www.teachmemicro.com/wp-content/uploads/2026/08/pico-vs-pico-2-pio-comparison-300x129.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/08/pico-vs-pico-2-pio-comparison-768x331.avif 768w" sizes="auto, (max-width: 943px) 100vw, 943px" /></p>
<p>Programmable I/O, or <strong>PIO</strong>, was one of the features that made the original RP2040 unusual. PIO uses small programmable state machines that can generate and receive digital signals without requiring constant attention from the main CPU. The original Pico provides <strong>eight PIO state machines</strong>.  The Pico 2 increases this to <strong>twelve PIO state machines</strong>.</p>
<p>PIO can be used for applications such as:</p>
<ul>
<li>WS2812 addressable LEDs</li>
<li>VGA video generation</li>
<li>Custom serial protocols</li>
<li>Additional UART-like interfaces</li>
<li>Precise pulse generation</li>
<li>Logic analyzer inputs</li>
<li>Unusual sensor interfaces</li>
</ul>
<p>If your project makes heavy use of PIO, the Pico 2 gives you considerably more resources to work with.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Better Security</strong></h2>
<p>Security is another area where the RP2350 is substantially different from the RP2040.</p>
<p>The RP2350 adds a much more extensive security architecture, including features such as:</p>
<ul>
<li>Arm TrustZone for Cortex-M</li>
<li>Secure boot capabilities</li>
<li>SHA-256 acceleration</li>
<li>True random number generation</li>
<li>One-time-programmable storage</li>
<li>Hardware support for isolating secure resources</li>
</ul>
<p>For a hobby project blinking LEDs, these features may not matter at all.</p>
<p>For a commercial embedded product, however, they can become very useful.</p>
<p>For example, secure boot can help ensure that only authorized firmware runs on a device. Hardware cryptographic support can also help with authentication, device identity, and secure firmware updates.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Are Pico and Pico 2 Pin Compatible?</strong></h2>
<p>One of the best things about the Pico 2 is that Raspberry Pi retained the familiar <strong>40-pin physical layout</strong> of the original Pico.</p>
<p>Both boards expose 26 multifunction GPIO pins, and normal peripheral connections can generally remain in the same locations.</p>
<p>For example, an existing circuit could contain:</p>
<pre><code>Raspberry Pi Pico
      |
      +-- BME280 via I2C
      |
      +-- OLED via SPI
      |
      +-- Servo via PWM
      |
      +-- Push Button via GPIO</code></pre>
<p>In many cases, the original Pico can simply be replaced with a Pico 2 without redesigning the surrounding circuit.</p>
<p>This compatibility is one of the strongest advantages of the second-generation board because an existing Pico design does not necessarily need to be rebuilt from scratch.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What About Existing Pico Programs?</strong></h2>
<p>Basic applications written using standard APIs are also relatively easy to move between the two boards.</p>
<p>For example, a simple MicroPython LED blink program looks familiar:</p>
<pre><code>from machine import Pin
from time import sleep

led = Pin(25, Pin.OUT)

while True:
    led.toggle()</code></pre>
<p>GPIO, I2C, SPI, UART, ADC, and PWM applications written through supported APIs generally require little or no modification.</p>
<p>Programs that directly manipulate RP2040 hardware registers are a different matter. Because the RP2350 is a different microcontroller, very low-level code may need to be updated.</p>
<p>For most Arduino, MicroPython, and Pico SDK projects, however, the transition is much easier.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Programming the Pico 2</strong></h2>
<p>The familiar Pico programming process remains.</p>
<p>Both boards can be programmed using <strong>C/C++</strong> and <strong>MicroPython</strong>, and both support Raspberry Pi's UF2 drag-and-drop programming method.</p>
<p>Hold the <strong>BOOTSEL</strong> button while connecting the board to USB and the bootloader appears as a USB mass-storage device. You can then copy the appropriate UF2 firmware file to the board.</p>
<p>One important point is that firmware compiled for the RP2040 should not simply be assumed to work as RP2350 firmware. When downloading MicroPython or building C/C++ applications, select the correct Pico 2 target.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>GPIO and Peripheral Comparison</strong></h2>
<p>Despite the much more capable processor, the conventional peripheral configuration remains familiar.</p>
<table>
<thead>
<tr>
<th>Peripheral</th>
<th>Pico</th>
<th>Pico 2</th>
</tr>
</thead>
<tbody>
<tr>
<td>Exposed GPIO</td>
<td>26</td>
<td>26</td>
</tr>
<tr>
<td>Exposed ADC Inputs</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<td>UART</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>SPI</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>I2C</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>PWM Channels</td>
<td>16</td>
<td>16</td>
</tr>
<tr>
<td>PIO State Machines</td>
<td>8</td>
<td>12</td>
</tr>
</tbody>
</table>
<p>Moving to the Pico 2 therefore doesn't suddenly provide twice as many UART, SPI, or I2C controllers.</p>
<p>The biggest improvements are concentrated in the processor, RAM, flash storage, PIO subsystem, and security hardware.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Does the Pico 2 Have Wi-Fi?</strong></h2>
<p>No. The standard <strong>Raspberry Pi Pico 2 does not include Wi-Fi or Bluetooth</strong>.</p>
<p>Just as Raspberry Pi offers both Pico and Pico W boards based on the RP2040, there are separate Pico 2 and <strong>Pico 2 W</strong> boards based on the RP2350.</p>
<p>The family can therefore be viewed like this:</p>
<pre><code>RP2040
 ├── Pico
 └── Pico W

RP2350
 ├── Pico 2
 └── Pico 2 W</code></pre>
<p>If your project requires wireless networking, the Pico 2 W is the appropriate second-generation version.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>When Does the Pico 2 Make a Difference?</strong></h2>
<p><img data-dominant-color="d7e3da" data-has-transparency="false" style="--dominant-color: #d7e3da;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12571 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/raspberry-pi-pico-vs-pico-2-workload-comparison.avif" alt="" width="959" height="694" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/raspberry-pi-pico-vs-pico-2-workload-comparison.avif 959w, https://www.teachmemicro.com/wp-content/uploads/2026/08/raspberry-pi-pico-vs-pico-2-workload-comparison-300x217.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/08/raspberry-pi-pico-vs-pico-2-workload-comparison-768x556.avif 768w" sizes="auto, (max-width: 959px) 100vw, 959px" /></p>
<p>For simple microcontroller projects, you may notice very little difference.</p>
<p>Consider a basic temperature display:</p>
<pre><code>Sensor → I2C → Pico → Display</code></pre>
<p>The RP2040 already has far more processing power than this application requires. Replacing it with a Pico 2 probably won't make the temperature reading appear noticeably faster.</p>
<p>Now consider a more complicated system:</p>
<pre><code>Multiple Sensors
       |
       v
Signal Filtering
       |
       v
Sensor Fusion
       |
       v
PID Control
       |
       v
High-Speed Data Logging
       |
       v
Display Output</code></pre>
<p>Here, the faster Cortex-M33 processors, hardware floating point, additional RAM, extra flash, and increased PIO resources can become useful.</p>
<p>The same is true for audio processing, robotics, graphics, real-time data acquisition, and other demanding embedded applications.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Should You Upgrade an Existing Pico Project?</strong></h2>
<p>Not necessarily.</p>
<p>If your existing Raspberry Pi Pico project already works reliably and isn't running out of CPU time, RAM, flash, or PIO resources, replacing the RP2040 board may provide no practical improvement.</p>
<p>A push button doesn't need a Cortex-M33 processor just to tell you whether it has been pressed.</p>
<p>The Pico 2 becomes worthwhile when the limitations of the original Pico actually start becoming relevant.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Which One Should You Choose?</strong></h2>
<p>Choose the <strong>Raspberry Pi Pico</strong> if:</p>
<ul>
<li>Your project consists mainly of ordinary sensor and GPIO tasks.</li>
<li>The RP2040 already provides enough processing power.</li>
<li>You don't need large amounts of RAM.</li>
<li>You don't need advanced security features.</li>
<li>Lowest possible board cost is important.</li>
</ul>
<p>Choose the <strong>Raspberry Pi Pico 2</strong> if:</p>
<ul>
<li>You need more CPU performance.</li>
<li>You need more RAM or flash storage.</li>
<li>Your program performs significant floating-point calculations.</li>
<li>You are doing DSP or signal processing.</li>
<li>You need additional PIO state machines.</li>
<li>You want the RP2350 security features.</li>
<li>You want to experiment with RISC-V.</li>
<li>You want additional performance headroom for future expansion.</li>
</ul>
<p>For a new project, the Pico 2 is generally the more capable choice when the small additional cost isn't an issue.</p>
<p>That doesn't make the original Pico obsolete. The RP2040 remains more than capable enough for a very large number of embedded applications.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Raspberry Pi Pico vs Pico 2: Final Thoughts</strong></h2>
<p>The Raspberry Pi Pico 2 represents a much more substantial upgrade than its nearly identical appearance suggests.</p>
<p>The change from <strong>RP2040 to RP2350</strong> brings:</p>
<ul>
<li>Cortex-M33 processors instead of Cortex-M0+</li>
<li>150 MHz maximum clock instead of 133 MHz</li>
<li>520 kB RAM instead of 264 kB</li>
<li>4 MB onboard flash instead of 2 MB</li>
<li>Hardware floating-point and DSP support</li>
<li>Optional Hazard3 RISC-V processors</li>
<li>12 PIO state machines instead of 8</li>
<li>A much more capable security architecture</li>
</ul>
<p>At the same time, Raspberry Pi retained the familiar Pico form factor and external pin arrangement.</p>
<p>For simple embedded projects, the original Raspberry Pi Pico remains perfectly capable. But for applications involving signal processing, larger programs, robotics, graphics, security, or applications that simply need more room to grow, the <strong>Raspberry Pi Pico 2 is clearly the more capable platform</strong>.</p>
<p>The post <a href="https://www.teachmemicro.com/raspberry-pi-pico-vs-raspberry-pi-pico-2/">Raspberry Pi Pico vs Raspberry Pi Pico 2: What&#039;s the Difference?</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using APDS-9960 as a Color Sensor with ESP32: Reflected Light and Positioning Matter</title>
		<link>https://www.teachmemicro.com/apds-9960-color-sensor-esp32/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Mon, 10 Aug 2026 02:34:44 +0000</pubDate>
				<category><![CDATA[Sensor Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12453</guid>

					<description><![CDATA[<p>The APDS-9960 is probably better known as a gesture sensor. It can detect hand movements such as left, right, up, and down, while also providing proximity sensing. However, the same small module also contains red, green, blue, and clear light-sensing channels. This means we can also use the APDS-9960 as a color sensor. There is &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/apds-9960-color-sensor-esp32/">Using APDS-9960 as a Color Sensor with ESP32: Reflected Light and Positioning Matter</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The APDS-9960 is probably better known as a <a href="https://www.teachmemicro.com/using-grove-gesture-sensor/">gesture sensor</a>. It can detect hand movements such as left, right, up, and down, while also providing proximity sensing. However, the same small module also contains red, green, blue, and clear light-sensing channels. This means we can also use the <strong>APDS-9960 as a <a href="https://www.teachmemicro.com/better-arduino-color-sensor-tcs230/">color sensor</a></strong>.</p>
<p>There is an important catch. The APDS-9960 does not simply look at an object and return a value such as "red" or "green." It measures the red, green, blue, and overall light reaching the sensor. When we use it to identify the color of an object, what we are really measuring is <strong>light reflected from that object's surface</strong>.</p>
<p>This makes the light source, distance, and positioning of the sensor surprisingly important. In this tutorial, I will connect an APDS-9960 to an ESP32, read its RGB values, and test it on real colored objects. I will also show why getting consistent color detection requires more than just reading three numbers from the sensor.</p>
<p><span id="more-12453"></span></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>How the APDS-9960 Detects Color</strong></h2>
<p><img data-dominant-color="ece7e9" data-has-transparency="false" style="--dominant-color: #ece7e9;" loading="lazy" decoding="async" class="aligncenter wp-image-12457 size-full not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/apds-9960_breakout.avif" alt="APDS9960 breakout board" width="1000" height="750" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/apds-9960_breakout.avif 1000w, https://www.teachmemicro.com/wp-content/uploads/2026/08/apds-9960_breakout-300x225.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/08/apds-9960_breakout-768x576.avif 768w" sizes="auto, (max-width: 1000px) 100vw, 1000px" /></p>
<p>The APDS-9960 contains separate sensing channels for:</p>
<ul>
<li><strong>Red</strong> light</li>
<li><strong>Green</strong> light</li>
<li><strong>Blue</strong> light</li>
<li><strong>Clear</strong> or overall light intensity</li>
</ul>
<p>The sensor converts the amount of light detected by each channel into digital values that can be read by the ESP32 through I2C.</p>
<p>For example, when a red object is illuminated with white light, the surface tends to reflect more of the red wavelengths while absorbing more of the other visible wavelengths. Ideally, the red value measured by the APDS-9960 will therefore be higher than the green and blue values.</p>
<p>The basic arrangement looks like this:</p>
<p><img data-dominant-color="f1e4e4" data-has-transparency="false" style="--dominant-color: #f1e4e4;" loading="lazy" decoding="async" class="aligncenter wp-image-12458 size-full not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/apds9960-color-sensor.avif" alt="APDS9960 color sensor" width="674" height="842" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/apds9960-color-sensor.avif 674w, https://www.teachmemicro.com/wp-content/uploads/2026/08/apds9960-color-sensor-240x300.avif 240w" sizes="auto, (max-width: 674px) 100vw, 674px" /></p>
<p>This distinction is important. We are not directly measuring an intrinsic "color value" stored inside the object. We are measuring reflected light. Change the light source or the physical arrangement and the measured values can change too.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Why the Light Source Matters</strong></h2>
<p>For an object to be measured, there must first be enough visible light falling on it. The object then reflects part of that light toward the APDS-9960.</p>
<p>During my initial tests, simply placing a colored plastic folder near the APDS-9960 without deliberate illumination produced very low RGB values. Illuminating the plastic with the flashlight from a phone made the differences between the channels much easier to observe.</p>
<p>This gives us our first important rule when using the APDS-9960 for color detection:</p>
<p><strong>Use a consistent light source.</strong></p>
<p>A phone flashlight is perfectly adequate for experimentation, but a permanent project would be better served by one or more fixed white LEDs mounted at a known distance and angle from the target.</p>
<p>Relying entirely on room lighting can create problems. The readings taken during daytime near a window may be different from readings taken at night under a warm-white lamp. Even two apparently white light sources can have significantly different spectral characteristics.</p>
<p>The APDS-9960 does contain an integrated infrared LED, but that LED is intended for its proximity and gesture functions. Infrared illumination is not a substitute for a visible white light source when we want to determine the visible color of an object.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Why Sensor Positioning Matters</strong></h2>
<p>The second thing that became obvious during testing was that <strong>positioning matters almost as much as illumination</strong>.</p>
<p>Changing the distance between the APDS-9960 and the object changes how much reflected light reaches the sensor. Changing the angle can also change the relative RGB readings, particularly when testing smooth or glossy materials.</p>
<p>There are actually several distances and angles that we should try to keep constant:</p>
<div class="checklist tie-list-shortcode">
<ul>
<li>Distance from the APDS-9960 to the object</li>
<li>Distance from the light source to the object</li>
<li>Angle of the sensor relative to the object</li>
<li>Angle of the light source relative to the object</li>
</ul>
</div>
<p>The effect becomes even more noticeable with transparent or semi-transparent objects. Light can travel through the material, reflect from whatever is behind it, and eventually reach the sensor. In this case, the background effectively becomes part of the measurement.</p>
<p>If your final project needs repeatable color detection, consider building a small fixture or enclosure that holds the sensor, light source, and target at fixed positions.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Connecting the APDS-9960 to ESP32</strong></h2>
<p><img data-dominant-color="dad8da" data-has-transparency="false" style="--dominant-color: #dad8da;" loading="lazy" decoding="async" class="aligncenter wp-image-12456 size-large not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-adps-9960-1024x670.avif" alt="esp32 to adps-9960 wiring" width="618" height="404" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-adps-9960-1024x670.avif 1024w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-adps-9960-300x196.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-adps-9960-768x503.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-adps-9960.avif 1100w" sizes="auto, (max-width: 618px) 100vw, 618px" /></p>
<p>The APDS-9960 communicates through I2C. For a typical ESP32 development board, GPIO21 and GPIO22 can be used as SDA and SCL respectively.</p>
<table>
<thead>
<tr>
<th>APDS-9960</th>
<th>ESP32</th>
</tr>
</thead>
<tbody>
<tr>
<td>VCC</td>
<td>3.3 V</td>
</tr>
<tr>
<td>GND</td>
<td>GND</td>
</tr>
<tr>
<td>SDA</td>
<td>GPIO21</td>
</tr>
<tr>
<td>SCL</td>
<td>GPIO22</td>
</tr>
</tbody>
</table>
<p>GPIO21 and GPIO22 are only conventional I2C pins on the ESP32. Other GPIO pins can be assigned in software if necessary.</p>
<p>Also check the particular APDS-9960 breakout board you are using before applying power. The bare APDS-9960 is a low-voltage device, while some breakout boards add regulators or level shifting. For an unknown generic module, using 3.3 V is the safer starting point.</p>
<p>The APDS-9960 has a fixed I2C address of <em>0x39</em>.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Installing the APDS-9960 Library</strong></h2>
<p><img data-dominant-color="f0f3f3" data-has-transparency="false" style="--dominant-color: #f0f3f3;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12455 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/adafruit-apds9960-library.avif" alt="" width="406" height="297" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/08/adafruit-apds9960-library.avif 406w, https://www.teachmemicro.com/wp-content/uploads/2026/08/adafruit-apds9960-library-300x219.avif 300w" sizes="auto, (max-width: 406px) 100vw, 406px" /></p>
<p>For this experiment, I used the <strong>Adafruit APDS9960 Library</strong>. In the Arduino IDE, open the Library Manager and search for:</p>
<p><em>Adafruit APDS9960</em></p>
<p>Install the library together with any dependencies requested by the Arduino IDE.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32 APDS-9960 Color Sensor Code</strong></h2>
<p>The following sketch initializes the sensor, waits until new color data is available, and then reads the red, green, blue, and clear channels.</p>
<p>I also calculate normalized RGB values, which we will discuss shortly.</p>
<pre><code>#include &lt;Wire.h&gt;
#include &lt;Adafruit_APDS9960.h&gt;

#define SDA_PIN 21
#define SCL_PIN 22

Adafruit_APDS9960 apds;

void setup()
{
  Serial.begin(115200);
  delay(1000);

  Serial.println();
  Serial.println("APDS-9960 Color Sensor Test");

  Wire.begin(SDA_PIN, SCL_PIN);

  if (!apds.begin())
  {
    Serial.println("APDS-9960 not found.");
    Serial.println("Check wiring and power.");

    while (1)
    {
      delay(1000);
    }
  }

  Serial.println("APDS-9960 detected.");

  // Increase sensitivity for reflected-light measurements
  apds.setADCIntegrationTime(100);
  apds.setADCGain(APDS9960_AGAIN_16X);

  // Enable RGB / ambient-light sensing
  apds.enableColor(true);
}

void loop()
{
  uint16_t red;
  uint16_t green;
  uint16_t blue;
  uint16_t clear;

  while (!apds.colorDataReady())
  {
    delay(5);
  }

  apds.getColorData(&amp;red, &amp;green, &amp;blue, &amp;clear);

  Serial.println("-------------------------");

  Serial.print("R: ");
  Serial.println(red);

  Serial.print("G: ");
  Serial.println(green);

  Serial.print("B: ");
  Serial.println(blue);

  Serial.print("C: ");
  Serial.println(clear);

  if (clear &gt; 0)
  {
    float rNorm = (float)red / clear;
    float gNorm = (float)green / clear;
    float bNorm = (float)blue / clear;

    Serial.print("Normalized: R=");
    Serial.print(rNorm, 3);

    Serial.print(" G=");
    Serial.print(gNorm, 3);

    Serial.print(" B=");
    Serial.println(bNorm, 3);
  }

  delay(500);
}</code></pre>
<p>I increased the ADC integration time and selected 16× gain because reflected-light measurements can sometimes be weak. A longer integration time allows the sensor to collect light for longer, while the higher gain increases sensitivity.</p>
<p>These values are not mandatory. In a brightly illuminated setup, the sensor may produce very large readings or saturate. If this happens, reduce the gain or integration time.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Testing a Red Plastic Folder</strong></h2>
<p>For my first test, I placed a red plastic folder in front of the APDS-9960 and illuminated it using the flashlight from a phone.</p>
<p>The ESP32 reported:</p>
<pre><code>R: 7
G: 3
B: 3
C: 13
Normalized: R=0.538 G=0.231 B=0.231</code></pre>
<p>The absolute values are quite small, but the important part is the relationship between the channels. The red channel is clearly stronger than both green and blue.</p>
<table>
<thead>
<tr>
<th>Channel</th>
<th>Raw Value</th>
<th>Normalized Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Red</td>
<td>7</td>
<td>0.538</td>
</tr>
<tr>
<td>Green</td>
<td>3</td>
<td>0.231</td>
</tr>
<tr>
<td>Blue</td>
<td>3</td>
<td>0.231</td>
</tr>
</tbody>
</table>
<p>Even this simple experiment shows that the APDS-9960 can distinguish a strongly red surface.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Testing a Green Semi-Transparent Folder</strong></h2>
<p>I then replaced the red folder with a green semi-transparent plastic folder while using the same phone flashlight.</p>
<p>This time, I obtained:</p>
<pre><code>R: 11
G: 29
B: 27
C: 72
Normalized: R=0.153 G=0.403 B=0.375</code></pre>
<p>The green channel is the highest:</p>
<table>
<thead>
<tr>
<th>Channel</th>
<th>Raw Value</th>
<th>Normalized Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Red</td>
<td>11</td>
<td>0.153</td>
</tr>
<tr>
<td>Green</td>
<td>29</td>
<td>0.403</td>
</tr>
<tr>
<td>Blue</td>
<td>27</td>
<td>0.375</td>
</tr>
</tbody>
</table>
<p>However, notice that the blue reading is also very close to green. The result is therefore not nearly as clear-cut as our red sample.</p>
<p>There are several possible contributors. The folder is semi-transparent, so some of the measured light can come through the plastic rather than only being reflected from its surface. The background behind the plastic can therefore affect the reading. The spectrum of the phone flashlight also affects the amount of red, green, and blue energy available for reflection or transmission.</p>
<p>The result still looks significantly different from the red sample, but it demonstrates why a single rule such as "the largest RGB value is the color" can easily become unreliable.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Why Normalize the RGB Values?</strong></h2>
<p>Suppose we move the light closer to the object. The raw red, green, and blue values may all increase even though the object's color has not changed.</p>
<p>One simple way of reducing the effect of overall brightness is to divide each RGB channel by the clear-channel reading:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_2460e6068cfb85abfdcdba9f0c35822a.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="R_n = \frac{R}{C}" /></span><script type='math/tex'>R_n = \frac{R}{C}</script></p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_75aa47f25e5ea7eb11144c9287ebd9ee.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="G_n = \frac{G}{C}" /></span><script type='math/tex'>G_n = \frac{G}{C}</script></p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_6e13510b3e5eacf99a11b349bb13c5e0.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="B_n = \frac{B}{C}" /></span><script type='math/tex'>B_n = \frac{B}{C}</script></p>
<p>For our red folder:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_c139e2697802c7ae61b800e5cb179985.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="R_n = \frac{7}{13} = 0.538" /></span><script type='math/tex'>R_n = \frac{7}{13} = 0.538</script></p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_1d8e89a589019caef1e8e3ec009ced4e.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="G_n = \frac{3}{13} = 0.231" /></span><script type='math/tex'>G_n = \frac{3}{13} = 0.231</script></p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_c7758d587c821643bbf8ddcbd210f14b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="B_n = \frac{3}{13} = 0.231" /></span><script type='math/tex'>B_n = \frac{3}{13} = 0.231</script></p>
<p>The resulting ratios make the dominance of red easy to see.</p>
<p>Normalization is useful for simple object classification, but it should not be confused with full color calibration. It cannot completely compensate for a different light source, different viewing angle, reflections, transparency, or other changes to the optical setup.</p>
<p>Also be careful when the clear value is very small. For example, when <em>C</em> is only 10 or 20 counts, a change of just one ADC count represents a significant change in the calculated ratio. It is better to improve the illumination or sensor sensitivity than to build a classifier from extremely weak signals.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Raw RGB Values Versus Object Color</strong></h2>
<p>A common mistake when building a color detector is to assume that an object can be classified simply by finding the largest channel:</p>
<pre><code>if (red &gt; green &amp;&amp; red &gt; blue)
{
  // Must be red?
}</code></pre>
<p>This can work for strongly separated colors under controlled conditions, but our green-folder experiment shows the problem. Green was 29 while blue was 27. Those values are too close for me to confidently build a universal rule around them.</p>
<p>A more practical system should first collect several measurements of every object or color that needs to be recognized.</p>
<p>For example:</p>
<table>
<thead>
<tr>
<th>Sample</th>
<th>R/C</th>
<th>G/C</th>
<th>B/C</th>
</tr>
</thead>
<tbody>
<tr>
<td>Red plastic</td>
<td>0.538</td>
<td>0.231</td>
<td>0.231</td>
</tr>
<tr>
<td>Green translucent plastic</td>
<td>0.153</td>
<td>0.403</td>
<td>0.375</td>
</tr>
<tr>
<td>Blue sample</td>
<td>Measure</td>
<td>Measure</td>
<td>Measure</td>
</tr>
<tr>
<td>Yellow sample</td>
<td>Measure</td>
<td>Measure</td>
<td>Measure</td>
</tr>
<tr>
<td>White sample</td>
<td>Measure</td>
<td>Measure</td>
<td>Measure</td>
</tr>
<tr>
<td>Black sample</td>
<td>Measure</td>
<td>Measure</td>
<td>Measure</td>
</tr>
</tbody>
</table>
<p>Take multiple measurements instead of relying on only one reading. From these samples, you can establish ranges or thresholds that are appropriate for your particular hardware and mechanical arrangement.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Using a Confidence Margin</strong></h2>
<p>For simple experiments, another improvement is to require the winning color channel to exceed the others by a minimum amount.</p>
<p>For example:</p>
<pre><code>String classifyColor(float r, float g, float b)
{
  const float margin = 0.08;

  if ((r - g &gt; margin) &amp;&amp; (r - b &gt; margin))
    return "RED";

  if ((g - r &gt; margin) &amp;&amp; (g - b &gt; margin))
    return "GREEN";

  if ((b - r &gt; margin) &amp;&amp; (b - g &gt; margin))
    return "BLUE";

  return "UNSURE";
}</code></pre>
<p>Our red folder would easily satisfy this type of test. The green translucent folder probably would not because its green and blue readings are very close. Returning <em>UNSURE</em> is usually better than confidently returning the wrong color.</p>
<p>The value of 0.08 here is only an example. A real threshold should come from measurements of your actual objects.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Improving APDS-9960 Color Detection</strong></h2>
<p>If I were turning this breadboard experiment into a real color-sensing project, I would make several changes.</p>
<h3>1. Use a Fixed White LED</h3>
<p>Instead of holding a phone flashlight by hand, mount a white LED at a fixed angle and distance from the target.</p>
<p>This gives every object approximately the same illumination and makes calibration much more meaningful.</p>
<h3>2. Keep the Object at a Fixed Distance</h3>
<p>A mechanical stop, slot, guide, or enclosure can make sure every sample appears at the same distance from the APDS-9960.</p>
<p>This is particularly useful for projects such as a color sorter where objects always travel through the same sensing position.</p>
<h3>3. Control the Viewing Angle</h3>
<p>Glossy plastic can produce strong specular reflections. A small change in angle may send much more or much less reflected light directly into the sensor.</p>
<p>Mounting the sensor and illumination mechanically eliminates much of this variation.</p>
<h3>4. Control the Background</h3>
<p>This becomes important when testing thin or translucent objects. Place an opaque and consistent backing behind the object if possible.</p>
<p>A white, black, or otherwise known background can produce much more repeatable measurements than whatever happens to be sitting behind the test object.</p>
<h3>5. Average Several Measurements</h3>
<p>Instead of classifying an object from one sample, take perhaps 5 to 20 readings and calculate an average.</p>
<p>This reduces the effect of occasional noisy readings and small changes in reflected light.</p>
<h3>6. Avoid Extremely Low Signal Levels</h3>
<p>If the clear reading is only a few counts, normalized values can become unstable. Increase illumination, increase integration time, increase gain, or move the object closer until the sensor produces a healthier signal.</p>
<p>At the other extreme, avoid saturating the sensor. If values approach their maximum range, reduce gain, integration time, or illumination.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Is the APDS-9960 a Good Color Sensor?</strong></h2>
<p>After experimenting with it, I would say <strong>yes, with some qualifications</strong>.</p>
<p>The APDS-9960 can definitely distinguish differences in reflected color. Our red sample produced a strongly dominant red channel, while the green sample produced a completely different RGB distribution.</p>
<p>However, I would use it primarily for <strong>color classification</strong> rather than precision color measurement.</p>
<p>Good applications include:</p>
<ul>
<li>Detecting whether an object is red, green, or blue</li>
<li>Sorting a limited set of known colored objects</li>
<li>Identifying colored markers or cards</li>
<li>Educational experiments involving reflected light</li>
<li>Adding simple color awareness to an ESP32 project</li>
</ul>
<p>I would not expect an uncalibrated APDS-9960 sitting openly on a breadboard to accurately identify arbitrary colors under arbitrary room lighting.</p>
<p>The key is to treat the sensor, illumination source, object position, and background as <strong>one optical measurement system</strong>.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Final Thoughts</strong></h2>
<p>The APDS-9960 is much more than a gesture sensor. Its red, green, blue, and clear channels allow it to become a surprisingly useful color detector when connected to an ESP32.</p>
<p>The most important lesson from my tests, however, is that <strong>color sensing is really an optical-system problem</strong>. The APDS-9960 measures the light that reaches it. When detecting the color of an object, this is largely light that has been reflected from that object.</p>
<p>As a result, the light source matters. Distance matters. Angle matters. The background matters, and the material itself matters.</p>
<p>For a quick experiment, a phone flashlight and a hand-held sample are enough to prove that the sensor responds differently to different colors. For a reliable project, use fixed white illumination and keep the sensor, object, and light source in repeatable positions.</p>
<p>With those conditions controlled, the APDS-9960 becomes a good candidate for simple color classification projects such as object sorters, colored-card interfaces, educational experiments, and other ESP32 projects where only a limited set of known colors needs to be identified.</p>
<p>The post <a href="https://www.teachmemicro.com/apds-9960-color-sensor-esp32/">Using APDS-9960 as a Color Sensor with ESP32: Reflected Light and Positioning Matter</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Adding an External Antenna to ESP32: U.FL, PCB Modifications, and Placement</title>
		<link>https://www.teachmemicro.com/adding-antenna-to-esp32/</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Fri, 31 Jul 2026 00:36:54 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12389</guid>

					<description><![CDATA[<p>Adding an antenna to ESP32 can improve Wi-Fi and Bluetooth reliability when the board is installed far from an access point, placed inside an enclosure, or surrounded by components that interfere with its built-in PCB antenna. However, the modification involves more than soldering a piece of wire to the board. The antenna connection is part &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/adding-antenna-to-esp32/">Adding an External Antenna to ESP32: U.FL, PCB Modifications, and Placement</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Adding an antenna to ESP32 can improve Wi-Fi and Bluetooth reliability when the board is installed far from an access point, placed inside an enclosure, or surrounded by components that interfere with its built-in PCB antenna. However, the modification involves more than soldering a piece of wire to the board.</p>
<p>The antenna connection is part of a carefully designed radio-frequency path. The antenna, connector, coaxial cable, PCB trace, matching components, and surrounding ground plane all affect how much of the ESP32's transmitted power is actually radiated.</p>
<p>In this tutorial, we will look at the safest ways of adding an external antenna to an ESP32. This includes choosing a module with a factory-installed U.FL connector, changing an antenna selection jumper, modifying an existing PCB, selecting the correct antenna, and positioning it inside the finished project.</p>
<p><span id="more-12389"></span></p>
<p><strong>Important:</strong> The antenna arrangement differs between ESP32 boards. Before moving or removing any component, find the schematic for your exact development board and inspect the RF section under magnification.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<p><!-- Suggested image: Three ESP32 antenna configurations side by side: PCB antenna module, U.FL module, and development board with an antenna selector jumper. --></p>
<h2><strong>Why Add an External Antenna to an ESP32?</strong></h2>
<p>Most ESP32 development boards already contain a small meandered PCB antenna. For a board sitting openly on a desk, this antenna is often sufficient. Problems usually appear after the board is installed in the final product.</p>
<p>An external antenna may be useful when:</p>
<ul>
<li>The ESP32 is installed inside a metal enclosure.</li>
<li>A battery, LCD, motor, relay, or large PCB is close to the built-in antenna.</li>
<li>The device must communicate through several walls.</li>
<li>The ESP32 is located near the edge of the access point's coverage.</li>
<li>The product needs an antenna mounted outside its enclosure.</li>
<li>The orientation of the built-in PCB antenna is unsuitable for the installation.</li>
<li>A directional antenna is required for a fixed point-to-point link.</li>
</ul>
<p>An external antenna does not automatically guarantee longer range. A badly positioned external antenna connected through a poor cable can perform worse than the original PCB antenna. The entire wireless link must be considered.</p>
<p>A simplified link budget can be written as:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_fe449c7f36fc03606417804b0d84e286.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="P_R = P_T + G_T + G_R - L_{path} - L_{cable} - L_{mismatch}" /></span><script type='math/tex'>P_R = P_T + G_T + G_R - L_{path} - L_{cable} - L_{mismatch}</script></p>
<p>Here, <em>P<sub>R</sub></em> is the received power, <em>P<sub>T</sub></em> is the transmitted power, <em>G<sub>T</sub></em> and <em>G<sub>R</sub></em> are antenna gains, and the remaining terms represent path, cable, and impedance mismatch losses.</p>
<p>This equation explains why installing a higher-gain antenna may not help when a long, lossy cable or an incorrectly modified RF trace introduces additional losses.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Understand Which Type of ESP32 Board You Have</strong></h2>
<p><img data-dominant-color="c5c4c2" data-has-transparency="false" style="--dominant-color: #c5c4c2;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12391 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-antenna-configurations.avif" alt="ESP32 Antenna Configuration" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-antenna-configurations.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-antenna-configurations-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<p>The term “ESP32 board” can refer to the ESP32 chip, a shielded ESP32 module, or a complete development board. The antenna may be part of any one of these assemblies.</p>
<table>
<thead>
<tr>
<th>Board or Module Type</th>
<th>Antenna Arrangement</th>
<th>Recommended Approach</th>
</tr>
</thead>
<tbody>
<tr>
<td>ESP32 module with PCB antenna</td>
<td>Antenna is etched directly onto the module PCB</td>
<td>Use the existing antenna or replace the module with an external-antenna version</td>
</tr>
<tr>
<td>ESP32 module ending in U or UE</td>
<td>Factory-installed micro-coaxial connector</td>
<td>Connect a compatible external antenna</td>
</tr>
<tr>
<td>Development board with PCB antenna and U.FL connector</td>
<td>A small RF jumper selects one antenna path</td>
<td>Move the jumper to the external-antenna position</td>
</tr>
<tr>
<td>Board with no connector but an accessible RF feed</td>
<td>Modification requires isolating the existing antenna</td>
<td>Attach a short 50 Ω micro-coaxial cable at the RF feed</td>
</tr>
<tr>
<td>Custom PCB using a bare ESP32 chip</td>
<td>RF matching and antenna network are designed on the PCB</td>
<td>Use controlled-impedance layout and RF validation</td>
</tr>
</tbody>
</table>
<h3><strong>ESP32 Modules with External Antenna Connectors</strong></h3>
<p>Espressif sells several module families in separate PCB-antenna and external-antenna versions. Examples include:</p>
<table>
<thead>
<tr>
<th>PCB Antenna Version</th>
<th>External Antenna Version</th>
</tr>
</thead>
<tbody>
<tr>
<td>ESP32-WROOM-32E</td>
<td>ESP32-WROOM-32UE</td>
</tr>
<tr>
<td>ESP32-S3-WROOM-1</td>
<td>ESP32-S3-WROOM-1U</td>
</tr>
<tr>
<td>ESP32-C3-WROOM-02</td>
<td>ESP32-C3-WROOM-02U</td>
</tr>
</tbody>
</table>
<p>The letter <strong>U</strong> commonly indicates an external-antenna connector in Espressif module names. However, this naming convention should not be assumed for every third-party board. Always verify the full module number printed on the metal shield.</p>
<p>A module such as the ESP32-WROOM-32UE does not contain the familiar meandered PCB antenna. Its RF output is routed directly to the small connector on the module. This is normally the easiest and most reliable option for adding an external antenna.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What Is a U.FL Connector?</strong></h2>
<p><img data-dominant-color="dfdcd3" data-has-transparency="false" style="--dominant-color: #dfdcd3;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12390 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/rf-connector-reference-chart.avif" alt="RF Connector Reference chart" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/rf-connector-reference-chart.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/07/rf-connector-reference-chart-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<p>U.FL is a miniature 50 Ω coaxial connector family made by Hirose. ESP32 sellers sometimes call the same connector an IPEX connector. More precisely, Espressif's first-generation external antenna connector is compatible with Hirose U.FL, I-PEX MHF I, and Amphenol AMC cable plugs.</p>
<p>These connectors are very small and should be handled carefully. The cable plug must be centered directly over the board receptacle and pressed vertically downward. When correctly connected, it should produce a small tactile click.</p>
<p>Do not press the connector at an angle. Doing so can bend the center contact or pull the receptacle away from the PCB.</p>
<p>Also be careful when ordering cables. I-PEX MHF I and MHF 4 are different connector families. An MHF 4 cable is smaller and should not be forced onto a first-generation U.FL or MHF I receptacle.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h3><strong>U.FL-to-SMA Pigtails</strong></h3>
<p>A common arrangement uses a short U.FL-to-SMA coaxial pigtail. The U.FL end connects to the ESP32 module, while the SMA end is mounted through the enclosure. A larger external whip antenna then screws onto the SMA connector.</p>
<p>This arrangement provides two advantages:</p>
<ul>
<li>The fragile U.FL connector remains protected inside the enclosure.</li>
<li>The external antenna can be replaced without repeatedly disconnecting the U.FL plug.</li>
</ul>
<p>Choose the SMA end carefully. SMA and reverse-polarity SMA connectors look similar but have different center contacts. Verify that the panel connector and antenna are compatible before ordering.</p>
<aside class="tmm-product-card tmm-product-card--align-center tmm-product-card--layout-default" style="--tmm-product-card-title-lines:3;--tmm-product-card-desc-lines:5;" aria-label="Product recommendation: 2.4 GHz 3 dBi U.FL-to-SMA Antenna Kit">
							<div class="tmm-product-card__media">
					<img width="150" height="150" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/esp32-antenna-150x150.avif" class="tmm-product-card__image not-transparent wp-post-image" alt="" loading="lazy" decoding="async" data-has-transparency="false" data-dominant-color="eae8e5" style="--dominant-color: #eae8e5;" />				</div>
			
			<div class="tmm-product-card__content">
				<div class="tmm-product-card__title">2.4 GHz 3 dBi U.FL-to-SMA Antenna Kit</div>

									<p class="tmm-product-card__description">An external antenna and U.FL-to-SMA pigtail combination suitable for ESP32 boards with a compatible U.FL/IPEX antenna connector, making it easy to mount the antenna outside an enclosure.</p>
							</div>

							<div class="tmm-product-card__actions">
					<a class="button tmm-product-card__button" href="https://amzn.to/4xf9WsP" target="_blank" rel="noopener" aria-label="Check Price for 2.4 GHz 3 dBi U.FL-to-SMA Antenna Kit">
						Check Price					</a>
				</div>
					</aside>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<p><!-- Suggested image: Close-up diagram comparing U.FL/MHF I, MHF 4, SMA, and RP-SMA connectors. --></p>
<h2><strong>Method 1: Use an ESP32 Module with a Factory U.FL Connector</strong></h2>
<p>The best method is to begin with a board or module designed for an external antenna. Examples include development boards populated with an ESP32-WROOM-32UE, ESP32-S3-WROOM-1U, or ESP32-C3-WROOM-02U.</p>
<h3><strong>Parts Required</strong></h3>
<ul>
<li>ESP32 development board with an external-antenna module</li>
<li>Compatible U.FL or MHF I cable assembly</li>
<li>2.4 GHz, 50 Ω antenna suitable for the module</li>
<li>Optional U.FL-to-SMA bulkhead pigtail</li>
<li>Plastic connector tool or fine nonmetallic tweezers</li>
</ul>
<aside class="tmm-product-card tmm-product-card--align-center tmm-product-card--layout-default" style="--tmm-product-card-title-lines:3;--tmm-product-card-desc-lines:5;" aria-label="Product recommendation: DFRobot FireBeetle 2 ESP32-C5-U">
							<div class="tmm-product-card__media">
					<img width="150" height="150" src="https://www.teachmemicro.com/wp-content/uploads/2026/08/firebeetle-esp32-150x150.avif" class="tmm-product-card__image not-transparent wp-post-image" alt="" loading="lazy" decoding="async" data-has-transparency="false" data-dominant-color="d7d5d2" style="--dominant-color: #d7d5d2;" />				</div>
			
			<div class="tmm-product-card__content">
				<div class="tmm-product-card__title">DFRobot FireBeetle 2 ESP32-C5-U</div>

									<p class="tmm-product-card__description">A compact ESP32 development board with a factory U.FL external antenna connector, dual-band 2.4/5 GHz Wi-Fi 6, Bluetooth LE, Thread and Zigbee support. A good option when you need reliable external-antenna connectivity without modifying the RF section of the board.</p>
							</div>

							<div class="tmm-product-card__actions">
					<a class="button tmm-product-card__button" href="https://www.dfrobot.com/product-3144.html?tracking=UcPQpy" target="_blank" rel="noopener" aria-label="Check Price for DFRobot FireBeetle 2 ESP32-C5-U">
						Check Price					</a>
				</div>
					</aside>
<h3><strong>Connection Procedure</strong></h3>
<ol>
<li>Disconnect USB power and any external power supply.</li>
<li>Confirm the connector type using the module datasheet.</li>
<li>Inspect the U.FL receptacle for contamination or damage.</li>
<li>Align the cable plug directly over the receptacle.</li>
<li>Press downward on the metal connector body rather than on the cable.</li>
<li>Route the cable so that it does not pull sideways on the connector.</li>
<li>Add strain relief to the cable several millimeters away from the receptacle.</li>
<li>Install the antenna before applying power.</li>
</ol>
<p>Do not attach adhesive, epoxy, or hot glue directly over the connector until the connection has been tested. Strain relief should hold the cable to the main PCB without pulling on the RF receptacle.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Method 2: Move the ESP32 Antenna Selection Jumper</strong></h2>
<p>Some development boards contain both a PCB antenna and a U.FL-style connector. Only one path should normally be active. A small zero-ohm resistor or solder bridge connects the ESP32's RF feed to the selected antenna.</p>
<p>This arrangement is particularly common on some ESP32 camera boards and compact IoT development boards.</p>
<p><!-- Suggested image: Enlarged three-pad antenna selector showing center RF feed, PCB antenna route, and U.FL route. --></p>
<h3><strong>How the Three-Pad Selector Works</strong></h3>
<p><img data-dominant-color="759574" data-has-transparency="false" style="--dominant-color: #759574;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12397 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/pcb-antenna-jumper-selection.avif" alt="PCB antenna jumper selection" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/pcb-antenna-jumper-selection.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/07/pcb-antenna-jumper-selection-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<p>A typical selector contains three pads:</p>
<ul>
<li>The center pad is connected to the ESP32 RF output.</li>
<li>One outside pad leads to the built-in PCB antenna.</li>
<li>The other outside pad leads to the U.FL connector.</li>
</ul>
<p>A zero-ohm resistor bridges the center pad to one of the two antenna paths. To select the external antenna, the resistor is removed from the PCB-antenna path and installed between the center pad and the connector path.</p>
<p>The arrangement is not identical on every board. The external path may be above, below, or beside the original jumper. Never assume that rotating the resistor by 90 degrees is correct without tracing the circuit.</p>
<h3><strong>Tools Required</strong></h3>
<ul>
<li>Fine-tip temperature-controlled soldering iron</li>
<li>Fine soldering tweezers</li>
<li>Flux</li>
<li>Solder wick</li>
<li>Magnification</li>
<li>ESD-safe work surface</li>
<li>Multimeter for checking shorts and continuity</li>
</ul>
<h3><strong>Modification Procedure</strong></h3>
<ol>
<li>Remove all power from the board.</li>
<li>Take a clear photograph of the original jumper position.</li>
<li>Identify the radio feed, PCB antenna route, and connector route from the schematic or PCB traces.</li>
<li>Apply a small amount of flux to the zero-ohm resistor.</li>
<li>Heat both ends evenly and lift the component with tweezers.</li>
<li>Clean the pads using solder wick if necessary.</li>
<li>Place the resistor between the RF feed pad and the external-connector pad.</li>
<li>Inspect the joint under magnification.</li>
<li>Check that the PCB antenna path is no longer connected.</li>
<li>Connect the external antenna before powering the ESP32.</li>
</ol>
<p>A solder blob can sometimes be used instead of reinstalling the zero-ohm resistor, but it must not bridge all three pads. Connecting the PCB antenna and external antenna simultaneously creates an uncontrolled RF junction that can cause severe impedance mismatch.</p>
<p>A multimeter can identify an accidental short or open circuit, but it cannot prove that the RF path has a 50 Ω characteristic impedance at 2.4 GHz. Proper RF performance must be evaluated through signal, throughput, or network-analyzer testing.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Method 3: Add a Coaxial Connection to a Board Without U.FL</strong></h2>
<p>This is the most difficult method and should normally be used only for experimentation. If a standard ESP32-WROOM module already contains the PCB antenna, the RF path is part of the module itself. The carrier board does not usually provide a convenient antenna feed that can be modified.</p>
<p>For this type of board, replacing the complete module or development board with an external-antenna version is usually safer than cutting into the module's PCB antenna.</p>
<h3><strong>When a Direct PCB Modification Is Possible</strong></h3>
<p>A modification may be possible when:</p>
<ul>
<li>The RF feed trace is visible and accessible.</li>
<li>The board contains an exposed matching network.</li>
<li>The existing PCB antenna can be disconnected without disturbing the matching components.</li>
<li>A nearby ground point is available for the coaxial shield.</li>
<li>The modification is being performed on a prototype rather than a certified production product.</li>
</ul>
<h3><strong>Why a Loose Wire Is Not Recommended</strong></h3>
<p>A common shortcut is to solder a random wire to the antenna feed. Although the wire may radiate some RF energy, it does not provide a controlled 50 Ω connection. The exposed section of wire, ground geometry, nearby objects, and solder joint all become part of the antenna.</p>
<p>The free-space wavelength at 2.4 GHz is approximately:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_d6d4c8846f3d015b890e99d150ee535a.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\lambda = \frac{c}{f} \approx 125\text{ mm}" /></span><script type='math/tex'>\lambda = \frac{c}{f} \approx 125\text{ mm}</script></p>
<p>A free-space quarter wavelength is therefore approximately:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_d0544a6dc77d49991dc706d41a6ad392.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\frac{\lambda}{4} \approx 31\text{ mm}" /></span><script type='math/tex'>\frac{\lambda}{4} \approx 31\text{ mm}</script></p>
<p>This does not mean that soldering a 31 mm wire to the ESP32 will produce a properly matched antenna. Practical antennas are affected by conductor width, ground plane dimensions, dielectric material, enclosure, loading, and nearby components.</p>
<h3><strong>Recommended Direct-Modification Approach</strong></h3>
<p>When a direct modification is unavoidable, use a short length of 50 Ω micro-coaxial cable rather than an unshielded wire.</p>
<ol>
<li>Locate the antenna feed and matching network.</li>
<li>Determine which side of the series component is connected to the radio.</li>
<li>Remove the jumper or cut the trace leading to the built-in antenna.</li>
<li>Solder the coaxial cable's center conductor to the radio-side RF feed.</li>
<li>Solder the coaxial shield to ground immediately beside the feed point.</li>
<li>Keep the exposed center conductor extremely short.</li>
<li>Secure the cable to the PCB so movement does not pull on the RF pad.</li>
<li>Terminate the other end at a proper U.FL, SMA, or other 50 Ω connector.</li>
</ol>
<p>The shield connection is as important as the center conductor. A long shield lead creates inductance and can make an otherwise short connection behave poorly at 2.4 GHz.</p>
<p>Do not scrape or cut the antenna trace while the board is powered. Avoid excessive heat because small RF pads can separate easily from the PCB.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Designing an External Antenna Connection on a Custom ESP32 PCB</strong></h2>
<p><img data-dominant-color="a1b09f" data-has-transparency="false" style="--dominant-color: #a1b09f;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12396 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/good-vs-poor-rf-layout.avif" alt="good-vs-poor-rf-layout" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/good-vs-poor-rf-layout.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/07/good-vs-poor-rf-layout-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<p>If you are creating a custom ESP32 board, the RF connection should be designed into the PCB rather than added afterward.</p>
<p>The RF trace between the matching network and antenna connector should have a characteristic impedance of approximately 50 Ω. The correct trace width depends on:</p>
<ul>
<li>PCB dielectric thickness</li>
<li>Copper thickness</li>
<li>Dielectric constant</li>
<li>Distance to the reference ground plane</li>
<li>Whether the trace is microstrip or coplanar waveguide</li>
<li>Spacing between the trace and surrounding ground copper</li>
</ul>
<p>Because these values depend on the PCB manufacturer's stack-up, copying the RF trace width from another board does not guarantee the correct impedance.</p>
<h3><strong>RF Layout Guidelines</strong></h3>
<ul>
<li>Keep the RF trace as short as possible.</li>
<li>Do not create branches or stubs in the antenna path.</li>
<li>Keep the trace width consistent.</li>
<li>Avoid routing the RF trace through vias.</li>
<li>Maintain a continuous ground reference beneath the trace.</li>
<li>Use ground vias along the sides of a coplanar RF trace.</li>
<li>Keep crystals, high-speed clocks, USB lines, UART lines, and switching regulators away from the antenna section.</li>
<li>Place the matching network close to the ESP32 RF output or antenna as directed by the reference design.</li>
<li>Keep copper and signal traces away from the connector area where required by the connector footprint.</li>
<li>Include a pi or CLC matching footprint even when the initial design uses zero-ohm or unpopulated components.</li>
</ul>
<p>The matching network gives you a way to compensate for the final PCB, connector, enclosure, and antenna during RF testing. The correct matching values normally require a vector network analyzer or equivalent RF measurement setup.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Choosing the Correct ESP32 External Antenna</strong></h2>
<p>The antenna must match the radio, connector, installation, and certification requirements of the exact ESP32 module.</p>
<h3><strong>Frequency</strong></h3>
<p>Common ESP32, ESP32-C3, and ESP32-S3 modules operate their Wi-Fi and Bluetooth radios in the 2.4 GHz band. A 2.4 GHz Wi-Fi or Wi-Fi/Bluetooth antenna is therefore required for these modules.</p>
<p>Do not select an antenna only because it physically fits the connector. An antenna intended only for GPS, cellular, LoRa, or sub-GHz operation may have very poor performance at 2.4 GHz.</p>
<p>For newer ESP32 variants supporting additional radio bands, use an antenna specified for every band that the design will use.</p>
<h3><strong>Impedance</strong></h3>
<p>The antenna and cable should have a nominal impedance of 50 Ω. This matches the RF system used by the ESP32 module and standard Wi-Fi antenna assemblies.</p>
<h3><strong>Antenna Gain</strong></h3>
<p>Higher gain does not mean that the antenna generates additional power. It concentrates radiation in particular directions while reducing it in others.</p>
<p>For example, a vertical high-gain whip may provide more horizontal coverage but create weaker areas directly above or below the antenna. A low-gain antenna may produce a more useful radiation pattern for a device whose orientation changes.</p>
<p>Certification must also be considered. Some Espressif external-antenna module datasheets specify the antenna used during certification and recommend that replacement antenna gain not exceed that value. The ESP32-WROOM-32UE and ESP32-S3-WROOM-1U documentation, for example, specifies a 2.4 GHz, 50 Ω antenna and lists 2.33 dBi as the certification antenna's maximum gain.</p>
<p>That value should not automatically be applied to every ESP32 module. Check the datasheet and certification documents for your exact part number. Using a different antenna type or higher gain may require additional EMC or regulatory testing.</p>
<h3><strong>External Whip Antenna</strong></h3>
<p>A screw-on whip antenna is useful when the antenna can be mounted outside the enclosure. It is easy to replace and can be oriented after installation.</p>
<h3><strong>Adhesive FPC Antenna</strong></h3>
<p>An adhesive flexible antenna can be installed inside a plastic enclosure. It should be mounted according to the antenna manufacturer's instructions. Some flexible antennas are designed to operate against a specific ground plane, while others require clearance from metal.</p>
<p>Do not fold, cut, or sharply bend the active antenna section. The coaxial cable may usually be routed around the enclosure, but the radiating element should remain in its intended shape.</p>
<h3><strong>PCB Antenna</strong></h3>
<p>A separate PCB antenna can be connected through a coaxial cable and mounted away from the ESP32 board. As with an FPC antenna, the antenna's ground-plane and clearance requirements must be followed.</p>
<h3><strong>Directional Antenna</strong></h3>
<p>A patch, panel, or Yagi-style antenna may be useful for a fixed point-to-point connection. It must be aimed correctly and may not be suitable for devices that move or need coverage in several directions.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32 Antenna Placement</strong></h2>
<p>Antenna placement is often more important than antenna gain. Moving an ordinary antenna away from metal and electrical noise can produce a greater improvement than replacing it with a higher-gain antenna in the same poor location.</p>
<h3><strong>Keep the Antenna Away from Metal</strong></h3>
<p>Metal close to an antenna can block radiation and change its resonant frequency. Avoid placing the antenna directly against:</p>
<ul>
<li>Metal enclosures</li>
<li>Large batteries</li>
<li>Heat sinks</li>
<li>LCD frames</li>
<li>Metal mounting plates</li>
<li>Large connectors</li>
<li>Shielding cans</li>
</ul>
<p>When using a metal enclosure, mount the antenna outside through a bulkhead connector. The coaxial cable can pass through the wall while the antenna remains in free space.</p>
<h3><strong>Keep the Antenna Away from Noise Sources</strong></h3>
<p>Switching regulators, DC motors, relays, high-speed displays, USB circuits, memory buses, and digital clocks can introduce interference. Place the antenna and its cable away from these circuits whenever possible.</p>
<p>Do not route the antenna cable parallel to high-current motor or relay wiring. If the paths must cross, crossing at approximately 90 degrees is generally preferable to running them together.</p>
<h3><strong>Respect the PCB Antenna Keepout Area</strong></h3>
<p>When using an ESP32 module's built-in PCB antenna, place the antenna end at the edge of the carrier PCB. Allowing the antenna to extend beyond the edge is usually the preferred arrangement.</p>
<p>If the antenna cannot extend past the board, there should be no copper, traces, or components under or beside the active antenna region. Espressif recommends providing at least 15 mm of clearance around the PCB antenna inside the finished enclosure.</p>
<p>The exact keepout geometry should come from the hardware design guide for the selected module.</p>
<h3><strong>Consider Polarization</strong></h3>
<p>A straight whip antenna normally has linear polarization. For communication with a typical access point whose antennas are vertical, starting with the ESP32 antenna vertically oriented is reasonable.</p>
<p>Rotating one antenna by 90 degrees relative to the other can reduce the received signal. Reflections inside buildings may reduce or change this effect, so the final orientation should still be tested experimentally.</p>
<h3><strong>Do Not Coil the Cable Around the Antenna</strong></h3>
<p>Extra coaxial cable should be routed away from the radiating element. Do not wrap it tightly around the whip or place a coil directly behind an adhesive antenna. The cable shield and nearby conductive objects can alter the antenna pattern.</p>
<h3><strong>Test the Finished Enclosure</strong></h3>
<p>An antenna that performs well on an open workbench may behave differently after the enclosure, battery, display, and wiring are installed. Always evaluate the complete assembled product.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<p><!-- Suggested image: Good and bad ESP32 antenna placement examples inside plastic and metal enclosures. --></p>
<h2><strong>Testing the ESP32 Antenna Modification</strong></h2>
<p>Test the original antenna before making the modification so that you have a baseline. Use the same ESP32 board, access point, location, power supply, orientation, and firmware for both tests.</p>
<p>Useful measurements include:</p>
<ul>
<li>Average RSSI</li>
<li>Packet loss</li>
<li>Connection and reconnection time</li>
<li>TCP or UDP throughput</li>
<li>Maximum usable distance</li>
<li>Performance through walls</li>
<li>Performance at different antenna orientations</li>
</ul>
<p>RSSI is measured in dBm. Values closer to zero represent a stronger received signal. Because RSSI changes with movement, reflections, and nearby people, compare averages rather than relying on one reading.</p>
<h3><strong>Arduino RSSI Test Sketch</strong></h3>
<p>The following sketch connects to an access point and prints individual RSSI readings followed by their average:</p>
<pre><code>#include &lt;WiFi.h&gt;

const char *ssid = "YOUR_WIFI_SSID";
const char *password = "YOUR_WIFI_PASSWORD";

const int sampleCount = 20;
const unsigned long sampleDelayMs = 500;

void setup() {
  Serial.begin(115200);
  delay(1000);

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  Serial.print("Connecting");

  unsigned long startTime = millis();

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");

    if (millis() - startTime &gt; 30000) {
      Serial.println("\nConnection timed out.");
      return;
    }
  }

  Serial.println("\nConnected.");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("BSSID: ");
  Serial.println(WiFi.BSSIDstr());
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Wi-Fi disconnected.");
    delay(2000);
    return;
  }

  long rssiTotal = 0;

  Serial.println("Collecting RSSI samples:");

  for (int i = 0; i &lt; sampleCount; i++) {
    int32_t rssi = WiFi.RSSI();
    rssiTotal += rssi;

    Serial.print("Sample ");
    Serial.print(i + 1);
    Serial.print(": ");
    Serial.print(rssi);
    Serial.println(" dBm");

    delay(sampleDelayMs);
  }

  float averageRssi = (float)rssiTotal / sampleCount;

  Serial.print("Average RSSI: ");
  Serial.print(averageRssi, 1);
  Serial.println(" dBm\n");

  delay(3000);
}</code></pre>
<p>Record several test runs with the original antenna and several more with the external antenna. Do not hold the board or antenna during one test and place it on a table during another. Your hand can affect the antenna and invalidate the comparison.</p>
<h3><strong>Use More Than RSSI</strong></h3>
<p>An improved RSSI reading does not always mean that the entire connection is better. Interference, packet retries, antenna pattern, and access-point behavior can affect actual data transfer.</p>
<p>For a more useful evaluation, transfer a large file or run a local TCP throughput test while monitoring packet loss and disconnections. Repeat the test in the location where the device will actually operate.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Troubleshooting an ESP32 External Antenna</strong></h2>
<h3><strong>The External Antenna Performs Worse</strong></h3>
<p>Check the following:</p>
<ul>
<li>The zero-ohm jumper may still select the PCB antenna.</li>
<li>Both antenna paths may accidentally be connected.</li>
<li>The U.FL plug may not be fully seated.</li>
<li>The antenna may be designed for the wrong frequency.</li>
<li>The antenna may be too close to metal.</li>
<li>The coaxial cable may be damaged or excessively long.</li>
<li>The antenna may have a null in the direction of the access point.</li>
<li>The connector or RF pad may have been damaged during soldering.</li>
</ul>
<h3><strong>Wi-Fi Works Only When the Board Is Close to the Router</strong></h3>
<p>This often indicates an open or badly mismatched antenna path. Inspect the selector resistor and U.FL connection. Confirm that the antenna is connected to the radio side of the matching network rather than to the isolated PCB-antenna side.</p>
<h3><strong>The Signal Changes When the Cable Is Touched</strong></h3>
<p>A large change when touching the cable can indicate that the cable shield is not properly grounded, the connector is loose, or an exposed section of conductor is acting as part of the antenna.</p>
<h3><strong>The U.FL Connector Detached from the PCB</strong></h3>
<p>Do not attempt to glue it back in place and expect the RF path to work. The center signal pad and surrounding ground pads must be electrically restored. If the pads have lifted, repair normally requires microscope-assisted micro-soldering and a careful RF connection.</p>
<h3><strong>The Board Resets While Transmitting</strong></h3>
<p>This problem is more likely related to the power supply than the antenna. Wi-Fi transmission produces short current peaks. Check the 3.3 V regulator, USB cable, supply wiring, and decoupling capacitors.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Frequently Asked Questions</strong></h2>
<h3><strong>Can I connect an antenna directly to an ESP32 GPIO?</strong></h3>
<p>No. The Wi-Fi and Bluetooth antenna uses the ESP32's RF output path, not a general-purpose input/output pin. Connecting an antenna to a GPIO will not improve radio range and may expose that GPIO to unwanted RF signals or static discharge.</p>
<h3><strong>Can I use the PCB antenna and external antenna at the same time?</strong></h3>
<p>Not by simply connecting both paths together. A passive split creates an impedance mismatch and divides transmitted power. A proper diversity or dual-antenna design requires an RF switch, matching network, and support from the selected ESP32 variant and firmware.</p>
<h3><strong>Can I solder an SMA connector directly to the board?</strong></h3>
<p>Only when the board has a correctly designed 50 Ω RF footprint and sufficient mechanical support. A full-size SMA connector is too large to hang from a small antenna feed pad. A short micro-coaxial pigtail connected to a panel-mounted SMA connector is usually safer.</p>
<h3><strong>Will a 9 dBi antenna give three times the ESP32 range?</strong></h3>
<p>Not necessarily. Gain is directional, cable loss reduces the improvement, and the other end of the connection must still receive and return data. Higher gain may also exceed the antenna conditions used for the module's regulatory certification.</p>
<h3><strong>Does the external antenna improve both Wi-Fi and Bluetooth?</strong></h3>
<p>On common ESP32 modules, Wi-Fi and Bluetooth share the 2.4 GHz RF path. A correctly selected external antenna can therefore affect both. The exact internal radio arrangement should still be confirmed in the datasheet for the selected ESP32 variant.</p>
<h3><strong>Can I operate an external-antenna ESP32 without an antenna?</strong></h3>
<p>Avoid intentionally transmitting for extended periods without an antenna. An open connector produces a severe impedance mismatch, greatly reduces communication performance, and may stress the RF output stage.</p>
<h3><strong>Is firmware configuration required after moving the antenna jumper?</strong></h3>
<p>Normally, no. A passive zero-ohm antenna selector changes the physical RF path and is not controlled by software. Boards using an electronic RF switch or antenna-diversity circuit may require GPIO or ESP-IDF configuration.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Summary</strong></h2>
<p>The safest way of adding an external antenna to an ESP32 is to use a module designed for it, such as an ESP32-WROOM-32UE or ESP32-S3-WROOM-1U. The factory connector preserves the module's controlled RF path and avoids cutting the built-in PCB antenna.</p>
<p>For boards containing both a PCB antenna and a U.FL connector, locate the antenna selector and move its zero-ohm jumper to the external path. Do not leave both antenna paths connected.</p>
<p>Directly modifying a PCB-antenna module is possible in some cases, but it requires isolating the existing antenna and attaching a properly grounded 50 Ω micro-coaxial cable. A loose wire is not a reliable substitute for a matched antenna connection.</p>
<p>Finally, pay close attention to placement. Keep the antenna away from metal, high-speed circuits, batteries, displays, and noisy wiring. Test the complete device in its final enclosure using RSSI, packet loss, and actual throughput rather than assuming that a larger antenna will automatically provide better range.</p>
<p>The post <a href="https://www.teachmemicro.com/adding-antenna-to-esp32/">Adding an External Antenna to ESP32: U.FL, PCB Modifications, and Placement</a> appeared first on <a href="https://www.teachmemicro.com">Teach Me Microcontrollers!</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: www.teachmemicro.com @ 2026-10-27 03:02:54 by W3 Total Cache
-->