<?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>ESP32 Tutorial Archives | Microcontroller Tutorials</title>
	<atom:link href="https://www.teachmemicro.com/category/tutorials/esp32-tutorial/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.teachmemicro.com/category/tutorials/esp32-tutorial/</link>
	<description>Microcontroller Tutorials and Resources</description>
	<lastBuildDate>Tue, 28 Jul 2026 03:13:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>

<image>
	<url>https://www.teachmemicro.com/wp-content/uploads/2019/04/blue-icon-65x65.png</url>
	<title>ESP32 Tutorial Archives | Microcontroller Tutorials</title>
	<link>https://www.teachmemicro.com/category/tutorials/esp32-tutorial/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>ESP32-S3 Analog Input Leakage Current: High-Impedance ADC Design</title>
		<link>https://www.teachmemicro.com/esp32-s3-analog-input-leakage-current/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=esp32-s3-analog-input-leakage-current</link>
					<comments>https://www.teachmemicro.com/esp32-s3-analog-input-leakage-current/#respond</comments>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 28 Jul 2026 03:13:06 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12350</guid>

					<description><![CDATA[<p>The ESP32-S3 analog-to-digital converter works well with low-impedance voltage sources. However, problems can appear when the analog signal comes from a high-value resistor divider, a resistive sensor, or another circuit that cannot supply much current. You may notice that the measured voltage is lower than expected, the first reading is different from the following readings, &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-s3-analog-input-leakage-current/">ESP32-S3 Analog Input Leakage Current: High-Impedance ADC Design</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The ESP32-S3 analog-to-digital converter works well with low-impedance voltage sources. However, problems can appear when the analog signal comes from a high-value resistor divider, a resistive sensor, or another circuit that cannot supply much current.</p>
<p>You may notice that the measured voltage is lower than expected, the first reading is different from the following readings, or one ADC channel appears to affect another. These symptoms are often blamed on <strong>analog input leakage current</strong>. Leakage is part of the problem, but the ESP32-S3's switched-capacitor ADC input also creates a temporary load whenever a conversion starts.</p>
<p>In this article, we will examine how ESP32-S3 analog input leakage affects high-impedance circuits and how to design an ADC input that produces stable and repeatable readings.</p>
<p><span id="more-12350"></span></p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>How the ESP32-S3 ADC Input Works</strong></h2>
<p>The ESP32-S3 contains two 12-bit successive-approximation ADC units. Together, they provide 20 analog input channels:</p>
<table>
<thead>
<tr>
<th>ADC Unit</th>
<th>Channels</th>
<th>GPIO Pins</th>
</tr>
</thead>
<tbody>
<tr>
<td>ADC1</td>
<td>ADC1_CH0 to ADC1_CH9</td>
<td>GPIO1 to GPIO10</td>
</tr>
<tr>
<td>ADC2</td>
<td>ADC2_CH0 to ADC2_CH9</td>
<td>GPIO11 to GPIO20</td>
</tr>
</tbody>
</table>
<p>For most projects, ADC1 is the better choice. Espressif recommends ADC1, and ADC2 analog functions cannot be used normally while Wi-Fi is using the ADC2 peripheral.</p>
<p>A 12-bit conversion produces values from 0 to 4095. However, the raw result is not automatically equal to an exact voltage because the internal reference voltage varies from one chip to another. The ESP32-S3 reference is designed around 1.1 V, but its actual value can vary approximately from 1.0 V to 1.2 V. For this reason, calibrated voltage functions should be used whenever possible.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What Is Analog Input Leakage Current?</strong></h2>
<p>An ideal ADC input would have infinite resistance and draw no current from the measured circuit. A real microcontroller pin contains protection structures, switches, multiplexers, transistors, parasitic capacitance, and internal analog circuitry. Small currents can therefore flow into or out of the pin.</p>
<p>When the source resistance is low, this current produces almost no visible error. When the source resistance is hundreds of kilohms or several megohms, even a tiny current can create a measurable voltage drop.</p>
<p>The approximate DC error caused by leakage is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_06ca51353687d5f7e2e937cd03c1a494.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt=" V_{error}=I_{leak}\times R_{source} " /></span><script type='math/tex'> V_{error}=I_{leak}\times R_{source} </script></p>
<p>For example, suppose an analog source has an equivalent resistance of 200 kΩ. The resulting errors would be:</p>
<table>
<thead>
<tr>
<th>Assumed Leakage Current</th>
<th>Voltage Error with 200 kΩ Source</th>
</tr>
</thead>
<tbody>
<tr>
<td>10 nA</td>
<td>2 mV</td>
</tr>
<tr>
<td>50 nA</td>
<td>10 mV</td>
</tr>
<tr>
<td>100 nA</td>
<td>20 mV</td>
</tr>
<tr>
<td>1 µA</td>
<td>200 mV</td>
</tr>
</tbody>
</table>
<p>The ESP32-S3 datasheet specifies a maximum high-level and low-level digital GPIO input current of 50 nA under its stated DC test conditions. However, this value should <strong>not</strong> be treated as a guaranteed ADC input leakage specification. The datasheet does not provide a separate worst-case analog leakage value covering ADC conversions, attenuation settings, temperature, board contamination, and all operating modes.</p>
<p>For a high-impedance design, it is safer to consider the 50 nA figure only as a pin-level reference. The complete ADC circuit should be tested across temperature and operating conditions, or isolated from the signal source using an analog buffer.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>The ADC Is a Dynamic Load</strong></h2>
<p><img data-dominant-color="edeef0" data-has-transparency="false" style="--dominant-color: #edeef0;" decoding="async" class="aligncenter wp-image-12365 size-full not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/impedance-variation.avif" alt="Source impedance variation" width="718" height="235" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/impedance-variation.avif 718w, https://www.teachmemicro.com/wp-content/uploads/2026/07/impedance-variation-300x98.avif 300w" sizes="(max-width: 718px) 100vw, 718px" /></p>
<p>Leakage current explains steady DC error, but it does not explain every problem seen with high-impedance inputs. The ESP32-S3 uses a successive-approximation ADC, which includes an internal sampling network.</p>
<p>At the beginning of a conversion, an internal switch briefly connects the selected GPIO to a sampling capacitor. The input source must provide enough charge for this capacitor to approach the input voltage during the available acquisition period.</p>
<p>A simplified settling relationship is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_57871c5086b6249b2b61e5ea5d7553f0.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt=" V_{error}=V_{step}e^{-\frac{t_{acq}}{R_{source}C_{sample}}} " /></span><script type='math/tex'> V_{error}=V_{step}e^{-\frac{t_{acq}}{R_{source}C_{sample}}} </script></p>
<p>Here, <em>Rsource</em> is the source resistance, <em>Csample</em> is the effective internal sampling capacitance, and <em>tacq</em> is the acquisition time.</p>
<p>As the source resistance increases, the capacitor takes longer to charge. If the ADC starts its conversion before the voltage settles, the result can be lower or higher than the actual signal. This effect may appear even when the static leakage current is extremely small.</p>
<p>The internal attenuation network also changes the signal path. Consequently, source loading and conversion error may vary with the selected attenuation setting.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Why the First ADC Reading Can Be Wrong</strong></h2>
<p>The ADC input multiplexer connects several analog channels to the same converter. When software changes from one channel to another, the sampling network can retain a small amount of charge from the previous channel.</p>
<p>A low-impedance source quickly replaces this charge. A high-impedance source may require more time. The first reading can therefore be influenced by:</p>
<ul>
<li>The voltage previously measured on another ADC channel</li>
<li>Residual charge in the sampling network</li>
<li>The selected attenuation setting</li>
<li>The external filter capacitor</li>
<li>The time between channel selection and conversion</li>
</ul>
<p>A common software solution is to perform one dummy conversion after changing channels and discard its result. This helps, but it does not replace proper analog input design.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>High-Value Resistor Divider Example</strong></h2>
<p>Consider a circuit that measures a supply voltage of up to 15 V. To minimize battery drain, we use a 1 MΩ upper resistor and a 220 kΩ lower resistor.</p>
<p>The voltage presented to the ESP32-S3 is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_781a090fa519aa3a6b619eadb7a9f4e4.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{ADC}=V_{IN}\frac{R_{BOTTOM}}{R_{TOP}+R_{BOTTOM}}" /></span><script type='math/tex'>V_{ADC}=V_{IN}\frac{R_{BOTTOM}}{R_{TOP}+R_{BOTTOM}}</script></p>
<p>At 15 V:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_b42fce7284f9fb9a7c588fda9302878c.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{ADC}=15\frac{220000}{1000000+220000}=2.705\text{ V}" /></span><script type='math/tex'>V_{ADC}=15\frac{220000}{1000000+220000}=2.705\text{ V}</script></p>
<p>This keeps the input below the ESP32-S3 datasheet's 2.9 V effective calibrated range for the highest attenuation setting.</p>
<p>The divider current at 15 V is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_3bad6b6c6d698ebaf0deeb2c1e473624.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{DIV}=\frac{15}{1000000+220000}=12.3\mu\text{A}" /></span><script type='math/tex'>I_{DIV}=\frac{15}{1000000+220000}=12.3\mu\text{A}</script></p>
<p>The ADC does not see 1.22 MΩ as its source resistance. The Thevenin resistance at the divider output is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_add2bb1513c56f52f309512b1f7d9408.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="R_{TH}=R_{TOP}\parallel R_{BOTTOM}" /></span><script type='math/tex'>R_{TH}=R_{TOP}\parallel R_{BOTTOM}</script></p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_01bd6c320f788e257b00ef2f005be99b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="R_{TH}=\frac{1000000\times220000}{1000000+220000}=180.3\text{ k}\Omega" /></span><script type='math/tex'>R_{TH}=\frac{1000000\times220000}{1000000+220000}=180.3\text{ k}\Omega</script></p>
<p>This is still a relatively high source impedance. An assumed leakage current of 100 nA would produce approximately:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_5b0a0eb47806ec13eae54dde4dce27fd.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{error}=100\text{ nA}\times180.3\text{ k}\Omega=18.0\text{ mV}" /></span><script type='math/tex'>V_{error}=100\text{ nA}\times180.3\text{ k}\Omega=18.0\text{ mV}</script></p>
<p>At the original 15 V input, the equivalent error is multiplied by the divider ratio:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_2ac995f837aa778b04a40ae0f88177f6.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{IN_error}=18.0\text{ mV}\times\frac{1220000}{220000}\approx100\text{ mV}" /></span><script type='math/tex'>V_{IN_error}=18.0\text{ mV}\times\frac{1220000}{220000}\approx100\text{ mV}</script></p>
<p>This calculation only estimates static leakage error. Sampling transients, resistor tolerance, ADC error, noise, temperature, and PCB leakage can add further error.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Adding a Capacitor to the ADC Input</strong></h2>
<p>Espressif recommends placing a 0.1 µF capacitor between the ADC pin and ground. The capacitor performs two useful functions:</p>
<ul>
<li>It filters high-frequency noise before it reaches the ADC.</li>
<li>It acts as a local charge reservoir for the ADC sampling capacitor.</li>
</ul>
<p><img data-dominant-color="f6f6f7" data-has-transparency="false" style="--dominant-color: #f6f6f7;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12364 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-s3-adc-circuit.avif" alt="ESP32-S3 ADC Added capacitor" width="724" height="543" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-s3-adc-circuit.avif 724w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-s3-adc-circuit-300x225.avif 300w" sizes="auto, (max-width: 724px) 100vw, 724px" /></p>
<p>For the 1 MΩ and 220 kΩ divider, the approximate time constant with a 0.1 µF capacitor is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_fc9f92edb00764728e1996ea4fce8204.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\tau=R_{TH}C" /></span><script type='math/tex'>\tau=R_{TH}C</script></p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_18a3fdec57ae339d54c9785dd5cbc5a7.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\tau=180.3\text{ k}\Omega\times0.1\mu\text{F}=18.0\text{ ms}" /></span><script type='math/tex'>\tau=180.3\text{ k}\Omega\times0.1\mu\text{F}=18.0\text{ ms}</script></p>
<p>A capacitor is normally considered nearly settled after about five time constants:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_f08e5213fb1b9dd9e33eb4c4a1136fad.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="t_{settle}\approx5\tau=90\text{ ms}" /></span><script type='math/tex'>t_{settle}\approx5\tau=90\text{ ms}</script></p>
<p>If the divider is permanently connected, the capacitor remains charged and this delay is not usually needed before every measurement. If the divider is switched on only when a reading is required, the firmware should wait approximately 90 to 100 ms before sampling.</p>
<p>The capacitor and divider also create a low-pass filter. Its approximate cutoff frequency is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_7b1eed22ba7d7c6e86854fb49f3183a8.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="f_c=\frac{1}{2\pi R_{TH}C}" /></span><script type='math/tex'>f_c=\frac{1}{2\pi R_{TH}C}</script></p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_dd474d35e45eb768446289d4f8b57329.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="f_c=\frac{1}{2\pi(180.3\text{k}\Omega)(0.1\mu\text{F})}\approx8.8\text{ Hz}" /></span><script type='math/tex'>f_c=\frac{1}{2\pi(180.3\text{k}\Omega)(0.1\mu\text{F})}\approx8.8\text{ Hz}</script></p>
<p>This is suitable for battery voltage, temperature, and other slowly changing signals. It may be too slow for audio, vibration, or fast control signals.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Method 1: Reduce the Divider Resistance</strong></h2>
<p>The simplest way to reduce loading error is to use lower resistor values. A 100 kΩ and 22 kΩ divider has the same ratio as the 1 MΩ and 220 kΩ divider, but its Thevenin resistance is only about 18 kΩ.</p>
<table>
<thead>
<tr>
<th>Divider</th>
<th>Current at 15 V</th>
<th>Thevenin Resistance</th>
</tr>
</thead>
<tbody>
<tr>
<td>1 MΩ and 220 kΩ</td>
<td>12.3 µA</td>
<td>180.3 kΩ</td>
</tr>
<tr>
<td>100 kΩ and 22 kΩ</td>
<td>123 µA</td>
<td>18.0 kΩ</td>
</tr>
<tr>
<td>47 kΩ and 10 kΩ</td>
<td>263 µA</td>
<td>8.25 kΩ</td>
</tr>
</tbody>
</table>
<p>The lower-value divider consumes more current, but it is less affected by leakage, PCB contamination, and ADC sampling transients.</p>
<p>For mains-powered equipment, the additional current may not matter. For a battery-powered sensor that sleeps most of the time, a switched divider may provide a better compromise.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Method 2: Use a Switched Voltage Divider</strong></h2>
<p>A switched divider keeps the resistors disconnected while the ESP32-S3 is sleeping. The firmware enables the divider shortly before taking a measurement and disables it afterward.</p>
<p>A high-side P-channel MOSFET or a low-leakage analog load switch can be used to connect the upper resistor to the measured supply. After enabling the circuit, wait for the divider and filter capacitor to settle.</p>
<p>The switch itself must also be selected carefully. MOSFET off-state leakage, gate leakage, and contamination around the ADC node may become significant when using megohm resistors.</p>
<p>A low-side switch should generally be avoided for this application because it can leave the ADC pin connected to the measured voltage through the upper resistor while the lower resistor is disconnected. This can make the pin float, forward-bias protection structures, or create an unexpected current path.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Method 3: Buffer the Signal with an Op-Amp</strong></h2>
<p>For very high-impedance sensors, the best solution is usually a voltage follower.</p>
<p>The sensor or resistor network connects to the op-amp input, while the op-amp output drives the ESP32-S3 ADC. The op-amp presents a very high input resistance to the sensor and a low output resistance to the ADC.</p>
<p>Look for the following characteristics:</p>
<ul>
<li>Very low input bias current</li>
<li>Operation from a 3.3 V supply</li>
<li>Rail-to-rail input and output</li>
<li>Input common-mode range that includes the measured signal</li>
<li>Output swing that covers the required ADC voltage range</li>
<li>Low offset voltage and low drift</li>
<li>Stability when driving a capacitive load</li>
</ul>
<p>A small resistor, typically between 100 Ω and 1 kΩ, can be placed between the op-amp output and the ADC capacitor. This resistor isolates the amplifier from the capacitor and can improve stability.</p>
<p>Some sensor types have an impedance so high that they should never be connected directly to a microcontroller ADC. In such cases, an amplifier designed for extremely low input bias current is required.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Method 4: Improve PCB Layout and Cleanliness</strong></h2>
<p>At megohm impedance levels, the PCB becomes part of the analog circuit. Flux residue, dust, moisture, solder mask contamination, and fingerprints can create leakage paths comparable to the signal current.</p>
<p>Use these layout practices:</p>
<ul>
<li>Keep the high-impedance trace short.</li>
<li>Place the filter capacitor close to the ESP32-S3 pin.</li>
<li>Keep the trace away from switching regulators, clocks, USB, and antenna circuitry.</li>
<li>Avoid routing digital signals parallel to the ADC trace.</li>
<li>Clean flux residue from the board.</li>
<li>Use a ground plane around ordinary low-level analog traces.</li>
<li>For extremely high impedance, consider a driven guard around the sensitive node.</li>
</ul>
<p>Also make sure that internal pull-up and pull-down resistors are disabled on the ADC pin. These resistors are approximately tens of kilohms and will completely disturb a high-value divider.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Choosing the ADC Attenuation</strong></h2>
<p>The ESP32-S3 ADC supports several attenuation settings. Higher attenuation permits a larger voltage at the pin but usually produces more conversion error near the upper end of the range.</p>
<table>
<thead>
<tr>
<th>Arduino Setting</th>
<th>Approximate ESP32-S3 Range</th>
<th>Datasheet Effective Calibrated Range</th>
</tr>
</thead>
<tbody>
<tr>
<td><em>ADC_ATTEN_DB_0</em></td>
<td>Up to approximately 0.95 V</td>
<td>0 to 0.85 V</td>
</tr>
<tr>
<td><em>ADC_ATTEN_DB_2_5</em></td>
<td>Up to approximately 1.25 V</td>
<td>0 to 1.10 V</td>
</tr>
<tr>
<td><em>ADC_ATTEN_DB_6</em></td>
<td>Up to approximately 1.75 V</td>
<td>0 to 1.60 V</td>
</tr>
<tr>
<td><em>ADC_ATTEN_DB_11</em></td>
<td>Up to approximately 3.10 V</td>
<td>0 to 2.90 V</td>
</tr>
</tbody>
</table>
<p>The Arduino API continues to use the name <em>ADC_ATTEN_DB_11</em>, while newer ESP-IDF documentation may describe the highest attenuation as 12 dB.</p>
<p>For best accuracy, do not design the divider so that the maximum expected voltage is exactly at the upper ADC limit. Leave margin for supply variation, resistor tolerance, transients, and calibration error.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Arduino ESP32-S3 ADC Example</strong></h2>
<p>The following sketch reads a high-value 1 MΩ and 220 kΩ divider connected to GPIO4. GPIO4 is an ADC1 pin on the ESP32-S3.</p>
<p>The circuit should include a 0.1 µF ceramic capacitor from GPIO4 to ground.</p>
<pre><pre><code class="language-cpp">#include &lt;Arduino.h&gt;

const uint8_t ADC_PIN = 4;

const float R_TOP = 1000000.0f;
const float R_BOTTOM = 220000.0f;

const uint8_t SAMPLE_COUNT = 32;

uint32_t readAdcMillivolts()
{
    uint64_t total = 0;

    // Dummy conversion to help the ADC input settle.
    (void)analogRead(ADC_PIN);
    delayMicroseconds(500);

    for (uint8_t i = 0; i &lt; SAMPLE_COUNT; i++)
    {
        total += analogReadMilliVolts(ADC_PIN);
        delayMicroseconds(500);
    }

    return (uint32_t)(total / SAMPLE_COUNT);
}

float readInputVoltage()
{
    const uint32_t adcMillivolts = readAdcMillivolts();
    const float adcVoltage = adcMillivolts / 1000.0f;

    return adcVoltage * (R_TOP + R_BOTTOM) / R_BOTTOM;
}

void setup()
{
    Serial.begin(115200);

    analogReadResolution(12);
    analogSetPinAttenuation(ADC_PIN, ADC_ATTEN_DB_11);

    delay(100);
}

void loop()
{
    const uint32_t adcMillivolts = readAdcMillivolts();
    const float inputVoltage =
        (adcMillivolts / 1000.0f) *
        (R_TOP + R_BOTTOM) / R_BOTTOM;

    Serial.printf(
        &quot;ADC: %lu mV, Input: %.3f V\n&quot;,
        (unsigned long)adcMillivolts,
        inputVoltage
    );

    delay(1000);
}</code></pre></pre>
<p>The <em>analogReadMilliVolts()</em> function returns a calibrated voltage rather than an uncorrected raw ADC count. Averaging reduces random noise, while the dummy conversion helps after the input multiplexer or attenuation circuitry has changed state.</p>
<p>The code does not compensate for resistor tolerance, leakage, PCB contamination, or gain and offset error in the complete circuit. Final calibration should be performed using a trusted multimeter or voltage reference.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Two-Point Calibration</strong></h2>
<p>For better accuracy, apply two known input voltages and record the corresponding ESP32-S3 readings. The corrected voltage can then be represented as:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_49285d1232c2a11d26264dee8f43deaa.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{corrected}=mV_{measured}+b" /></span><script type='math/tex'>V_{corrected}=mV_{measured}+b</script></p>
<p>The gain is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_12a6f19256e56bf72976d94897ee7693.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="m=\frac{V_{actual2}-V_{actual1}}{V_{measured2}-V_{measured1}}" /></span><script type='math/tex'>m=\frac{V_{actual2}-V_{actual1}}{V_{measured2}-V_{measured1}}</script></p>
<p>The offset is:</p>
<p><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_486d713efdead5e330ce9b674e16985b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="b=V_{actual1}-mV_{measured1}" /></span><script type='math/tex'>b=V_{actual1}-mV_{measured1}</script></p>
<p>Store the resulting gain and offset in flash or non-volatile storage. This calibration corrects consistent gain and offset errors, but it cannot fully correct unstable leakage, insufficient settling time, or temperature-dependent errors.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Sampling High-Impedance ADC Channels</strong></h2>
<p>Use the following sampling sequence when reading a high-impedance source:</p>
<ol>
<li>Enable the divider or sensor power, when applicable.</li>
<li>Wait for the external RC network to settle.</li>
<li>Select the ADC channel and attenuation.</li>
<li>Perform one dummy conversion.</li>
<li>Take multiple calibrated readings.</li>
<li>Average or median-filter the samples.</li>
<li>Disable the measurement circuit when finished.</li>
</ol>
<p>Do not sample faster than the source and filter network can settle. Increasing the sample rate does not improve a circuit whose source cannot recharge the ADC input between conversions.</p>
<p>If several ADC channels have very different voltages, consider reading each channel twice and discarding the first conversion. A buffer amplifier for each high-impedance channel provides a more reliable solution.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Wi-Fi and ADC Noise</strong></h2>
<p>ADC1 can be used while Wi-Fi is active, but wireless transmission causes rapidly changing current consumption. These current pulses can introduce noise into the power and ground networks.</p>
<p>For precision measurements:</p>
<ul>
<li>Use ADC1 instead of ADC2.</li>
<li>Add proper supply decoupling near the ESP32-S3.</li>
<li>Place the ADC capacitor close to the pin.</li>
<li>Avoid taking critical readings during Wi-Fi transmission.</li>
<li>Average several readings.</li>
<li>Consider temporarily stopping wireless activity during calibration or precision sampling.</li>
</ul>
<p>The ESP32-S3 datasheet's ADC characterization was performed with a 100 nF capacitor, a DC input signal, an ambient temperature of 25 °C, and Wi-Fi disabled. A real application may therefore show more noise than the published test results.</p>
<p><strong><div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div></strong></p>
<h2><strong>Common ESP32-S3 High-Impedance ADC Problems</strong></h2>
<table>
<thead>
<tr>
<th>Symptom</th>
<th>Likely Cause</th>
<th>Recommended Fix</th>
</tr>
</thead>
<tbody>
<tr>
<td>Reading is consistently low</td>
<td>Leakage or insufficient acquisition settling</td>
<td>Lower source resistance, add a capacitor, or use a buffer</td>
</tr>
<tr>
<td>First reading is different</td>
<td>Residual charge from another channel</td>
<td>Discard the first conversion</td>
</tr>
<tr>
<td>Reading changes when touched</td>
<td>Input is floating or extremely high impedance</td>
<td>Add a defined bias path and buffer the signal</td>
</tr>
<tr>
<td>Channels affect each other</td>
<td>Multiplexer charge and insufficient settling</td>
<td>Slow the sampling sequence or buffer each channel</td>
</tr>
<tr>
<td>Voltage changes in humid conditions</td>
<td>PCB surface leakage</td>
<td>Clean the board and improve guarding</td>
</tr>
<tr>
<td>Large spikes during wireless activity</td>
<td>Supply or ground noise</td>
<td>Improve decoupling, layout, and sampling timing</td>
</tr>
<tr>
<td>Upper voltage range is inaccurate</td>
<td>Operating near the attenuation limit</td>
<td>Change the divider ratio and add voltage margin</td>
</tr>
</tbody>
</table>
<h2>ESP32-S3 High-Impedance ADC Design Checklist</h2>
<ul>
<li>Prefer GPIO1 to GPIO10 on ADC1.</li>
<li>Keep the maximum ADC voltage below the calibrated effective range.</li>
<li>Add approximately 0.1 µF between the ADC pin and ground.</li>
<li>Calculate the divider's Thevenin resistance.</li>
<li>Estimate leakage error using the required voltage accuracy.</li>
<li>Allow at least five external RC time constants after switching the circuit on.</li>
<li>Discard the first reading after changing channels.</li>
<li>Average multiple calibrated readings.</li>
<li>Disable internal pull-up and pull-down resistors.</li>
<li>Keep the ADC trace short and away from noisy signals.</li>
<li>Clean the PCB when using megohm impedances.</li>
<li>Use an op-amp buffer when the sensor impedance is very high.</li>
<li>Calibrate the complete circuit, not only the ADC.</li>
</ul>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Conclusion</strong></h2>
<p>The ESP32-S3 ADC input should not be treated as an ideal infinite-resistance voltmeter. Static leakage current can create voltage error when the source impedance is high, while the ADC's internal sampling network can temporarily load the signal during each conversion.</p>
<p>A 0.1 µF capacitor at the ADC pin is an important starting point and is recommended by Espressif. However, the capacitor must be combined with adequate settling time, suitable divider values, ADC1 channel selection, averaging, careful PCB layout, and calibration.</p>
<p>For battery monitoring and other slowly changing signals, a high-value divider with a local capacitor may provide acceptable performance. For extremely high-impedance sensors or precision measurements, a low-bias-current op-amp buffer is the more reliable solution.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>References</strong></h2>
<ul>
<li><a href="https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf">Espressif ESP32-S3 Series Datasheet</a></li>
<li><a href="https://docs.espressif.com/projects/esp-hardware-design-guidelines/en/latest/esp32s3/">ESP32-S3 Hardware Design Guidelines</a></li>
<li><a href="https://docs.espressif.com/projects/arduino-esp32/en/latest/api/adc.html">Arduino-ESP32 ADC API</a></li>
<li><a href="https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-reference/peripherals/adc/adc_calibration.html">ESP32-S3 ADC Calibration Driver</a></li>
</ul>
<p>The post <a href="https://www.teachmemicro.com/esp32-s3-analog-input-leakage-current/">ESP32-S3 Analog Input Leakage Current: High-Impedance ADC Design</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.teachmemicro.com/esp32-s3-analog-input-leakage-current/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Power an ESP32-S3 with AA Batteries</title>
		<link>https://www.teachmemicro.com/how-to-power-an-esp32-s3-with-aa-batteries/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-power-an-esp32-s3-with-aa-batteries</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 21 Jul 2026 00:36:29 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12295</guid>

					<description><![CDATA[<p>The ESP32-S3 is commonly powered through its USB port while programming and testing. However, USB power is not always practical for projects installed outdoors, mounted on walls, or placed far from an electrical outlet. In this tutorial, we will build an ESP32-S3 AA battery power supply using two AA cells and an MT3608 boost converter. &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-power-an-esp32-s3-with-aa-batteries/">How to Power an ESP32-S3 with AA Batteries</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The ESP32-S3 is commonly powered through its USB port while programming and testing. However, USB power is not always practical for projects installed outdoors, mounted on walls, or placed far from an electrical outlet.</p>
<p>In this tutorial, we will build an <strong>ESP32-S3 AA battery power supply</strong> using two AA cells and an MT3608 boost converter. We will also add an optional battery-voltage monitor and use deep sleep to make the batteries last longer.</p>
<p><span id="more-12295"></span></p>
<p>The short answer is that an ESP32-S3 can run from AA batteries, but the batteries should not normally be connected directly to the development board. The voltage must remain stable, and the power circuit must handle the short current bursts produced when the ESP32-S3 uses Wi-Fi or Bluetooth.</p>
<blockquote><p>This tutorial assumes an ESP32-S3 development board similar to the Espressif ESP32-S3-DevKitC-1. Other ESP32-S3 boards may have different regulators, power LEDs, USB circuits, pin labels, and power-input arrangements. Check your board’s schematic before applying power through its 3V3 pin.</p></blockquote>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Can an ESP32-S3 Run on AA Batteries?</strong></h2>
<p>Yes. Two AA batteries can power an ESP32-S3 when used with a suitable voltage converter. The ESP32-S3 chip normally operates from a 3.3 V supply. Its acceptable supply range is approximately 3.0 V to 3.6 V. The power source must also handle the ESP32-S3’s brief current peaks when its radio begins transmitting.</p>
<p>A pair of alkaline AA batteries has a nominal voltage of:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_5e1a9e3affa6368dbf38f3a867844cfd.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="2 \times 1.5\text{ V} = 3.0\text{ V}" /></span><script type='math/tex'>2 \times 1.5\text{ V} = 3.0\text{ V}</script></p>
<p>At first glance, 3.0 V may appear close enough to the ESP32-S3’s operating voltage. The problem is that battery voltage does not remain fixed. A fresh alkaline cell may measure above 1.5 V without a load. Its voltage then gradually falls as the battery discharges. The terminal voltage can also drop suddenly when the ESP32-S3 draws a high-current pulse. Directly connecting two AA cells to the 3V3 pin may therefore work initially but become unreliable as the batteries discharge. The ESP32-S3 may reset when Wi-Fi starts, fail to boot, or stop operating long before the batteries are completely empty.</p>
<p>To avoid this problem, we will use an <strong>MT3608 boost converter</strong> adjusted to provide a regulated 3.3 V output.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>What Is the MT3608?</strong></h2>
<p>The MT3608 is a small step-up or <strong>boost converter</strong>. A boost converter takes a lower DC voltage and raises it to a higher regulated voltage.</p>
<p>Common MT3608 breakout modules include:</p>
<ul>
<li>An MT3608 switching-regulator IC</li>
<li>An inductor</li>
<li>A Schottky diode</li>
<li>Input and output capacitors</li>
<li>A multi-turn adjustment potentiometer</li>
<li>IN+, IN−, OUT+, and OUT− connection pads</li>
</ul>
<p>The MT3608 IC has an input-voltage range of approximately 2 V to 24 V and an adjustable output. Its switching frequency is 1.2 MHz. For this project, the module raises the changing voltage from two AA cells to a regulated 3.3 V:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_a017593452a0a1194a9cc141ebf45f55.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{\text{BATTERY}} \longrightarrow \text{MT3608} \longrightarrow 3.3\text{ V}" /></span><script type='math/tex'>V_{\text{BATTERY}} \longrightarrow \text{MT3608} \longrightarrow 3.3\text{ V}</script></p>
<p>The MT3608 is inexpensive and widely available, which makes it practical for a prototype or hobby project.</p>
<h3><strong>The MT3608 Is Boost-Only</strong></h3>
<p>An important limitation is that the MT3608 can only raise voltage. It cannot reduce a voltage that is higher than the desired output. For example, an MT3608 should not be used to convert a three-cell alkaline battery pack from approximately 4.5 V down to 3.3 V. That would require a buck converter or buck-boost converter instead. Two alkaline or two NiMH AA cells remain below the desired 3.3 V output during normal use, so they are suitable for this circuit.</p>
<h3><strong>MT3608 Current Ratings</strong></h3>
<p>Many online listings describe an MT3608 module as a “2 A boost converter.” This does not necessarily mean that the module can continuously supply 2 A at its output under all conditions.</p>
<p>The available output current depends on:</p>
<ul>
<li>Input voltage</li>
<li>Output voltage</li>
<li>Converter efficiency</li>
<li>Inductor rating</li>
<li>Diode rating</li>
<li>PCB layout</li>
<li>Cooling</li>
<li>Battery condition and internal resistance</li>
</ul>
<p>At a low battery voltage, the converter must draw more current from the batteries than it delivers at its output.</p>
<p>Ignoring converter losses for a moment:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_9c7711005537dd32c6d3a3dc34c612e8.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{\text{IN}}I_{\text{IN}} \approx V_{\text{OUT}}I_{\text{OUT}}" /></span><script type='math/tex'>V_{\text{IN}}I_{\text{IN}} \approx V_{\text{OUT}}I_{\text{OUT}}</script></p>
<p>After including converter efficiency:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_d5cd53bf6b6d6502e7d45a47a78dae4e.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{IN}} = \frac{V_{\text{OUT}}I_{\text{OUT}}}{V_{\text{IN}}\eta}" /></span><script type='math/tex'>I_{\text{IN}} = \frac{V_{\text{OUT}}I_{\text{OUT}}}{V_{\text{IN}}\eta}</script></p>
<p>For example, supplying 500 mA at 3.3 V from a 2.0 V battery pack at 85% efficiency would require approximately:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_2401054c657c8d8638c3d133b2658c46.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{IN}} = \frac{3.3 \times 0.5}{2.0 \times 0.85}" /></span><script type='math/tex'>I_{\text{IN}} = \frac{3.3 \times 0.5}{2.0 \times 0.85}</script></p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_08bbb5dba71e84cc759afcb7a28838c3.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{IN}} \approx 0.97\text{ A}" /></span><script type='math/tex'>I_{\text{IN}} \approx 0.97\text{ A}</script></p>
<p>The ESP32-S3 will not continuously draw 500 mA in a typical sensor project, but the batteries and converter must tolerate its short radio-current peaks.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>AA Battery Configurations for the MT3608</strong></h2>
<p>Here is what happens with common AA battery arrangements:</p>
<table>
<thead>
<tr>
<th>Battery configuration</th>
<th>Approximate nominal voltage</th>
<th>Suitable with MT3608?</th>
</tr>
</thead>
<tbody>
<tr>
<td>One alkaline AA</td>
<td>1.5 V</td>
<td>No; below the MT3608’s specified input range</td>
</tr>
<tr>
<td>Two alkaline AAs</td>
<td>3.0 V</td>
<td>Yes; boost to 3.3 V</td>
</tr>
<tr>
<td>Three alkaline AAs</td>
<td>4.5 V</td>
<td>No; the MT3608 cannot step down to 3.3 V</td>
</tr>
<tr>
<td>Four alkaline AAs</td>
<td>6.0 V</td>
<td>No; use a step-down converter</td>
</tr>
<tr>
<td>Two NiMH AAs</td>
<td>2.4 V</td>
<td>Yes; boost to 3.3 V</td>
</tr>
<tr>
<td>Three NiMH AAs</td>
<td>3.6 V</td>
<td>No; the pack may be above the required output</td>
</tr>
</tbody>
</table>
<p>Two AA cells are therefore the most convenient arrangement for this MT3608 circuit.</p>
<p><img data-dominant-color="d7d6d1" data-has-transparency="false" style="--dominant-color: #d7d6d1;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12303 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-powering-options.avif" alt="ESP32-S3 Powering Options" width="836" height="471" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-powering-options.avif 836w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-powering-options-300x169.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-powering-options-768x433.avif 768w" sizes="auto, (max-width: 836px) 100vw, 836px" /></p>
<p>Because the MT3608 requires approximately 2 V at its input, the converter will eventually stop operating when a two-cell pack falls to about 1 V per cell.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Required Components</strong></h2>
<p>For the basic ESP32-S3 AA battery circuit, you will need:</p>
<ul>
<li>ESP32-S3 development board</li>
<li>MT3608 adjustable boost-converter module</li>
<li>Two-cell AA battery holder</li>
<li>Two matching AA batteries</li>
<li>On/off switch</li>
<li>470 µF electrolytic capacitor rated for at least 6.3 V</li>
<li>0.1 µF ceramic capacitor</li>
<li>Multimeter</li>
<li>Jumper wires or soldered connections</li>
</ul>
<p>For battery-voltage measurement, you will also need:</p>
<ul>
<li>Two 100 kΩ resistors</li>
<li>One optional 0.1 µF capacitor</li>
</ul>
<p>Use batteries of the same type, brand, capacity, and approximate age. Do not mix alkaline batteries with rechargeable batteries in the same holder.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Adjusting the MT3608 to 3.3 V</strong></h2>
<p>The MT3608 is an adjustable converter. It must be configured before it is connected to the ESP32-S3.</p>
<blockquote><p><strong>Do not connect the MT3608 output to the ESP32-S3 until you have measured and adjusted its output voltage.</strong> An incorrectly adjusted module can output a voltage high enough to damage the board.</p></blockquote>
<p>Follow these steps:</p>
<ol>
<li>Insert the two AA batteries into the holder.</li>
<li>Connect the battery holder’s positive wire to MT3608 IN+.</li>
<li>Connect the battery holder’s negative wire to MT3608 IN−.</li>
<li>Set your multimeter to DC voltage.</li>
<li>Measure between MT3608 OUT+ and OUT−.</li>
<li>Turn the adjustment potentiometer slowly while watching the multimeter.</li>
<li>Stop when the output reads approximately 3.30 V.</li>
<li>Disconnect the batteries before wiring the ESP32-S3.</li>
</ol>
<p>The potentiometer on many MT3608 modules is multi-turn. It may require several rotations before the output voltage visibly changes. Do not assume that every module adjusts in the same direction. Watch the multimeter while turning the potentiometer.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S3 AA Battery Wiring Diagram</strong></h2>
<p>After adjusting the MT3608 to 3.3 V, connect the complete circuit as follows:<br />
<img data-dominant-color="d5d2d1" data-has-transparency="false" style="--dominant-color: #d5d2d1;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12300 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/ESP32-S3-AA-battery-circuit.avif" alt="ESP32-S3-AA-battery-circuit" width="849" height="539" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/ESP32-S3-AA-battery-circuit.avif 849w, https://www.teachmemicro.com/wp-content/uploads/2026/07/ESP32-S3-AA-battery-circuit-300x190.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/07/ESP32-S3-AA-battery-circuit-768x488.avif 768w" sizes="auto, (max-width: 849px) 100vw, 849px" /><br />
Observe the polarity of the electrolytic capacitor:</p>
<ul>
<li>Capacitor positive lead to MT3608 OUT+</li>
<li>Capacitor negative lead to ground</li>
</ul>
<p>The 470 µF capacitor helps supply short bursts of current when the ESP32-S3 radio begins transmitting. The 0.1 µF ceramic capacitor helps suppress higher-frequency noise. Install the capacitors close to the ESP32-S3 power pins rather than close to the battery holder. Long wires add resistance and inductance, reducing the capacitors’ ability to support fast current pulses.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Why We Connect the MT3608 to the 3V3 Pin</strong></h2>
<p>Most ESP32-S3 development boards include an onboard regulator that converts USB or 5 V input to 3.3 V.</p>
<p>One option would be to adjust the MT3608 to 5 V and connect it to the board’s 5V input. However, the power would pass through two conversion stages:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_11baa0169c342cec13b386afdb3ea105.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\text{AA batteries} \longrightarrow 5\text{ V boost} \longrightarrow 3.3\text{ V regulator}" /></span><script type='math/tex'>\text{AA batteries} \longrightarrow 5\text{ V boost} \longrightarrow 3.3\text{ V regulator}</script></p>
<p>Each conversion stage wastes some energy.</p>
<p>By adjusting the MT3608 to 3.3 V and connecting it directly to the 3V3 pin, we bypass the development board’s onboard regulator:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_1865d1c2872fe8631aeb1f9bd7284218.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\text{AA batteries} \longrightarrow 3.3\text{ V boost} \longrightarrow \text{ESP32-S3}" /></span><script type='math/tex'>\text{AA batteries} \longrightarrow 3.3\text{ V boost} \longrightarrow \text{ESP32-S3}</script></p>
<p>The disadvantage is that the board no longer has another regulator between the MT3608 and the ESP32-S3. The MT3608 output must therefore be adjusted accurately and checked before every major wiring change.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Important USB Power Warning</strong></h2>
<p>Disconnect the AA battery supply before connecting the ESP32-S3 to USB unless the board or external circuit includes proper power-source isolation.</p>
<p>Connecting USB power and an external 3.3 V supply at the same time may cause current to flow backward into one of the power sources.</p>
<p>A simple approach during development is:</p>
<ol>
<li>Switch off or disconnect the AA battery holder.</li>
<li>Connect USB and upload the sketch.</li>
<li>Disconnect USB.</li>
<li>Reconnect or switch on the battery supply.</li>
</ol>
<p>A more advanced design can use:</p>
<ul>
<li>Schottky-diode isolation</li>
<li>An ideal-diode controller</li>
<li>A load switch</li>
<li>A power multiplexer</li>
<li>A jumper that selects USB or battery power</li>
</ul>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Adding an ESP32-S3 Battery Voltage Monitor</strong></h2>
<p>The ESP32-S3 can measure the battery voltage using one of its ADC pins. The measurement must be taken from the battery side of the MT3608. Measuring the converter output would normally show approximately 3.3 V even as the batteries discharge.</p>
<p>For a two-cell AA pack, use the following voltage divider:<br />
<img data-dominant-color="d5d1cf" data-has-transparency="false" style="--dominant-color: #d5d1cf;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12301 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/ESP32-S3-AA-battery-level-detection.avif" alt="ESP32 S3 AA battery detection" width="805" height="526" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/ESP32-S3-AA-battery-level-detection.avif 805w, https://www.teachmemicro.com/wp-content/uploads/2026/07/ESP32-S3-AA-battery-level-detection-300x196.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/07/ESP32-S3-AA-battery-level-detection-768x502.avif 768w" sizes="auto, (max-width: 805px) 100vw, 805px" /><br />
GPIO4 is an ADC-capable pin on the ESP32-S3 and corresponds to ADC1 channel 3.</p>
<p>The two equal resistors divide the battery voltage by two:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_d0add98ad55c0cdc88048b507a466899.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{\text{ADC}} = V_{\text{BATTERY}} \times \frac{100\,\text{k}\Omega}{100\,\text{k}\Omega + 100\,\text{k}\Omega}" /></span><script type='math/tex'>V_{\text{ADC}} = V_{\text{BATTERY}} \times \frac{100\,\text{k}\Omega}{100\,\text{k}\Omega + 100\,\text{k}\Omega}</script></p>
<p>Therefore:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_d130dfdb8911f0c0c85c0791bdfeae02.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{\text{BATTERY}} = V_{\text{ADC}} \times 2" /></span><script type='math/tex'>V_{\text{BATTERY}} = V_{\text{ADC}} \times 2</script></p>
<p>A fresh two-cell alkaline pack measuring 3.2 V produces approximately:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_ec4685acde79bad4f6c18310f6c88ac9.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{\text{ADC}} = \frac{3.2}{2} = 1.6\text{ V}" /></span><script type='math/tex'>V_{\text{ADC}} = \frac{3.2}{2} = 1.6\text{ V}</script></p>
<p>An optional 0.1 µF capacitor from GPIO4 to ground can reduce noise and help stabilize the ADC reading. This is especially useful because the two 100 kΩ resistors create a relatively high-impedance source for the ADC.</p>
<p>The resistor divider continuously draws a small current:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_43b8c72aa5b0a3b28486faa3fbb85209.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I = \frac{3.0\text{ V}}{200\,\text{k}\Omega}" /></span><script type='math/tex'>I = \frac{3.0\text{ V}}{200\,\text{k}\Omega}</script></p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_600f9c5dd5d57ff227ea07eadc9434ac.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I = 15\,\mu\text{A}" /></span><script type='math/tex'>I = 15\,\mu\text{A}</script></p>
<p>This is small compared with the consumption of a normal ESP32-S3 development board. However, it can become significant in a highly optimized low-power design. On a custom PCB, the voltage divider can be enabled only when a measurement is required by using a MOSFET or analog switch.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>ESP32-S3 AA Battery and Deep-Sleep Code</strong></h2>
<p>The following Arduino sketch reads the AA battery-pack voltage and then places the ESP32-S3 into deep sleep for ten minutes.</p>
<pre><pre><code class="language-cpp">#include &lt;Arduino.h&gt;

#define BATTERY_ADC_PIN       4
#define SLEEP_TIME_SECONDS    600

const float R1 = 100000.0;
const float R2 = 100000.0;

float readBatteryVoltage()
{
  const int numberOfSamples = 32;
  uint32_t totalMillivolts = 0;

  for (int i = 0; i &lt; numberOfSamples; i++)
  {
    totalMillivolts += analogReadMilliVolts(BATTERY_ADC_PIN);
    delay(5);
  }

  float adcMillivolts =
      totalMillivolts / (float)numberOfSamples;

  float adcVoltage = adcMillivolts / 1000.0;

  // Reconstruct the original battery voltage.
  float batteryVoltage =
      adcVoltage * ((R1 + R2) / R2);

  return batteryVoltage;
}

void setup()
{
  Serial.begin(115200);
  delay(1000);

  analogReadResolution(12);
  analogSetPinAttenuation(BATTERY_ADC_PIN, ADC_11db);

  float batteryVoltage = readBatteryVoltage();

  Serial.print(&quot;AA battery pack voltage: &quot;);
  Serial.print(batteryVoltage, 2);
  Serial.println(&quot; V&quot;);

  /*
   * Read sensors or transmit data here.
   *
   * Keep this section as short as possible.
   * Wi-Fi, Bluetooth and sensors consume much
   * more current than deep sleep.
   */

  Serial.println(&quot;Entering deep sleep...&quot;);
  Serial.flush();

  esp_sleep_enable_timer_wakeup(
      (uint64_t)SLEEP_TIME_SECONDS * 1000000ULL
  );

  esp_deep_sleep_start();
}

void loop()
{
  // The ESP32-S3 restarts setup() after waking
  // from deep sleep.
}</code></pre></pre>
<p>The Arduino-ESP32 API provides <em>analogReadMilliVolts()</em>, which returns a calibrated voltage value rather than a raw ADC count.</p>
<p>The 11 dB attenuation setting increases the input range available to the ADC. The divided battery voltage should remain well below the ESP32-S3 ADC pin’s maximum level.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>How the Deep-Sleep Code Works</strong></h2>
<p>The ESP32-S3 executes <em>setup()</em> after power-up and after every deep-sleep wake-up.</p>
<p><img data-dominant-color="eff1f2" data-has-transparency="false" style="--dominant-color: #eff1f2;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12302 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-power-timeline.avif" alt="" width="836" height="471" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-power-timeline.avif 836w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-power-timeline-300x169.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-power-timeline-768x433.avif 768w" sizes="auto, (max-width: 836px) 100vw, 836px" /></p>
<p>The program performs the following sequence:</p>
<ol>
<li>Configures the ADC.</li>
<li>Reads the AA battery-pack voltage.</li>
<li>Performs the required sensor or communication task.</li>
<li>Configures a ten-minute wake-up timer.</li>
<li>Enters deep sleep.</li>
<li>Restarts when the timer expires.</li>
</ol>
<p>The sleep duration is configured here:</p>
<pre><pre><code class="language-cpp">#define SLEEP_TIME_SECONDS 600</code></pre></pre>
<p>For a five-minute interval, change it to:</p>
<pre><pre><code class="language-cpp">#define SLEEP_TIME_SECONDS 300</code></pre></pre>
<p>For a one-hour interval:</p>
<pre><pre><code class="language-cpp">#define SLEEP_TIME_SECONDS 3600</code></pre></pre>
<p>Deep sleep powers down the ESP32-S3 CPUs and most digital peripherals while retaining selected RTC circuitry. The ESP32-S3 chip itself can reach a deep-sleep current of only a few microamps under datasheet conditions. A complete development board will normally consume more because it may also contain:</p>
<ul>
<li>A voltage regulator</li>
<li>USB-to-UART circuitry</li>
<li>USB interface circuitry</li>
<li>A power indicator LED</li>
<li>An addressable RGB LED</li>
<li>Flash memory</li>
<li>PSRAM</li>
<li>Pull-up resistors</li>
<li>Other support components</li>
</ul>
<p>The MT3608 module also consumes current while operating, even when the ESP32-S3 is asleep.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Is the MT3608 Good for Deep-Sleep Projects?</strong></h2>
<p>The MT3608 is suitable for learning, prototyping, and inexpensive battery-powered projects. However, it is not necessarily the best converter for an ultra-low-power sensor node.</p>
<p>The total sleep current includes:</p>
<ul>
<li>ESP32-S3 development-board sleep current</li>
<li>MT3608 operating current</li>
<li>Any indicator LED on the MT3608 module</li>
<li>Battery-monitor divider current</li>
<li>Sensor standby current</li>
<li>Leakage through connected peripherals</li>
</ul>
<p>Some generic MT3608 modules include an indicator LED that remains on continuously. Even a small LED current can become significant when the ESP32-S3 spends most of its time sleeping. For a project intended to run for many months or years, consider a boost converter designed specifically for low-quiescent-current operation.</p>
<p>The MT3608 remains useful for this tutorial because it is:</p>
<ul>
<li>Inexpensive</li>
<li>Widely available</li>
<li>Easy to adjust</li>
<li>Easy to mount on a breadboard or prototype board</li>
<li>Capable of supplying an ESP32-S3 when properly selected and tested</li>
</ul>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Estimating ESP32-S3 AA Battery Runtime</strong></h2>
<p>Battery runtime depends more on average current than on the highest current peak.</p>
<p>A basic runtime estimate is:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_654b267138c4f0c0834bfd5a80ecf6f1.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\text{Runtime (hours)} = \frac{\text{Battery capacity (mAh)}}{\text{Average battery current (mA)}}" /></span><script type='math/tex'>\text{Runtime (hours)} = \frac{\text{Battery capacity (mAh)}}{\text{Average battery current (mA)}}</script></p>
<p>Suppose the ESP32-S3:</p>
<ul>
<li>Draws 120 mA while awake</li>
<li>Remains awake for five seconds</li>
<li>Wakes every ten minutes</li>
<li>Draws 0.3 mA from the 3.3 V rail while sleeping</li>
</ul>
<p>Its average current on the regulated 3.3 V rail is approximately:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_d4adf6f552dd7113dd7b70281a1f2c21.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{AVG}} = 120\left(\frac{5}{600}\right) + 0.3\left(\frac{595}{600}\right)" /></span><script type='math/tex'>I_{\text{AVG}} = 120\left(\frac{5}{600}\right) + 0.3\left(\frac{595}{600}\right)</script></p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_90fbb09b1d4daaaa2174040ae0e6aa6e.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{AVG}} \approx 1.30\text{ mA}" /></span><script type='math/tex'>I_{\text{AVG}} \approx 1.30\text{ mA}</script></p>
<p>Assuming an average battery voltage of 2.4 V and an MT3608 efficiency of 85%:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_60ae932dd66e7154db3613aa6164d6d1.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{BATTERY}} = \frac{3.3 \times 1.30}{2.4 \times 0.85}" /></span><script type='math/tex'>I_{\text{BATTERY}} = \frac{3.3 \times 1.30}{2.4 \times 0.85}</script></p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_452da26b987489d60c867e59fa7fa1b5.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{BATTERY}} \approx 2.1\text{ mA}" /></span><script type='math/tex'>I_{\text{BATTERY}} \approx 2.1\text{ mA}</script></p>
<p>Using a conservative usable battery capacity of 2000 mAh:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_c72abbcb6bfa2b7690a9f18b72cfe64f.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\text{Runtime} = \frac{2000}{2.1}" /></span><script type='math/tex'>\text{Runtime} = \frac{2000}{2.1}</script></p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_da97dfea8763830023f188315b1552f3.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\text{Runtime} \approx 953\text{ hours}" /></span><script type='math/tex'>\text{Runtime} \approx 953\text{ hours}</script></p>
<p>This is approximately 40 days.</p>
<p>However, this simplified calculation does not include the MT3608 module’s own operating current. Suppose the converter module and its indicator LED draw an additional 1 mA from the battery pack:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_52a6d6edcc84af6d56c47c599f8b53ee.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{TOTAL}} = 2.1\text{ mA} + 1.0\text{ mA}" /></span><script type='math/tex'>I_{\text{TOTAL}} = 2.1\text{ mA} + 1.0\text{ mA}</script></p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_a6824aee4a2390796c7a3eb001e2b14a.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="I_{\text{TOTAL}} = 3.1\text{ mA}" /></span><script type='math/tex'>I_{\text{TOTAL}} = 3.1\text{ mA}</script></p>
<p>The estimated runtime would then become:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_e1a451a43fbb361634f6d3a4cff388c4.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\text{Runtime} = \frac{2000}{3.1}" /></span><script type='math/tex'>\text{Runtime} = \frac{2000}{3.1}</script></p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_7e76d3781b30bc36a3ff78ac59018ba5.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="\text{Runtime} \approx 645\text{ hours}" /></span><script type='math/tex'>\text{Runtime} \approx 645\text{ hours}</script></p>
<p>This is approximately 27 days.</p>
<p>This example shows why converter idle current matters in a deep-sleep project.</p>
<p>Actual runtime may be lower because of:</p>
<ul>
<li>MT3608 quiescent and indicator-LED current</li>
<li>Battery internal resistance</li>
<li>Low-temperature operation</li>
<li>Wi-Fi connection time</li>
<li>Sensor consumption</li>
<li>Development-board LEDs</li>
<li>Battery age</li>
<li>Reduced alkaline capacity at higher discharge currents</li>
<li>The MT3608’s approximately 2 V minimum input</li>
<li>Conversion losses that change with load and battery voltage</li>
</ul>
<p>The best approach is to measure the current of the complete circuit using the actual batteries, converter module, sensors, development board, and firmware.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Alkaline or Rechargeable AA Batteries?</strong></h2>
<p>Both alkaline and NiMH AA batteries can be used with the MT3608.</p>
<h3><strong>Alkaline AA Batteries</strong></h3>
<p>Alkaline batteries have a nominal voltage of 1.5 V per cell. Two cells produce a nominal 3.0 V, which the MT3608 can raise to 3.3 V. Alkaline cells are widely available and have a relatively low self-discharge rate. However, their voltage continuously falls during use, and their effective capacity decreases under heavier loads. The high current pulses produced by Wi-Fi may cause a larger temporary voltage drop when alkaline batteries are old, cold, or partially discharged.</p>
<h3><strong>NiMH Rechargeable AA Batteries</strong></h3>
<p>NiMH batteries typically have a nominal voltage of 1.2 V per cell. Two cells therefore provide approximately:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_8601b2b1661b215f75a0dbb2a6a61d3b.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="2 \times 1.2\text{ V} = 2.4\text{ V}" /></span><script type='math/tex'>2 \times 1.2\text{ V} = 2.4\text{ V}</script></p>
<p>The MT3608 raises this to 3.3 V. NiMH batteries generally handle repeated high-current pulses better than ordinary alkaline batteries and can be recharged many times. For a frequently used wireless sensor, NiMH cells may be more economical. Low-self-discharge NiMH batteries are especially useful when the device spends long periods asleep.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Improving ESP32-S3 AA Battery Life</strong></h2>
<h3><strong>Use Deep Sleep</strong></h3>
<p>Do not leave the ESP32-S3 running continuously if it only needs to take a measurement every few minutes. Deep sleep normally provides a much larger improvement than simply reducing the CPU frequency while the chip remains awake.</p>
<h3><strong>Keep Wi-Fi Sessions Short</strong></h3>
<p>Connecting to Wi-Fi and transmitting data consume considerably more current than sleeping.</p>
<p>A battery-powered application should normally:</p>
<ol>
<li>Wake up.</li>
<li>Read its sensors.</li>
<li>Connect to the network.</li>
<li>Transmit its data.</li>
<li>Disconnect or shut down the radio.</li>
<li>Return to deep sleep.</li>
</ol>
<h3><strong>Remove or Disable Power LEDs</strong></h3>
<p>A power LED drawing even 1 mA becomes significant when the ESP32-S3 spends most of its time in deep sleep. Inspect both the ESP32-S3 board and the MT3608 module for always-on LEDs. Some boards allow the LED resistor to be removed. Others require cutting a trace. Only modify a board after inspecting its schematic and PCB connections.</p>
<h3><strong>Turn Sensors Off While Sleeping</strong></h3>
<p>Some sensors continue drawing current even while the ESP32-S3 is asleep. A load switch, P-channel MOSFET, or regulator-enable pin can disconnect sensor power before deep sleep.</p>
<h3><strong>Keep the Wires Short</strong></h3>
<p>Long, thin wires add resistance. A high current pulse can produce a temporary voltage drop across them:</p>
<p style="text-align: center;"><span class='MathJax_Preview'><img src='https://www.teachmemicro.com/wp-content/plugins/latex/cache/tex_6b4299cfa2a51ebe04bd563cc1a30371.gif' style='vertical-align: middle; border: none; padding-bottom:2px;' class='tex' alt="V_{\text{DROP}} = I \times R" /></span><script type='math/tex'>V_{\text{DROP}} = I \times R</script></p>
<p>Keep the battery, MT3608, capacitor, and ESP32-S3 power connections short and secure.</p>
<h3><strong>Use a Low-Power Converter for Long Deployments</strong></h3>
<p>The MT3608 is useful for prototyping, but a low-quiescent-current fixed-output converter is a better choice when maximizing runtime is more important than minimizing component cost.</p>
<h3><strong>Move to a Custom PCB</strong></h3>
<p>Development boards are excellent for prototyping, but their USB interface, LEDs, regulator, and support circuitry are not always optimized for battery operation. A custom PCB using an ESP32-S3 module and a low-power converter can achieve much lower sleep current.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Troubleshooting the ESP32-S3 MT3608 Circuit</strong></h2>
<h3><strong>The ESP32-S3 Resets When Wi-Fi Starts</strong></h3>
<p>This is usually caused by the 3.3 V rail dropping during a radio-current burst.</p>
<p>Check the following:</p>
<ul>
<li>MT3608 output voltage under load</li>
<li>Battery condition</li>
<li>Battery-holder contacts</li>
<li>Wire length and thickness</li>
<li>Output-capacitor value and placement</li>
<li>Solder joints</li>
<li>MT3608 module quality</li>
</ul>
<p>Place the 470 µF capacitor close to the ESP32-S3. Measure the 3.3 V rail while the ESP32-S3 starts Wi-Fi rather than measuring it only when the board is idle.</p>
<h3><strong>The MT3608 Output Does Not Change</strong></h3>
<p>The adjustment potentiometer may require many turns before the voltage changes. Continue turning it slowly while monitoring the output with a multimeter. Do not connect the ESP32-S3 while experimenting with the adjustment.</p>
<h3><strong>The MT3608 Cannot Reach 3.3 V</strong></h3>
<p>Measure the battery voltage at IN+ and IN−. The input may have fallen below the MT3608’s operating range, or the battery voltage may collapse when a load is connected.</p>
<p>Also inspect:</p>
<ul>
<li>Battery polarity</li>
<li>Loose holder contacts</li>
<li>Incorrect input/output wiring</li>
<li>A damaged converter module</li>
<li>An excessive load on the output</li>
</ul>
<h3><strong>The Board Works with USB but Not with Batteries</strong></h3>
<p>Measure the following voltages:</p>
<ol>
<li>Battery-pack voltage without a load</li>
<li>Battery-pack voltage with the ESP32-S3 connected</li>
<li>MT3608 input voltage</li>
<li>MT3608 output voltage</li>
<li>ESP32-S3 3V3-pin voltage during startup</li>
</ol>
<p>A reading of 3.3 V with no load does not guarantee that the converter can maintain 3.3 V during startup or Wi-Fi transmission.</p>
<h3><strong>The Battery Reading Is Too High or Too Low</strong></h3>
<p>Verify the actual resistor values and voltage-divider wiring. The battery-positive connection must come from the battery side of the MT3608. The ADC pin must connect to the junction of the two resistors. Average several ADC samples and compare the result with a multimeter. A small calibration factor can be added after testing:</p>
<pre><pre><code class="language-cpp">batteryVoltage *= 1.03;</code></pre></pre>
<p>Use a correction factor based on measurements from your own board and resistor divider.</p>
<h3><strong>The Deep-Sleep Current Is Still High</strong></h3>
<p>The ESP32-S3 chip may be sleeping correctly while the rest of the circuit continues drawing current.</p>
<p>Check:</p>
<ul>
<li>ESP32-S3 power LED</li>
<li>MT3608 indicator LED</li>
<li>USB-to-UART circuitry</li>
<li>MT3608 operating current</li>
<li>Sensor standby current</li>
<li>Battery voltage divider</li>
<li>External pull-up resistors</li>
<li>Other connected modules</li>
</ul>
<p>Measure current at the battery holder so that the reading includes the entire circuit rather than only the ESP32-S3 board.</p>
<div class="clear"></div><div style="margin-top:20px; margin-bottom:20px;" class="divider divider-solid"></div>
<h2><strong>Conclusion</strong></h2>
<p>Powering an ESP32-S3 with AA batteries is possible using a properly adjusted MT3608 boost converter. Two alkaline or two NiMH AA cells provide a suitable input for the MT3608. The module raises the changing battery voltage to a regulated 3.3 V that can be applied to the ESP32-S3 board’s 3V3 pin. Before connecting the development board, always use a multimeter to adjust the MT3608 output to 3.3 V. Add a large capacitor close to the ESP32-S3 to support Wi-Fi current bursts, and avoid connecting USB and external 3.3 V power simultaneously unless the sources are properly isolated.</p>
<p>The MT3608 is a practical and affordable choice for prototypes. However, its operating current and any onboard indicator LED can reduce the runtime of a deep-sleep project. Applications intended to operate for many months should use a converter specifically designed for low-quiescent-current battery operation. Battery life will ultimately depend on how long the ESP32-S3 remains awake. By reading sensors quickly, keeping Wi-Fi sessions short, turning off peripherals, and entering deep sleep between measurements, an ESP32-S3 AA battery project can operate for weeks or potentially months.</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-power-an-esp32-s3-with-aa-batteries/">How to Power an ESP32-S3 with AA Batteries</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Connect IR Proximity Sensor with ESP32</title>
		<link>https://www.teachmemicro.com/how-to-connect-ir-proximity-sensor-with-esp32/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-connect-ir-proximity-sensor-with-esp32</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 07 Jul 2026 00:11:42 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<category><![CDATA[Sensor Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12185</guid>

					<description><![CDATA[<p>In this tutorial, we will connect an IR proximity sensor with the ESP32. This sensor is commonly used for obstacle detection, line-following robots, object counters, and simple automation projects where you only need to know if an object is near or not. The common IR proximity sensor module has an infrared LED, an IR receiver, &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-connect-ir-proximity-sensor-with-esp32/">How to Connect IR Proximity Sensor with ESP32</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this tutorial, we will connect an <strong>IR proximity sensor</strong> with the <strong>ESP32</strong>. This sensor is commonly used for <a href="https://www.teachmemicro.com/arduino-robot-obstacle-avoiding/">obstacle detection</a>, <a href="https://www.teachmemicro.com/implementing-pid-for-a-line-follower-robot/">line-following robots</a>, object counters, and simple automation projects where you only need to know if an object is near or not.</p>
<p>The common IR proximity sensor module has an infrared LED, an IR receiver, a comparator IC, and a small potentiometer for adjusting the detection distance. Most modules only have three pins: <strong>VCC</strong>, <strong>GND</strong>, and <strong>OUT</strong>. Some versions also include an <strong>AO</strong> pin for analog output.</p>
<p>For this tutorial, we will first use the digital output pin, as it is the easiest way to detect an object using the ESP32.</p>
<p><span id="more-12185"></span></p>
<h3><strong>ESP32 IR Proximity Sensor Wiring Diagram</strong></h3>
<p>The safest way to connect the IR sensor to the ESP32 is to power the sensor from the <span style="box-sizing: border-box; margin: 0px; padding: 0px;"><strong>ESP32's 3.3V pin</strong></span>. Many IR sensor modules can run from 3.3V to 5V, but the ESP32 GPIO pins are 3.3V pins. Powering the module from 3.3V keeps the OUT signal at a safer voltage level for the ESP32.</p>
<p>Connect the sensor as follows:</p>
<p><img data-dominant-color="d8dbd8" data-has-transparency="false" style="--dominant-color: #d8dbd8;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12189 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-wiring-digital.avif" alt="ESP32 IR Proximity Wiring - Digital" width="660" height="432" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-wiring-digital.avif 660w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-wiring-digital-300x196.avif 300w" sizes="auto, (max-width: 660px) 100vw, 660px" /></p>
<table>
<thead>
<tr>
<th>IR Proximity Sensor Pin</th>
<th>ESP32 Pin</th>
</tr>
</thead>
<tbody>
<tr>
<td>VCC</td>
<td>3V3</td>
</tr>
<tr>
<td>GND</td>
<td>GND</td>
</tr>
<tr>
<td>OUT</td>
<td>GPIO 27</td>
</tr>
</tbody>
</table>
<p>For this example, I used <strong>GPIO 27</strong> as the input pin. You can use other ESP32 GPIO pins, but avoid GPIO pins used for bootstrapping unless you know what you are doing. GPIO 34 to GPIO 39 are input-only pins, so they can also be used for reading sensor signals, but they cannot be used to drive LEDs or other output devices.</p>
<p>If your sensor has four pins, the pins are usually:</p>
<table>
<thead>
<tr>
<th>Sensor Pin</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td>VCC</td>
<td>Power supply</td>
</tr>
<tr>
<td>GND</td>
<td>Ground</td>
</tr>
<tr>
<td>DO</td>
<td>Digital output</td>
</tr>
<tr>
<td>AO</td>
<td>Analog output</td>
</tr>
</tbody>
</table>
<p>Use <strong>DO</strong> for simple object detection. Use <strong>AO</strong> only if you want to read a changing analog value.</p>
<h3><strong>Basic ESP32 IR Sensor Code</strong></h3>
<p>After wiring the sensor, upload this sketch to your ESP32:</p>
<pre><code class="language-cpp">#define IR_SENSOR_PIN 27

void setup() {
  Serial.begin(115200);
  pinMode(IR_SENSOR_PIN, INPUT);
}

void loop() {
  int sensorValue = digitalRead(IR_SENSOR_PIN);

  if (sensorValue == LOW) {
    Serial.println("Object detected");
  } else {
    Serial.println("No object");
  }

  delay(300);
}
</code></pre>
<p>Open the Serial Monitor and set the baud rate to <strong>115200</strong>. Place your hand or any object in front of the IR sensor. You should see the message change from “No object” to “Object detected”.</p>
<p>Most IR obstacle sensors are <strong>active LOW</strong>. This means the output pin becomes LOW when the sensor detects an object. This is why the code checks:</p>
<pre><code class="language-cpp">if (sensorValue == LOW)
</code></pre>
<p>If your sensor behaves the opposite way, just reverse the condition.</p>
<h3><strong>Using an LED Indicator</strong></h3>
<p>The Serial Monitor is useful for testing, but sometimes it is better to see the sensor output using an LED. In this next example, we will turn on an LED connected to GPIO 26 whenever an object is detected.</p>
<p>Connect the LED as follows:</p>
<p><img data-dominant-color="d6d9d6" data-has-transparency="false" style="--dominant-color: #d6d9d6;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12191 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-led.avif" alt="ESP32 IR Proximity with LED indicator circuit" width="660" height="432" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-led.avif 660w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-led-300x196.avif 300w" sizes="auto, (max-width: 660px) 100vw, 660px" /></p>
<table>
<thead>
<tr>
<th>LED Part</th>
<th>ESP32 Connection</th>
</tr>
</thead>
<tbody>
<tr>
<td>LED anode (+)</td>
<td>GPIO 26 through a 220Ω resistor</td>
</tr>
<tr>
<td>LED cathode (-)</td>
<td>GND</td>
</tr>
</tbody>
</table>
<p>Here is the code:</p>
<pre><code class="language-cpp">#define IR_SENSOR_PIN 27
#define LED_PIN 26

void setup() {
  Serial.begin(115200);

  pinMode(IR_SENSOR_PIN, INPUT);
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  int sensorValue = digitalRead(IR_SENSOR_PIN);

  if (sensorValue == LOW) {
    Serial.println("Object detected");
    digitalWrite(LED_PIN, HIGH);
  } else {
    Serial.println("No object");
    digitalWrite(LED_PIN, LOW);
  }

  delay(300);
}
</code></pre>
<p>This is a simple setup, but it is already useful for testing object detection. You can replace the LED with a buzzer, relay module, or motor driver input depending on your project.</p>
<h3><strong>Adjusting the Detection Distance</strong></h3>
<p>Most IR proximity sensor modules have a small potentiometer. This potentiometer sets the reference voltage of the comparator circuit. In simpler terms, it adjusts how sensitive the sensor is.</p>
<p>Turn the potentiometer slowly while watching the Serial Monitor. If the sensor detects an object even when nothing is nearby, reduce the sensitivity. If it does not detect your hand or object, increase the sensitivity.</p>
<p>The detection distance depends on several things:</p>
<ul>
<li>color of the object</li>
<li>reflectiveness of the surface</li>
<li>angle of the object</li>
<li>ambient light</li>
<li>supply voltage</li>
<li>sensor module quality</li>
</ul>
<p>White or shiny objects are usually easier to detect because they reflect more infrared light. Black or matte objects may be harder to detect because they absorb more light.</p>
<h3><strong>Reading the Analog Output</strong></h3>
<p>Some IR proximity sensor modules include an <strong>AO</strong> pin. This pin gives an analog voltage that changes depending on the received IR light. If you want to read this value, connect the sensor like this:</p>
<p><img data-dominant-color="d8dad8" data-has-transparency="false" style="--dominant-color: #d8dad8;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12190 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-wiring-analog.avif" alt="ESP32 IR Proximity Wiring - Analog" width="660" height="432" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-wiring-analog.avif 660w, https://www.teachmemicro.com/wp-content/uploads/2026/07/esp32-ir-proximity-wiring-analog-300x196.avif 300w" sizes="auto, (max-width: 660px) 100vw, 660px" /></p>
<table>
<thead>
<tr>
<th>IR Sensor Pin</th>
<th>ESP32 Pin</th>
</tr>
</thead>
<tbody>
<tr>
<td>VCC</td>
<td>3V3</td>
</tr>
<tr>
<td>GND</td>
<td>GND</td>
</tr>
<tr>
<td>AO</td>
<td>GPIO 34</td>
</tr>
</tbody>
</table>
<p>GPIO 34 is a good choice for analog input because it is an input-only ADC pin.</p>
<p>Upload this code:</p>
<pre><code class="language-cpp">#define IR_ANALOG_PIN 34

void setup() {
  Serial.begin(115200);

  analogReadResolution(12); // ESP32 default: 0 to 4095
  analogSetPinAttenuation(IR_ANALOG_PIN, ADC_11db);
}

void loop() {
  int analogValue = analogRead(IR_ANALOG_PIN);

  Serial.print("IR analog value: ");
  Serial.println(analogValue);

  delay(300);
}
</code></pre>
<p>The ESP32 returns a value from <strong>0 to 4095</strong> by default when using 12-bit ADC resolution. A higher or lower value does not directly mean “distance in centimeters”. It only tells you that the received IR reflection changed.</p>
<p>If you need actual distance measurement, this sensor is not the best choice. Use it as a proximity or obstacle detector, not as an accurate distance sensor. For distance measurement, consider sensors such as the VL53L0X time-of-flight sensor or an ultrasonic sensor.</p>
<h3><strong>Simple ESP32 Web Server Example</strong></h3>
<p>Since we are using an ESP32, we can also display the sensor status on a web page. This is useful if you want to monitor the sensor wirelessly.</p>
<p>Replace the WiFi name and password with your own:</p>
<pre><code class="language-cpp">#include &lt;WiFi.h&gt;
#include &lt;WebServer.h&gt;

#define IR_SENSOR_PIN 27

const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";

WebServer server(80);

String getSensorStatus() {
  int sensorValue = digitalRead(IR_SENSOR_PIN);

  if (sensorValue == LOW) {
    return "Object detected";
  } else {
    return "No object";
  }
}

void handleRoot() {
  String html = "&lt;!DOCTYPE html&gt;&lt;html&gt;";
  html += "&lt;head&gt;";
  html += "&lt;meta name='viewport' content='width=device-width, initial-scale=1'&gt;";
  html += "&lt;meta http-equiv='refresh' content='1'&gt;";
  html += "&lt;style&gt;";
  html += "body{font-family:Arial;text-align:center;margin-top:50px;}";
  html += ".box{display:inline-block;padding:20px;border:1px solid #ccc;border-radius:8px;}";
  html += "&lt;/style&gt;";
  html += "&lt;/head&gt;";
  html += "&lt;body&gt;";
  html += "&lt;div class='box'&gt;";
  html += "&lt;h2&gt;ESP32 IR Proximity Sensor&lt;/h2&gt;";
  html += "&lt;h1&gt;" + getSensorStatus() + "&lt;/h1&gt;";
  html += "&lt;/div&gt;";
  html += "&lt;/body&gt;&lt;/html&gt;";

  server.send(200, "text/html", html);
}

void setup() {
  Serial.begin(115200);
  pinMode(IR_SENSOR_PIN, INPUT);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("ESP32 IP Address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.begin();
}

void loop() {
  server.handleClient();
}
</code></pre>
<p>After uploading the code, open the Serial Monitor. Once the ESP32 connects to your WiFi network, it will print its IP address. Open that IP address in a browser connected to the same network.</p>
<p>The page refreshes every second and shows whether the sensor detects an object or not.</p>
<h3><strong>How the IR Proximity Sensor Works</strong></h3>
<p>The IR proximity sensor uses an infrared LED to transmit invisible light. When an object is in front of the sensor, some of this infrared light bounces back and reaches the IR receiver.</p>
<p>The module then compares the receiver signal against a reference level set by the potentiometer. This comparison is usually done by an LM393 comparator IC. If the reflected IR signal is strong enough, the comparator changes the state of the OUT pin.</p>
<p>This is why the output is digital. The ESP32 does not need to know the exact light intensity. It only reads whether the comparator output is HIGH or LOW.</p>
<p>The limitation is that the sensor depends heavily on reflection. A white surface may be detected farther away than a black surface. A shiny surface may give a stronger response than a dull one. Sunlight or strong ambient light may also affect the sensor.</p>
<h3><strong>Common Problems and Fixes</strong></h3>
<h4><strong>The sensor always says “Object detected”</strong></h4>
<p>Adjust the potentiometer slowly. The sensitivity may be too high. Also check if the sensor is facing a nearby surface such as the table or breadboard.</p>
<h4><strong>The sensor never detects anything</strong></h4>
<p>Check the wiring first. Make sure VCC goes to 3V3, GND goes to GND, and OUT goes to GPIO 27. Also try adjusting the potentiometer in the opposite direction.</p>
<h4><strong>The output is reversed</strong></h4>
<p>Some modules may behave differently. If your sensor prints “Object detected” when there is no object, change the condition from:</p>
<pre><code class="language-cpp">if (sensorValue == LOW)
</code></pre>
<p>to:</p>
<pre><code class="language-cpp">if (sensorValue == HIGH)
</code></pre>
<h4><strong>The ESP32 restarts or behaves strangely</strong></h4>
<p>Do not power the sensor from 5V and connect its OUT pin directly to the ESP32. The ESP32 uses 3.3V logic. If your module must be powered from 5V, use a voltage divider or a logic level shifter on the OUT pin.</p>
<h4><strong>The analog value is not stable</strong></h4>
<p>This is normal for simple IR modules. The reading can change because of hand movement, object color, surface angle, and ambient light. Take several readings and average them if you need a smoother value.</p>
<h3><strong>Final Thoughts</strong></h3>
<p>Connecting an IR proximity sensor with the ESP32 is straightforward if you use the digital output pin. The important thing is to remember that the sensor module and the ESP32 do not always use the same logic voltage. For beginner projects, power the IR sensor from the ESP32’s 3.3V pin and connect the OUT pin to a safe GPIO like GPIO 27.</p>
<p>This sensor is best used for simple object detection. It is not a precision distance sensor, but it works well for robot obstacle detection, counters, touchless triggers, and other projects where you only need to know if something is nearby.</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-connect-ir-proximity-sensor-with-esp32/">How to Connect IR Proximity Sensor with ESP32</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ESP32-C3 Super Mini Getting Started Guide: Pinout, Arduino IDE Setup, and Built-In LED</title>
		<link>https://www.teachmemicro.com/esp32-c3-super-mini-getting-started-guide-pinout-arduino-ide-setup-and-built-in-led/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=esp32-c3-super-mini-getting-started-guide-pinout-arduino-ide-setup-and-built-in-led</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Fri, 26 Jun 2026 22:02:15 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=12039</guid>

					<description><![CDATA[<p>The ESP32-C3 Super Mini is one of those tiny development boards that looks almost too small to be useful at first. But once you plug it in and get past the first upload, it becomes a very handy board for compact Wi-Fi and BLE projects. It is smaller than the usual ESP32 DevKit board, it uses &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-c3-super-mini-getting-started-guide-pinout-arduino-ide-setup-and-built-in-led/">ESP32-C3 Super Mini Getting Started Guide: Pinout, Arduino IDE Setup, and Built-In LED</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The <strong>ESP32-C3 Super Mini</strong> is one of those tiny development boards that looks almost too small to be useful at first. But once you plug it in and get past the first upload, it becomes a very handy board for compact Wi-Fi and BLE projects. It is smaller than the usual ESP32 DevKit board, it uses USB-C on most versions, and it can run Arduino sketches without much trouble. The only catch is that the <strong>ESP32-C3 Super Mini pinout</strong> can be a little confusing because some pins have boot functions, some are used by the onboard LED, and some online pinout diagrams do not always agree with each other.</p>
<p>In this guide, I will walk you through the important parts: what the board is, which Arduino IDE board option to select, how to upload your first sketch, which pins are safe to use, and how to blink the <strong>ESP32-C3 Super Mini built-in LED pin</strong>.</p>
<p><span id="more-12039"></span></p>
<p>If your board was sold as a <strong>Core ESP32-C3 board for Arduino</strong>, this guide should still help. Most of those small boards use the same ESP32-C3 chip and work with the same Arduino core.</p>
<h2><strong>What Is the ESP32-C3 Super Mini?</strong></h2>
<p>The ESP32-C3 Super Mini is a small development board based on Espressif’s ESP32-C3 microcontroller. Unlike the older and more common <a href="https://www.teachmemicro.com/esp32-board-guide-for-beginners/">ESP32 boards</a>, the ESP32-C3 does not use the dual-core Xtensa processor. Instead, it uses a single-core 32-bit RISC-V processor. For most Arduino projects, this difference is not something you need to worry about right away. Your digital outputs, analog inputs, Wi-Fi sketches, web servers, and sensor projects still work in the same familiar Arduino style.</p>
<p>The board gives you:</p>
<div class="checklist tie-list-shortcode">
<ul>
<li>Wi-Fi</li>
<li>Bluetooth Low Energy</li>
<li>Digital input and output pins</li>
<li>PWM</li>
<li>Analog inputs</li>
<li>I2C</li>
<li>SPI</li>
<li>UART</li>
<li>Native USB programming</li>
<li>A very small footprint</li>
</ul>
</div>
<p>That combination makes it useful for compact IoT projects, sensor nodes, Wi-Fi buttons, BLE beacons, and small battery-powered experiments.</p>
<hr />
<h2><strong>Why Use the ESP32-C3 Super Mini?</strong></h2>
<p>The biggest advantage is size. A full-size ESP32 DevKit board is easier to handle, but it takes up more space on a breadboard or inside an enclosure. The ESP32-C3 Super Mini gives you the main ESP32 features in a much smaller board.</p>
<p>I would use this board when:</p>
<div class="checklist tie-list-shortcode">
<ul>
<li>The project needs Wi-Fi but space is limited.</li>
<li>I want a cheap board for a small sensor node.</li>
<li>I need BLE but not Bluetooth Classic.</li>
<li>I want something smaller than an ESP32 DevKit.</li>
<li>I want a board that can plug directly into USB-C.</li>
</ul>
</div>
<p>However, I would not use it as my very first ESP32 board if I had no experience at all. The pins are closer together, the labeling can vary between sellers, and some GPIO pins have special boot behavior. It is still beginner-friendly, but you need to pay attention to the pinout.</p>
<hr />
<h2><strong>ESP32-C3 Super Mini Specifications</strong></h2>
<p>Here are the typical specifications of an ESP32-C3 Super Mini board:</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Typical Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Microcontroller</td>
<td>ESP32-C3</td>
</tr>
<tr>
<td>CPU</td>
<td>32-bit RISC-V single-core</td>
</tr>
<tr>
<td>Clock Speed</td>
<td>Up to 160 MHz</td>
</tr>
<tr>
<td>Wireless</td>
<td>2.4 GHz Wi-Fi and Bluetooth LE</td>
</tr>
<tr>
<td>SRAM</td>
<td>400 KB</td>
</tr>
<tr>
<td>ROM</td>
<td>384 KB</td>
</tr>
<tr>
<td>Flash</td>
<td>Usually 4 MB</td>
</tr>
<tr>
<td>Logic Level</td>
<td>3.3V</td>
</tr>
<tr>
<td>USB</td>
<td>Native USB through USB-C</td>
</tr>
<tr>
<td>ADC Pins</td>
<td>GPIO0 to GPIO5</td>
</tr>
<tr>
<td>Built-in LED</td>
<td>Usually GPIO8</td>
</tr>
<tr>
<td>Buttons</td>
<td>BOOT and RESET</td>
</tr>
</tbody>
</table>
<p>The flash size is usually 4 MB, but it is still worth checking your specific board. Some online listings reuse the same photos for slightly different versions.</p>
<hr />
<h2><strong>ESP32-C3 Super Mini Board Layout</strong></h2>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-c3-super-mini-board-details.avif"><img data-dominant-color="c1c1c1" data-has-transparency="false" style="--dominant-color: #c1c1c1;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-12046 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-c3-super-mini-board-details.avif" alt="ESP32 C3 Super Mini board layout" width="590" height="667" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-c3-super-mini-board-details.avif 590w, https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-c3-super-mini-board-details-265x300.avif 265w" sizes="auto, (max-width: 590px) 100vw, 590px" /></a></p>
<p>Most ESP32-C3 Super Mini boards have a USB-C connector on one end and the antenna on the other end. You will usually find two small buttons on the board:</p>
<ul>
<li><strong>BOOT button</strong></li>
<li><strong>RESET button</strong></li>
</ul>
<p>The BOOT button is useful when the board does not automatically enter upload mode. The RESET button simply restarts the board. You may also see a small LED near the center or side of the board. On many ESP32-C3 Super Mini boards, this LED is connected to GPIO8. This is the LED we will test later. One important layout note: keep the antenna area clear. Avoid placing metal parts, large wires, batteries, or copper areas directly under the antenna side of the board. The board may still work, but Wi-Fi range can suffer.</p>
<hr />
<h2><strong>ESP32-C3 Super Mini Pinout</strong></h2>
<p>The exact ESP32-C3 Super Mini pinout can vary slightly depending on the manufacturer, but most boards expose a similar group of pins.</p>
<div align="center"><iframe loading="lazy" style="border: 1px solid #e2e8f0; border-radius: 8px;" src="https://micropinouts.com/embed/board/esp32-c3-super-mini" width="800" height="600" frameborder="0"></iframe></div>
<p>A common pinout looks like this:</p>
<table>
<thead>
<tr>
<th>Board Label</th>
<th align="right">GPIO</th>
<th>Common Use</th>
</tr>
</thead>
<tbody>
<tr>
<td>5V</td>
<td align="right">-</td>
<td>5V from USB or 5V input</td>
</tr>
<tr>
<td>GND</td>
<td align="right">-</td>
<td>Ground</td>
</tr>
<tr>
<td>3V3</td>
<td align="right">-</td>
<td>3.3V output</td>
</tr>
<tr>
<td>0</td>
<td align="right">GPIO0</td>
<td>Digital I/O, ADC, PWM</td>
</tr>
<tr>
<td>1</td>
<td align="right">GPIO1</td>
<td>Digital I/O, ADC, PWM</td>
</tr>
<tr>
<td>2</td>
<td align="right">GPIO2</td>
<td>Digital I/O, ADC, PWM, strapping pin</td>
</tr>
<tr>
<td>3</td>
<td align="right">GPIO3</td>
<td>Digital I/O, ADC, PWM</td>
</tr>
<tr>
<td>4</td>
<td align="right">GPIO4</td>
<td>Digital I/O, ADC, PWM</td>
</tr>
<tr>
<td>5</td>
<td align="right">GPIO5</td>
<td>Digital I/O, ADC, PWM</td>
</tr>
<tr>
<td>6</td>
<td align="right">GPIO6</td>
<td>Digital I/O, PWM</td>
</tr>
<tr>
<td>7</td>
<td align="right">GPIO7</td>
<td>Digital I/O, PWM</td>
</tr>
<tr>
<td>8</td>
<td align="right">GPIO8</td>
<td>Digital I/O, often built-in LED</td>
</tr>
<tr>
<td>9</td>
<td align="right">GPIO9</td>
<td>Digital I/O, BOOT-related pin</td>
</tr>
<tr>
<td>10</td>
<td align="right">GPIO10</td>
<td>Digital I/O, PWM</td>
</tr>
<tr>
<td>20</td>
<td align="right">GPIO20</td>
<td>UART RX / GPIO</td>
</tr>
<tr>
<td>21</td>
<td align="right">GPIO21</td>
<td>UART TX / GPIO</td>
</tr>
</tbody>
</table>
<p>Some boards also print SDA and SCL labels beside GPIO8 and GPIO9. That does not mean you must always use those pins for I2C. On the ESP32-C3, you can usually assign I2C to different GPIO pins in your code.</p>
<hr />
<h2><strong>Pins You Should Be Careful With</strong></h2>
<p>This is the part that many beginners miss. Some ESP32-C3 pins are <strong>strapping pins</strong>. The chip checks these pins while booting. If your external circuit pulls one of these pins to the wrong level during reset, the board may fail to boot or fail to upload.</p>
<p>Be careful with these pins:</p>
<table>
<thead>
<tr>
<th>Pin</th>
<th>Why It Needs Care</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPIO2</td>
<td>Strapping pin</td>
</tr>
<tr>
<td>GPIO8</td>
<td>Strapping pin and often built-in LED</td>
</tr>
<tr>
<td>GPIO9</td>
<td>Strapping pin and usually connected to BOOT</td>
</tr>
<tr>
<td>GPIO18 / GPIO19</td>
<td>Used for native USB on many ESP32-C3 boards</td>
</tr>
</tbody>
</table>
<p>This does not mean you can never use these pins. You can. But for your first projects, it is better to start with less troublesome pins.</p>
<hr />
<h2><strong>Best GPIO Pins for Beginner Projects</strong></h2>
<p>For simple LEDs, buttons, sensors, and digital outputs, I would start with:</p>
<div class="checklist tie-list-shortcode">
<ul>
<li>GPIO0</li>
<li>GPIO1</li>
<li>GPIO3</li>
<li>GPIO4</li>
<li>GPIO5</li>
<li>GPIO6</li>
<li>GPIO7</li>
<li>GPIO10</li>
</ul>
</div>
<p>For analog input, use:</p>
<div class="checklist tie-list-shortcode">
<ul>
<li>GPIO0</li>
<li>GPIO1</li>
<li>GPIO2</li>
<li>GPIO3</li>
<li>GPIO4</li>
<li>GPIO5</li>
</ul>
</div>
<p>For UART, many boards use:</p>
<ul>
<li>GPIO20 = RX</li>
<li>GPIO21 = TX</li>
</ul>
<p>For the built-in LED, try: GPIO8</p>
<p>If GPIO8 does not work on your board, try GPIO10. Some clone boards use a different LED pin.</p>
<hr />
<h2><strong>Powering the ESP32-C3 Super Mini</strong></h2>
<p>The easiest way to power the board is through the USB-C connector.</p>
<p>You may also see <strong>5V</strong>, <strong>3V3</strong>, and <strong>GND</strong> pins on the board.</p>
<p>Here is what they mean:</p>
<ul>
<li><strong>5V</strong> is usually connected to the USB 5V line.</li>
<li><strong>3V3</strong> is the regulated 3.3V supply.</li>
<li><strong>GND</strong> is ground.</li>
</ul>
<p>The ESP32-C3 itself is a <strong>3.3V device</strong>. Its GPIO pins are not 5V tolerant. Do not connect a 5V signal directly to any GPIO pin. This is especially important when using modules like relays, ultrasonic sensors, or some older 5V Arduino modules. If a module outputs 5V, use a level shifter or voltage divider before connecting it to the ESP32-C3.</p>
<hr />
<h2><strong>Installing ESP32-C3 Support in Arduino IDE</strong></h2>
<p>To program the ESP32-C3 Super Mini with Arduino IDE, you need to install the ESP32 board package.</p>
<p>Open Arduino IDE, then go to:</p>
<p><em>File &gt; Preferences</em></p>
<p>Find <strong>Additional Boards Manager URLs</strong> and add this URL:</p>
<p><em>https://espressif.github.io/arduino-esp32/package_esp32_index.json</em></p>
<p>Then go to:</p>
<p><em>Tools &gt; Board &gt; Boards Manager</em></p>
<p>Search for: <em>esp32</em></p>
<p>Then, install the package named: <em>esp32 by Espressif Systems</em></p>
<p>After installation, restart Arduino IDE.</p>
<hr />
<h2><strong>Which Board Should You Select?</strong></h2>
<p>For most ESP32-C3 Super Mini boards, select:</p>
<p><em>Tools &gt; Board &gt; esp32 &gt; ESP32C3 Dev Module</em></p>
<p>This is the board option I would try first if your board is listed online as an ESP32-C3 Super Mini or Core ESP32-C3 board for Arduino.</p>
<p>Use these settings as a starting point:</p>
<table>
<thead>
<tr>
<th>Arduino IDE Setting</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Board</td>
<td>ESP32C3 Dev Module</td>
</tr>
<tr>
<td>USB CDC On Boot</td>
<td>Enabled</td>
</tr>
<tr>
<td>CPU Frequency</td>
<td>160 MHz</td>
</tr>
<tr>
<td>Flash Size</td>
<td>4MB</td>
</tr>
<tr>
<td>Upload Speed</td>
<td>921600</td>
</tr>
<tr>
<td>Port</td>
<td>Select the detected USB port</td>
</tr>
</tbody>
</table>
<p>The most important setting here is <strong>USB CDC On Boot</strong>. If this is disabled, your sketch may upload, but the Serial Monitor may not show anything through USB. If upload fails at 921600, lower the upload speed to 460800 or 115200.</p>
<hr />
<h2><strong>First Sketch: Check If the Board Is Working</strong></h2>
<p>Before connecting sensors or LEDs, upload a simple serial test sketch.</p>
<pre><code class="language-cpp">void setup() {
  Serial.begin(115200);
  delay(2000);

  Serial.println();
  Serial.println("ESP32-C3 Super Mini test");

  Serial.print("Chip model: ");
  Serial.println(ESP.getChipModel());

  Serial.print("Chip revision: ");
  Serial.println(ESP.getChipRevision());

  Serial.print("CPU frequency: ");
  Serial.print(ESP.getCpuFreqMHz());
  Serial.println(" MHz");

  Serial.print("Flash size: ");
  Serial.print(ESP.getFlashChipSize() / (1024 * 1024));
  Serial.println(" MB");
}

void loop() {
  Serial.println("Board is running...");
  delay(1000);
}
</code></pre>
<p>Upload the sketch, then open the Serial Monitor at: <em>115200 baud</em>. If you see the board information and the “Board is running...” message, your Arduino IDE setup is working. If you see nothing, press RESET once. Also check that <strong>USB CDC On Boot</strong> is enabled.</p>
<hr />
<h2><strong>If Upload Does Not Work</strong></h2>
<p>Some ESP32-C3 Super Mini boards enter upload mode automatically. Others are a little stubborn.</p>
<p>If Arduino IDE gets stuck at “Connecting...”, try this:</p>
<ol>
<li>Click Upload.</li>
<li>When you see “Connecting...”, hold the BOOT button.</li>
<li>Release BOOT once the upload starts.</li>
</ol>
<p>If that still does not work, hold BOOT, tap RESET, then try uploading again. Also check the USB cable. A surprising number of upload problems are caused by charge-only USB-C cables.</p>
<hr />
<h2><strong>ESP32-C3 Super Mini Built-In LED Pin</strong></h2>
<p>On many ESP32-C3 Super Mini boards, the built-in LED is connected to <strong>GPIO8</strong>. This LED is usually <strong>active LOW</strong>. That means the logic feels backward:</p>
<p>LOW = LED ON</p>
<p>HIGH = LED OFF</p>
<p>Upload this sketch to test the onboard LED:</p>
<pre><code class="language-cpp">#define LED_PIN 8

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_PIN, LOW);   // Turn LED ON
  delay(500);

  digitalWrite(LED_PIN, HIGH);  // Turn LED OFF
  delay(500);
}
</code></pre>
<p>If the onboard LED blinks, your board uses GPIO8 for the built-in LED. If nothing happens, try GPIO10:</p>
<pre><code class="language-cpp">#define LED_PIN 10
</code></pre>
<p>Some boards only have a power LED. If the LED turns on whenever the board is powered and never responds to your sketch, then it may not be connected to a GPIO pin.</p>
<hr />
<h2><strong>Why the Built-In LED Pin Can Be Annoying</strong></h2>
<p>GPIO8 is convenient because it is often connected to the onboard LED. However, GPIO8 is also a strapping pin. For a simple blink test, this is not a big problem. But if you connect an external circuit to GPIO8, especially a circuit with a pull-up or pull-down resistor, it may affect the way the ESP32-C3 boots.</p>
<p>So here is the practical rule: <em>Use GPIO8 for the onboard LED test. For external LEDs, buttons, and sensors, use another GPIO unless you have a reason to use GPIO8.</em></p>
<hr />
<h2><strong>Testing Wi-Fi on the ESP32-C3 Super Mini</strong></h2>
<p>Once the board can upload sketches, the next useful test is Wi-Fi. The ESP32-C3 supports 2.4 GHz Wi-Fi. It does not connect to 5 GHz-only networks.</p>
<p>Upload this Wi-Fi scanner:</p>
<pre><code class="language-cpp">#include &lt;WiFi.h&gt;

void setup() {
  Serial.begin(115200);
  delay(2000);

  Serial.println();
  Serial.println("ESP32-C3 Wi-Fi scan");

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  delay(100);

  int n = WiFi.scanNetworks();

  if (n == 0) {
    Serial.println("No networks found.");
  } else {
    Serial.print("Networks found: ");
    Serial.println(n);

    for (int i = 0; i &lt; n; i++) {
      Serial.print(i + 1);
      Serial.print(": ");
      Serial.print(WiFi.SSID(i));
      Serial.print("  RSSI: ");
      Serial.print(WiFi.RSSI(i));
      Serial.println(" dBm");
      delay(10);
    }
  }
}

void loop() {
}
</code></pre>
<p>Open the Serial Monitor at 115200 baud. You should see nearby Wi-Fi networks. If the scan works, your Wi-Fi hardware is fine.</p>
<hr />
<h2><strong>Connecting the ESP32-C3 Super Mini to Wi-Fi</strong></h2>
<p>Now try connecting to your router.</p>
<pre><code class="language-cpp">#include &lt;WiFi.h&gt;

const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("Wi-Fi connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
}
</code></pre>
<p>Replace the Wi-Fi name and password with your own network credentials. If the board keeps failing to connect, check that your network is 2.4 GHz. Many routers combine 2.4 GHz and 5 GHz under one network name, which usually works, but a 5 GHz-only network will not.</p>
<hr />
<h2><strong>Simple Web Server LED Control</strong></h2>
<p>Here is a small web server example that controls the built-in LED from a browser. This sketch assumes the onboard LED is on GPIO8 and active LOW.</p>
<pre><code class="language-cpp">#include &lt;WiFi.h&gt;
#include &lt;WebServer.h&gt;

const char* ssid = "YOUR_WIFI_NAME";
const char* password = "YOUR_WIFI_PASSWORD";

#define LED_PIN 8

WebServer server(80);

bool ledOn = false;

void handleRoot() {
  String html = "&lt;!DOCTYPE html&gt;&lt;html&gt;";
  html += "&lt;head&gt;";
  html += "&lt;meta name='viewport' content='width=device-width, initial-scale=1'&gt;";
  html += "&lt;style&gt;";
  html += "body{font-family:Arial;text-align:center;margin-top:40px;}";
  html += "button{font-size:24px;padding:12px 24px;margin:10px;}";
  html += "&lt;/style&gt;";
  html += "&lt;/head&gt;";

  html += "&lt;body&gt;";
  html += "&lt;h1&gt;ESP32-C3 Super Mini LED&lt;/h1&gt;";
  html += "&lt;p&gt;LED is ";
  html += ledOn ? "ON" : "OFF";
  html += "&lt;/p&gt;";
  html += "&lt;p&gt;&lt;a href='/on'&gt;&lt;button&gt;ON&lt;/button&gt;&lt;/a&gt;&lt;/p&gt;";
  html += "&lt;p&gt;&lt;a href='/off'&gt;&lt;button&gt;OFF&lt;/button&gt;&lt;/a&gt;&lt;/p&gt;";
  html += "&lt;/body&gt;&lt;/html&gt;";

  server.send(200, "text/html", html);
}

void handleOn() {
  ledOn = true;
  digitalWrite(LED_PIN, LOW);   // active-low LED ON
  handleRoot();
}

void handleOff() {
  ledOn = false;
  digitalWrite(LED_PIN, HIGH);  // active-low LED OFF
  handleRoot();
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);  // start with LED OFF

  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("Wi-Fi connected!");
  Serial.print("Open this address in your browser: http://");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/on", handleOn);
  server.on("/off", handleOff);

  server.begin();
}

void loop() {
  server.handleClient();
}
</code></pre>
<p>After uploading, open the Serial Monitor and copy the IP address. Open that address in a browser connected to the same Wi-Fi network. This is a good first “real” ESP32-C3 project because it tests USB, serial output, Wi-Fi, GPIO, and a simple web page.</p>
<hr />
<h2><strong>Using I2C on the ESP32-C3 Super Mini</strong></h2>
<p>Many ESP32-C3 Super Mini pinout diagrams show GPIO8 as SDA and GPIO9 as SCL. That can work, but I do not always recommend it for beginners because those pins have special boot behavior. A safer approach is to define the I2C pins yourself.</p>
<p>For example, you can try GPIO6 and GPIO7:</p>
<pre><code class="language-cpp">#include &lt;Wire.h&gt;

#define SDA_PIN 6
#define SCL_PIN 7

void setup() {
  Serial.begin(115200);
  delay(1000);

  Wire.begin(SDA_PIN, SCL_PIN);

  Serial.println("Scanning I2C bus...");

  for (byte address = 1; address &lt; 127; address++) {
    Wire.beginTransmission(address);
    byte error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at 0x");
      if (address &lt; 16) Serial.print("0");
      Serial.println(address, HEX);
    }
  }

  Serial.println("Scan complete.");
}

void loop() {
}
</code></pre>
<p>If your OLED display or sensor is already wired to the pins labeled SDA and SCL on your board, change the pin definitions to match those labels.</p>
<p>For example:</p>
<pre><code class="language-cpp">#define SDA_PIN 8
#define SCL_PIN 9
</code></pre>
<p>Just remember that GPIO8 and GPIO9 are not the safest pins for every circuit.</p>
<hr />
<h2><strong>Reading an Analog Sensor</strong></h2>
<p>The ESP32-C3 has analog input pins on GPIO0 to GPIO5. Here is a simple potentiometer test using GPIO0:</p>
<pre><code class="language-cpp">#define POT_PIN 0

void setup() {
  Serial.begin(115200);
}

void loop() {
  int value = analogRead(POT_PIN);

  Serial.print("ADC value: ");
  Serial.println(value);

  delay(300);
}
</code></pre>
<p>Wire the potentiometer like this:</p>
<table>
<thead>
<tr>
<th>Potentiometer Pin</th>
<th>ESP32-C3 Super Mini</th>
</tr>
</thead>
<tbody>
<tr>
<td>One outer pin</td>
<td>3V3</td>
</tr>
<tr>
<td>Other outer pin</td>
<td>GND</td>
</tr>
<tr>
<td>Middle pin</td>
<td>GPIO0</td>
</tr>
</tbody>
</table>
<p>Do not connect the potentiometer to 5V. The analog input pin should only receive a voltage between 0V and 3.3V.</p>
<hr />
<h2><strong>Using UART on GPIO20 and GPIO21</strong></h2>
<p>Many ESP32-C3 Super Mini boards expose GPIO20 and GPIO21 as RX and TX. Typical wiring looks like this:</p>
<table>
<thead>
<tr>
<th>ESP32-C3 Super Mini</th>
<th>External Serial Device</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPIO20 RX</td>
<td>TX</td>
</tr>
<tr>
<td>GPIO21 TX</td>
<td>RX</td>
</tr>
<tr>
<td>GND</td>
<td>GND</td>
</tr>
</tbody>
</table>
<p>Here is a simple UART bridge sketch:</p>
<pre><code class="language-cpp">HardwareSerial MySerial(1);

#define RX_PIN 20
#define TX_PIN 21

void setup() {
  Serial.begin(115200);
  MySerial.begin(9600, SERIAL_8N1, RX_PIN, TX_PIN);

  Serial.println("UART test started");
}

void loop() {
  if (MySerial.available()) {
    Serial.write(MySerial.read());
  }

  if (Serial.available()) {
    MySerial.write(Serial.read());
  }
}
</code></pre>
<p>This is useful when testing GPS modules, serial sensors, or other microcontrollers. Again, check voltage levels. The ESP32-C3 uses 3.3V logic.</p>
<hr />
<h2><strong>Common Problems and Fixes</strong></h2>
<h3><strong>The Board Does Not Show Up in Arduino IDE</strong></h3>
<p>Try another USB cable first. Make sure it is a data cable, not just a charging cable.</p>
<p>Then try:</p>
<ul>
<li>Another USB port</li>
<li>Pressing RESET</li>
<li>Holding BOOT while plugging in the board</li>
<li>Restarting Arduino IDE</li>
<li>Checking whether the port appears in Device Manager or your operating system’s serial device list</li>
</ul>
<p>Most ESP32-C3 Super Mini boards use native USB, so they usually do not need a separate CH340 or CP210x driver. But clone boards can vary.</p>
<h3><strong>Upload Gets Stuck at “Connecting...”</strong></h3>
<p>Put the board into bootloader mode manually:</p>
<ol>
<li>Hold BOOT.</li>
<li>Click Upload.</li>
<li>Release BOOT when upload starts.</li>
</ol>
<p>If needed, hold BOOT, tap RESET, release RESET, then release BOOT.</p>
<h3><strong>Serial Monitor Is Blank</strong></h3>
<p>Check these settings:</p>
<ul>
<li>Serial Monitor baud rate is 115200.</li>
<li>USB CDC On Boot is enabled.</li>
<li>Correct port is selected.</li>
<li>The sketch has <pre><code class="language-cpp">Serial.begin(115200);</code></pre>.</li>
<li>You added a short delay after <pre><code class="language-cpp">Serial.begin()</code></pre>.</li>
</ul>
<p>This is one of the most common ESP32-C3 beginner issues.</p>
<h3><strong>The Built-In LED Does Not Blink</strong></h3>
<p>Try GPIO8 first, then GPIO10.</p>
<p>Also remember that many ESP32-C3 Super Mini boards use active-low LED wiring. So this turns the LED on:</p>
<pre><code class="language-cpp">digitalWrite(LED_PIN, LOW);
</code></pre>
<p>And this turns it off:</p>
<pre><code class="language-cpp">digitalWrite(LED_PIN, HIGH);
</code></pre>
<p>If the LED never responds, it may only be a power LED.</p>
<h3><strong>The Board Resets When Wi-Fi Starts</strong></h3>
<p>Wi-Fi needs short bursts of current. If the USB cable or port is weak, the board may reset when Wi-Fi turns on.</p>
<p>Try:</p>
<ul>
<li>A better USB cable</li>
<li>A different USB port</li>
<li>A powered USB hub</li>
<li>Removing external loads from the 3.3V pin</li>
<li>Powering external modules separately</li>
</ul>
<p>Always connect grounds together when using an external power supply for sensors or modules.</p>
<hr />
<h2><strong>Recommended First Projects</strong></h2>
<p>Once the board is working, these are good projects to try:</p>
<ul>
<li>Built-in LED blink</li>
<li>Wi-Fi scanner</li>
<li>Web server LED control</li>
<li>OLED display using I2C</li>
<li>BME280 weather monitor</li>
<li>DHT22 temperature and humidity logger</li>
<li>MQTT sensor node</li>
<li>BLE beacon</li>
<li>Deep sleep battery monitor</li>
<li>UART GPS reader</li>
</ul>
<p>The ESP32-C3 Super Mini is especially good for small connected projects where a full-size ESP32 board feels too large.</p>
<hr />
<h2><strong>Frequently Asked Questions</strong></h2>
<h3><strong>What board should I select for ESP32-C3 Super Mini in Arduino IDE?</strong></h3>
<p>Select <strong>ESP32C3 Dev Module</strong>. This is the best starting option for most generic ESP32-C3 Super Mini boards.</p>
<h3><strong>Is the ESP32-C3 Super Mini compatible with Arduino?</strong></h3>
<p>Yes. Install the ESP32 board package by Espressif, then select <strong>ESP32C3 Dev Module</strong> in Arduino IDE.</p>
<h3><strong>What is the ESP32-C3 Super Mini built-in LED pin?</strong></h3>
<p>On many boards, the built-in LED is connected to <strong>GPIO8</strong>. It is usually active LOW, so writing <em>LOW</em> turns the LED on.</p>
<h3><strong>Why does my built-in LED not blink?</strong></h3>
<p>Your board may use a different LED pin, or the LED may only be a power indicator. Try GPIO8 first, then GPIO10.</p>
<h3><strong>Can I use GPIO8 and GPIO9?</strong></h3>
<p>Yes, but be careful. GPIO8 and GPIO9 are strapping pins. GPIO8 is often connected to the built-in LED, while GPIO9 is usually related to the BOOT button.</p>
<h3><strong>Can I connect 5V sensors to the ESP32-C3 Super Mini?</strong></h3>
<p>Only if the sensor output is 3.3V-compatible. The ESP32-C3 GPIO pins are not 5V tolerant. Use a level shifter when needed.</p>
<h3><strong>Does the ESP32-C3 support Bluetooth Classic?</strong></h3>
<p>No. The ESP32-C3 supports Bluetooth Low Energy, not Bluetooth Classic.</p>
<h3><strong>Can the ESP32-C3 Super Mini run MicroPython?</strong></h3>
<p>Yes. The board can run Arduino sketches, MicroPython, ESP-IDF projects, and PlatformIO projects. This guide focuses on Arduino IDE.</p>
<hr />
<h2><strong>Conclusion</strong></h2>
<p>The ESP32-C3 Super Mini is a small but capable board for Wi-Fi and Bluetooth Low Energy projects. It is not as forgiving as a large ESP32 DevKit board because the pins are closer together and some GPIOs have boot functions, but it is still a very useful board once you understand the basics.</p>
<p>For a smooth start, remember these points:</p>
<ul>
<li>Select <strong>ESP32C3 Dev Module</strong> in Arduino IDE.</li>
<li>Enable <strong>USB CDC On Boot</strong>.</li>
<li>Use GPIO8 for the built-in LED on most boards.</li>
<li>Remember that the built-in LED is usually active LOW.</li>
<li>Be careful with GPIO2, GPIO8, and GPIO9.</li>
<li>Do not connect 5V signals directly to any GPIO pin.</li>
</ul>
<p>After you get the first blink and Wi-Fi test working, the ESP32-C3 Super Mini becomes a great board for compact IoT projects, web-controlled devices, and small wireless sensor builds.</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-c3-super-mini-getting-started-guide-pinout-arduino-ide-setup-and-built-in-led/">ESP32-C3 Super Mini Getting Started Guide: Pinout, Arduino IDE Setup, and Built-In LED</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Run an SSH Server on ESP32</title>
		<link>https://www.teachmemicro.com/how-to-run-an-ssh-server-on-esp32/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-run-an-ssh-server-on-esp32</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Wed, 24 Jun 2026 01:35:46 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=11922</guid>

					<description><![CDATA[<p>An SSH server on ESP32 lets you log in to your microcontroller over Wi-Fi using a secure terminal connection. Instead of opening the Arduino Serial Monitor through USB, you can connect from a computer, phone, or another device on the same network and send commands remotely. In this tutorial, you will learn how to set &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-run-an-ssh-server-on-esp32/">How to Run an SSH Server on ESP32</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>An SSH server on ESP32 lets you log in to your microcontroller over Wi-Fi using a secure terminal connection. Instead of opening the Arduino Serial Monitor through USB, you can connect from a computer, phone, or another device on the same network and send commands remotely.</p>
<p>In this tutorial, you will learn how to set up an SSH server on ESP32 using the Arduino IDE. We will use the LibSSH-ESP32 library, generate the required SSH host key, connect the ESP32 to Wi-Fi, and log in from a terminal.</p>
<p>This project is useful if you want a safer remote command interface than plain Telnet. However, you should still treat it as a local-network tool, not as something to expose directly to the public internet.</p>
<p><span id="more-11922"></span></p>
<h2><strong>What Is an SSH Server?</strong></h2>
<p>SSH means <em><strong>Secure Shell</strong></em>. It is a protocol used to open an encrypted command-line session between two devices.</p>
<p>In a normal SSH setup, a computer connects to a server like this:</p>
<pre><code class="language-bash">ssh username@server_ip_address
</code></pre>
<p>For this project, the ESP32 becomes the SSH server. Your laptop, desktop PC, or phone becomes the SSH client.</p>
<p>Once connected, you can type commands into the SSH terminal. The ESP32 receives those commands and can respond with text, sensor readings, status messages, or control actions.</p>
<h2><strong>Why Use SSH on ESP32?</strong></h2>
<p>Most ESP32 projects use one of these interfaces:</p>
<ul>
<li>Serial Monitor through USB</li>
<li>Web server page</li>
<li>REST API</li>
<li>MQTT</li>
<li>Bluetooth serial</li>
<li>Telnet-style TCP server</li>
</ul>
<p>An SSH server is different because it provides an encrypted terminal session. That makes it useful for projects where you want remote diagnostics or command access without sending plain text over the network.</p>
<p>For example, an SSH server on ESP32 can be used for:</p>
<ul>
<li>Remote debugging</li>
<li>Reading sensor values from a terminal</li>
<li>Restarting parts of a program</li>
<li>Viewing device status</li>
<li>Controlling GPIO pins</li>
<li>Sending simple maintenance commands</li>
<li>Building a secure command console for an IoT device</li>
</ul>
<p>However, SSH also uses more memory and processing power than a simple TCP server. For small projects, a web server or serial command interface may still be easier.</p>
<h2><strong>Parts Required</strong></h2>
<p>For this tutorial, you need:</p>
<ul>
<li>ESP32 development board</li>
<li>USB cable</li>
<li>Arduino IDE</li>
<li>Wi-Fi network</li>
<li>Computer with an SSH client</li>
</ul>
<p>Most Linux and macOS systems already include the <em>ssh</em> command. On Windows 10 and Windows 11, OpenSSH is usually available from PowerShell or Command Prompt.</p>
<p>You can check by opening a terminal and typing:</p>
<pre><code class="language-bash">ssh
</code></pre>
<p>If you see SSH usage information, your computer already has an SSH client.</p>
<h2><strong>Installing the ESP32 Board Package</strong></h2>
<p>Before using the ESP32, install the ESP32 board package in the Arduino IDE.</p>
<p>Open Arduino IDE, then go to:</p>
<pre><code class="language-text">File &gt; Preferences
</code></pre>
<p>Add this URL to the Additional Boards Manager URLs field:</p>
<pre><code class="language-text">https://espressif.github.io/arduino-esp32/package_esp32_index.json
</code></pre>
<p>Then go to:</p>
<pre><code class="language-text">Tools &gt; Board &gt; Boards Manager
</code></pre>
<p>Search for:</p>
<pre><code class="language-text">esp32
</code></pre>
<p>Install the package by Espressif Systems.</p>
<p>After installation, select your ESP32 board from:</p>
<pre><code class="language-text">Tools &gt; Board &gt; esp32
</code></pre>
<p>For most ESP32-WROOM development boards, select:</p>
<pre><code class="language-text">ESP32 Dev Module
</code></pre>
<h2><strong>Installing the LibSSH-ESP32 Library</strong></h2>
<p>Next, install the SSH library.</p>
<p>In Arduino IDE, go to:</p>
<pre><code class="language-text">Sketch &gt; Include Library &gt; Manage Libraries
</code></pre>
<p>Search for:</p>
<pre><code class="language-text">LibSSH-ESP32
</code></pre>
<p>Install the library.</p>
<p>This library is a port of libssh for ESP32. It includes examples for SSH server, SSH client, SCP, key generation, and OTA-style file transfer.</p>
<h2><strong>How the ESP32 SSH Server Works</strong></h2>
<p>The ESP32 SSH server needs three main parts:</p>
<ol>
<li>Wi-Fi connection</li>
<li>SSH host key</li>
<li>SSH server task</li>
</ol>
<p>The Wi-Fi connection allows the ESP32 to join your local network.</p>
<p>The SSH host key identifies the ESP32 to the SSH client. This is different from the username and password. The host key lets your computer recognize the ESP32 if you connect again later.</p>
<p>The SSH server task listens for incoming SSH connections, handles authentication, and opens a terminal channel.</p>
<h2><strong>Generate the SSH Host Key</strong></h2>
<p>Before starting the SSH server, the ESP32 needs a host key. The LibSSH-ESP32 library includes a key generation example.</p>
<p>Open:</p>
<pre><code class="language-text">File &gt; Examples &gt; LibSSH-ESP32 &gt; keygen2
</code></pre>
<p>Upload the sketch to your ESP32.</p>
<p>Open the Serial Monitor and set the baud rate to:</p>
<pre><code class="language-text">115200
</code></pre>
<p>The sketch will mount SPIFFS and generate an SSH key. If the Serial Monitor asks whether to format SPIFFS, type:</p>
<pre><code class="language-text">y
</code></pre>
<p>Then press Enter.</p>
<p>Formatting SPIFFS erases files stored in the ESP32 flash file system, so do this only on a test board or when you do not need the existing SPIFFS files.</p>
<p>The key generation sketch usually creates a key file inside the ESP32 file system. The SSH server example will use this file as the host key.</p>
<h2><strong>Upload the SSH Server Example</strong></h2>
<p>After generating the key, open the SSH server example:</p>
<pre><code class="language-text">File &gt; Examples &gt; LibSSH-ESP32 &gt; samplesshd-kbdint
</code></pre>
<p>Find the Wi-Fi credentials near the top of the sketch:</p>
<pre><code class="language-cpp">const char *configSTASSID = "YourWiFiSSID";
const char *configSTAPSK = "YourWiFiPSK";
</code></pre>
<p>Change them to match your Wi-Fi network:</p>
<pre><code class="language-cpp">const char *configSTASSID = "YourNetworkName";
const char *configSTAPSK = "YourNetworkPassword";
</code></pre>
<p>Upload the sketch to your ESP32.</p>
<p>Open the Serial Monitor at 115200 baud. After the ESP32 connects to Wi-Fi, it should print its IP address.</p>
<p>For example:</p>
<pre><code class="language-text">% WiFi connected
% IPv4 Address: 192.168.1.45
Started sample libssh sshd on port 22
</code></pre>
<p>Write down the IP address because you will use it to connect through SSH.</p>
<h2><strong>Logging In to the ESP32 with SSH</strong></h2>
<p>Open a terminal on your computer.</p>
<p>Use this command:</p>
<pre><code class="language-bash">ssh libssh@192.168.1.45
</code></pre>
<p>Replace <pre><code class="language-cpp">192.168.1.45</code></pre> with the IP address printed by your ESP32.</p>
<p>When asked for a password, enter:</p>
<pre><code class="language-text">libssh
</code></pre>
<p>The default example username and password are:</p>
<pre><code class="language-text">Username: libssh
Password: libssh
</code></pre>
<p>If this is your first time connecting to the ESP32, your computer may show a message like this:</p>
<pre><code class="language-text">The authenticity of host '192.168.1.45' can't be established.
Are you sure you want to continue connecting?
</code></pre>
<p>Type:</p>
<pre><code class="language-text">yes
</code></pre>
<p>Then press Enter.</p>
<p>You should now have an SSH session connected to the ESP32.</p>
<h2><strong>Testing the SSH Connection</strong></h2>
<p>The sample SSH server behaves like a simple echo terminal. When you type text, the ESP32 sends the text back through the encrypted SSH channel.</p>
<p>Try typing:</p>
<pre><code class="language-text">hello esp32
</code></pre>
<p>The ESP32 should echo the text back.</p>
<p>This proves that:</p>
<ul>
<li>The ESP32 joined your Wi-Fi network</li>
<li>The SSH server started correctly</li>
<li>Your computer connected to the ESP32 through SSH</li>
<li>Authentication worked</li>
<li>The SSH channel is working</li>
</ul>
<h2><strong>Turning the Example into a Command Server</strong></h2>
<p>The default SSH server example is only a starting point. For a practical project, you can modify the part of the code that reads incoming characters from the SSH channel.</p>
<p>A simple command interface can support commands like:</p>
<pre><code class="language-text">status
led on
led off
restart
</code></pre>
<p>For example, the ESP32 can check the received text and respond with a message.</p>
<p>Conceptually, the command handler can work like this:</p>
<pre><code class="language-cpp">String handleCommand(String cmd) {
  cmd.trim();

  if (cmd == "status") {
    return "ESP32 is running\r\n";
  }

  if (cmd == "led on") {
    digitalWrite(2, HIGH);
    return "LED turned ON\r\n";
  }

  if (cmd == "led off") {
    digitalWrite(2, LOW);
    return "LED turned OFF\r\n";
  }

  if (cmd == "help") {
    return "Commands: status, led on, led off, help\r\n";
  }

  return "Unknown command. Type help.\r\n";
}
</code></pre>
<p>Then, when a complete line arrives from the SSH channel, pass it to <pre><code class="language-cpp">handleCommand()</code></pre> and write the response back to the SSH client.</p>
<p>This approach turns the ESP32 SSH server into a secure remote console.</p>
<h2><strong>Example Command Ideas</strong></h2>
<p>Here are some useful commands you can add to your ESP32 SSH server:</p>
<table>
<thead>
<tr>
<th>Command</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td>status</td>
<td>Print uptime, free heap, and Wi-Fi RSSI</td>
</tr>
<tr>
<td>ip</td>
<td>Print the ESP32 IP address</td>
</tr>
<tr>
<td>led on</td>
<td>Turn on an LED</td>
</tr>
<tr>
<td>led off</td>
<td>Turn off an LED</td>
</tr>
<tr>
<td>read adc</td>
<td>Print an analog reading</td>
</tr>
<tr>
<td>read temp</td>
<td>Print a sensor value</td>
</tr>
<tr>
<td>restart</td>
<td>Restart the ESP32</td>
</tr>
<tr>
<td>help</td>
<td>Show available commands</td>
</tr>
</tbody>
</table>
<p>For an IoT project, the <em>status</em> command is one of the most useful commands to add first.</p>
<p>Example response:</p>
<pre><code class="language-text">Uptime: 923 seconds
Free heap: 183420 bytes
WiFi RSSI: -54 dBm
IP: 192.168.1.45
</code></pre>
<h2><strong>Adding a Status Command</strong></h2>
<p>You can use standard Arduino and ESP32 functions to get basic system information.</p>
<p>For example:</p>
<pre><code class="language-cpp">String getStatus() {
  String msg;

  msg += "ESP32 Status\r\n";
  msg += "Uptime: ";
  msg += String(millis() / 1000);
  msg += " seconds\r\n";

  msg += "Free heap: ";
  msg += String(ESP.getFreeHeap());
  msg += " bytes\r\n";

  msg += "WiFi RSSI: ";
  msg += String(WiFi.RSSI());
  msg += " dBm\r\n";

  msg += "IP address: ";
  msg += WiFi.localIP().toString();
  msg += "\r\n";

  return msg;
}
</code></pre>
<p>Then your command handler can call this function:</p>
<pre><code class="language-cpp">if (cmd == "status") {
  return getStatus();
}
</code></pre>
<p>Now, when you connect through SSH and type:</p>
<pre><code class="language-text">status
</code></pre>
<p>The ESP32 can return live system information.</p>
<h2><strong>Changing the Default Username and Password</strong></h2>
<p>Do not keep the default login credentials in your final project.</p>
<p>Look for the username and password definitions in the SSH server example. They usually look similar to this:</p>
<pre><code class="language-cpp">#define SSHD_USER "libssh"
#define SSHD_PASSWORD "libssh"
</code></pre>
<p>Change them to something else:</p>
<pre><code class="language-cpp">#define SSHD_USER "esp32admin"
#define SSHD_PASSWORD "ChangeThisPassword123"
</code></pre>
<p>Use a long password, especially if the ESP32 will run on a shared network.</p>
<p>Again, do not expose this ESP32 SSH server directly to the internet. If you need remote access outside your home or lab network, use a VPN such as WireGuard or Tailscale instead.</p>
<h2><strong>Troubleshooting</strong></h2>
<h4><strong>The ESP32 Does Not Connect to Wi-Fi</strong></h4>
<p>Check the SSID and password in the sketch.</p>
<p>Also make sure your ESP32 is connecting to a 2.4 GHz Wi-Fi network. Most ESP32 boards do not connect to 5 GHz Wi-Fi.</p>
<h4><strong>The SSH Command Times Out</strong></h4>
<p>If the SSH client times out, check these items:</p>
<ul>
<li>The ESP32 IP address is correct</li>
<li>Your computer and ESP32 are on the same network</li>
<li>The ESP32 Serial Monitor says the SSH server started</li>
<li>Your router does not block client-to-client communication</li>
<li>The ESP32 did not restart due to a power issue</li>
</ul>
<p>You can also try pinging the ESP32:</p>
<pre><code class="language-bash">ping 192.168.1.45
</code></pre>
<p>Replace the IP address with your ESP32 address.</p>
<h4>The SSH Client Shows a Host Key Warning</h4>
<p>If you regenerate the ESP32 host key, your computer may warn you that the host key changed.</p>
<p>This can happen during development. Remove the old host entry from your computer’s <em>known_hosts</em> file, then connect again.</p>
<p>On many systems, you can use:</p>
<pre><code class="language-bash">ssh-keygen -R 192.168.1.45
</code></pre>
<p>Then connect again:</p>
<pre><code class="language-bash">ssh libssh@192.168.1.45
</code></pre>
<h4><strong>The ESP32 Restarts or Crashes</strong></h4>
<p>SSH needs more memory than a basic web server or TCP server. If the ESP32 crashes, try these fixes:</p>
<ul>
<li>Use a stable USB power source</li>
<li>Close other memory-heavy tasks</li>
<li>Avoid large buffers</li>
<li>Test with a basic ESP32 board first</li>
<li>Use an ESP32 module with PSRAM if your project is large</li>
<li>Avoid running camera, Bluetooth, web server, and SSH all at once unless you have enough memory</li>
</ul>
<h4><strong>Port 22 Does Not Work</strong></h4>
<p>Port 22 is the standard SSH port. However, some networks may block it.</p>
<p>For local testing, you can change the SSH server port in the sketch if the example allows it.</p>
<p>For example, you might use:</p>
<pre><code class="language-text">2222
</code></pre>
<p>Then connect with:</p>
<pre><code class="language-bash">ssh -p 2222 libssh@192.168.1.45
</code></pre>
<h4><strong>Security Notes</strong></h4>
<p>SSH is encrypted, but your ESP32 project is still only as secure as your implementation.</p>
<p>Follow these rules:</p>
<ul>
<li>Change the default username and password</li>
<li>Do not expose the ESP32 SSH server to the internet</li>
<li>Use a strong Wi-Fi password</li>
<li>Limit commands to only what your project needs</li>
<li>Do not add dangerous commands without confirmation</li>
<li>Avoid printing secrets in the SSH terminal</li>
<li>Disable the SSH server when not needed</li>
<li>Prefer key-based login for serious projects</li>
</ul>
<p>For hobby and lab projects, an ESP32 SSH server is best used on a trusted local network.</p>
<h2><strong>Arduino IDE vs ESP-IDF</strong></h2>
<p>This tutorial focuses on Arduino IDE because it is easier for beginners and fits most ESP32 hobby projects.</p>
<p>However, if you are already using ESP-IDF, there are ESP-IDF components that provide a more integrated SSH console experience. Some components can expose ESP-IDF console commands over SSH, which makes them useful for professional firmware debugging.</p>
<p>Use Arduino IDE if you want a fast starting point.</p>
<p>Use ESP-IDF if you want deeper control, command registration, and a more production-style console.</p>
<h2><strong>Project Ideas Using an ESP32 SSH Server</strong></h2>
<p>Once the SSH server is working, you can build projects such as:</p>
<ul>
<li>Remote GPIO control terminal</li>
<li>Secure sensor monitor</li>
<li>Wi-Fi relay controller</li>
<li>ESP32 diagnostics shell</li>
<li>Remote reboot tool</li>
<li>Secure serial-to-SSH bridge</li>
<li>Lab instrument command console</li>
<li>Local IoT maintenance port</li>
</ul>
<p>For example, a greenhouse controller can use an SSH command such as:</p>
<pre><code class="language-text">status
</code></pre>
<p>Then return:</p>
<pre><code class="language-text">Temperature: 29.4 C
Humidity: 71 %
Soil moisture: 48 %
Pump: OFF
Fan: ON
</code></pre>
<p>This gives you a simple maintenance interface without building a full web dashboard.</p>
<h2><strong>Conclusion</strong></h2>
<p>Running an SSH server on ESP32 gives your microcontroller a secure remote terminal over Wi-Fi. With the LibSSH-ESP32 library, you can start from the included SSH server example, connect to the ESP32 from a computer, and then build your own command interface.</p>
<p>For most beginner projects, a web server is still easier. However, if you want terminal-style control, encrypted communication, and remote diagnostics, an ESP32 SSH server is a powerful option.</p>
<p>Start with the sample SSH server, change the default credentials, add a few safe commands, and test everything on your local network before using it in a real project.</p>
<h2><strong>Frequently Asked Questions</strong></h2>
<h4><strong>Can ESP32 run an SSH server?</strong></h4>
<p>Yes. The ESP32 can run an SSH server using libraries such as LibSSH-ESP32. However, SSH uses more memory and processing power than simple serial, Telnet, or web server examples.</p>
<h4><strong>Is SSH better than Telnet on ESP32?</strong></h4>
<p>SSH is more secure because it encrypts the connection. Telnet sends data in plain text. If you only need a quick private lab test, Telnet is simpler. If you want encrypted terminal access, use SSH.</p>
<h4><strong>Can I SSH into ESP32 from Windows?</strong></h4>
<p>Yes. Open PowerShell or Command Prompt and use the <em>ssh</em> command:</p>
<pre><code class="language-bash">ssh username@esp32_ip_address
</code></pre>
<p>Windows 10 and Windows 11 usually include OpenSSH by default.</p>
<h4><strong>Can I expose my ESP32 SSH server to the internet?</strong></h4>
<p>You should not expose it directly to the public internet. Use it on a local network. For remote access, use a VPN instead.</p>
<h4><strong>What is the default username and password for the LibSSH-ESP32 example?</strong></h4>
<p>The sample server may use <em>libssh</em> as the username and <em>libssh</em> as the password. Change these credentials before using the project beyond basic testing.</p>
<h4><strong>Does ESP32-S3 also support an SSH server?</strong></h4>
<p>Yes, LibSSH-ESP32 lists support for ESP32 variants including ESP32-S3. However, always check the current library notes and test your specific board because memory availability and board support can vary.</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-run-an-ssh-server-on-esp32/">How to Run an SSH Server on ESP32</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ESP32-S3 Getting Started Guide: Pinout, Arduino IDE, USB, and First Project</title>
		<link>https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Fri, 19 Jun 2026 21:00:28 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=11830</guid>

					<description><![CDATA[<p>The ESP32-S3 is one of the most useful ESP32 boards for modern microcontroller projects. It keeps the Wi-Fi and Bluetooth features that made the original ESP32 popular, but adds better USB support, more memory options, and features aimed at AI, signal processing, and Human Interface Device applications. If you are coming from an Arduino UNO, &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/">ESP32-S3 Getting Started Guide: Pinout, Arduino IDE, USB, and First Project</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The <strong>ESP32-S3</strong> is one of the most useful ESP32 boards for modern microcontroller projects. It keeps the Wi-Fi and Bluetooth features that made the original ESP32 popular, but adds better USB support, more memory options, and features aimed at AI, signal processing, and Human Interface Device applications.</p>
<p>If you are coming from an <a href="https://www.teachmemicro.com/arduino-uno-pinout-diagram/">Arduino UNO</a>, <a href="https://www.teachmemicro.com/intro-nodemcu-arduino/">ESP8266</a>, or <a href="https://www.teachmemicro.com/sensor-display-on-esp32-web-server/">classic ESP32</a> board, the ESP32-S3 can look familiar at first. It still uses GPIO pins, 3.3V logic, serial communication, PWM, I2C, SPI, and ADC inputs. But there are also important differences. Some ESP32-S3 boards have native USB. Some have PSRAM. Some have two USB ports. Some use different onboard LED pins. Some pins are connected internally to flash or PSRAM and should not be used for normal I/O.</p>
<p>This guide explains the ESP32-S3 in a practical and beginner-friendly manner. We will examine the ESP32-S3, its comparison to the older ESP32, how to install it in the Arduino IDE, which pins are safe to use, how USB communication works, and how to upload your first sketch.</p>
<p><span id="more-11830"></span></p>
<h2><strong>What is the ESP32-S3?</strong></h2>
<p><img decoding="async" class="aligncenter" src="https://docs.espressif.com/projects/esp-idf/en/v4.4.7/esp32s3/_images/esp32-s3-devkitc-1-v1-isometric.png" alt="ESP32-S3-DevKitC-1 - ESP32-S3 - — ESP-IDF Programming Guide v4.4.7 documentation" /></p>
<p>The <a href="https://documentation.espressif.com/esp32-s3_datasheet_en.pdf">ESP32-S3</a> is a Wi-Fi and Bluetooth Low Energy microcontroller from Espressif. It is part of the ESP32 family, but it is not just a direct replacement for the original ESP32. It is a newer chip designed for connected embedded projects, USB devices, low-power IoT products, display projects, camera projects, and edge AI applications.</p>
<p>Common ESP32-S3 features include:</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>ESP32-S3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Wireless</td>
<td>2.4 GHz Wi-Fi and Bluetooth Low Energy</td>
</tr>
<tr>
<td>Logic level</td>
<td>3.3V</td>
</tr>
<tr>
<td>USB support</td>
<td>Native USB on supported boards</td>
</tr>
<tr>
<td>GPIO</td>
<td>Many flexible GPIO pins, depending on the module and board</td>
</tr>
<tr>
<td>ADC</td>
<td>Multiple analog-capable pins</td>
</tr>
<tr>
<td>PWM</td>
<td>Available on most usable GPIO pins</td>
</tr>
<tr>
<td>Common interfaces</td>
<td>UART, I2C, SPI, I2S, USB</td>
</tr>
<tr>
<td>Memory options</td>
<td>Varies by module; some boards include PSRAM</td>
</tr>
<tr>
<td>Popular use cases</td>
<td>IoT, displays, USB devices, keyboards, cameras, TinyML, sensor dashboards</td>
</tr>
</tbody>
</table>
<p>The exact features depend on the specific ESP32-S3 module or development board. For example, an ESP32-S3-WROOM board may expose different pins from a compact XIAO ESP32-S3 or ESP32-S3 Zero-style board. Always check the pinout of your exact board before wiring external components.</p>
<h2><strong>ESP32-S3 vs ESP32: What is Different?</strong></h2>
<p>The ESP32-S3 and the original ESP32 are both powerful Wi-Fi microcontrollers, but they are not identical.</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Original ESP32</th>
<th>ESP32-S3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Wi-Fi</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Bluetooth Classic</td>
<td>Yes on many ESP32 chips</td>
<td>No, BLE only</td>
</tr>
<tr>
<td>Bluetooth Low Energy</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Native USB</td>
<td>Usually no</td>
<td>Yes on supported boards</td>
</tr>
<tr>
<td>AI/vector instructions</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>GPIO behavior</td>
<td>Board-dependent</td>
<td>Board-dependent</td>
</tr>
<tr>
<td>Arduino IDE support</td>
<td>Yes</td>
<td>Yes</td>
</tr>
<tr>
<td>Best for</td>
<td>General IoT, Wi-Fi projects, classic ESP32 tutorials</td>
<td>USB projects, modern IoT, displays, cameras, BLE, TinyML</td>
</tr>
</tbody>
</table>
<p>The biggest practical difference for beginners is USB. Many classic ESP32 development boards use a USB-to-serial converter chip. On many ESP32-S3 boards, the USB port can connect directly to the microcontroller. This allows USB CDC serial, USB flashing, USB HID, USB MIDI, and other USB device projects.</p>
<p>However, this also creates confusion. In the Arduino IDE, you may need to enable the correct USB options before the Serial Monitor works properly.</p>
<h2><strong>Common ESP32-S3 Development Boards</strong></h2>
<p>There are many ESP32-S3 boards, and their pinouts are not always the same. Some common examples include:</p>
<table>
<thead>
<tr>
<th>Board Type</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>ESP32-S3-DevKitC-1</td>
<td>Official-style development board with many pins exposed</td>
</tr>
<tr>
<td>ESP32-S3-WROOM boards</td>
<td>Common general-purpose ESP32-S3 modules</td>
</tr>
<tr>
<td>ESP32-S3 N8R8 / N16R8 boards</td>
<td>Usually refers to flash and PSRAM combinations</td>
</tr>
<tr>
<td>Seeed Studio XIAO ESP32-S3</td>
<td>Very small board, good for compact projects</td>
</tr>
<tr>
<td>XIAO ESP32-S3 Sense</td>
<td>Includes camera/microSD features depending on version</td>
</tr>
<tr>
<td>ESP32-S3 Zero-style boards</td>
<td>Compact boards with fewer exposed pins</td>
</tr>
<tr>
<td>LilyGO ESP32-S3 boards</td>
<td>Often includes displays, battery circuits, or special peripherals</td>
</tr>
</tbody>
</table>

<a href='https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/lilygo-esp32-s3/'><img loading="lazy" decoding="async" width="150" height="150" src="https://www.teachmemicro.com/wp-content/uploads/2026/06/lilygo-esp32-s3-150x150.avif" class="attachment-thumbnail size-thumbnail not-transparent" alt="LilyGo ESP32-S3" data-has-transparency="false" data-dominant-color="b3b2b0" style="--dominant-color: #b3b2b0;" /></a>
<a href='https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/esp32-s3-zero/'><img loading="lazy" decoding="async" width="150" height="150" src="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-zero-150x150.avif" class="attachment-thumbnail size-thumbnail not-transparent" alt="ESP32-S3 Zero" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-zero-150x150.avif 150w, https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-zero-300x300.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-zero-768x765.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-zero.avif 906w" sizes="auto, (max-width: 150px) 100vw, 150px" data-has-transparency="false" data-dominant-color="b7bdc3" style="--dominant-color: #b7bdc3;" /></a>
<a href='https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/seeed-xiao-esp32s3/'><img loading="lazy" decoding="async" width="150" height="150" src="https://www.teachmemicro.com/wp-content/uploads/2026/06/seeed-xiao-esp32s3-150x150.avif" class="attachment-thumbnail size-thumbnail not-transparent" alt="Seeed XIAO ESP32-S3" data-has-transparency="false" data-dominant-color="e8e7e8" style="--dominant-color: #e8e7e8;" /></a>
<a href='https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/esp32-s3-wroom-1/'><img loading="lazy" decoding="async" width="150" height="150" src="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-wroom-1-150x150.avif" class="attachment-thumbnail size-thumbnail not-transparent" alt="ESP32-S3 WROOM 1 Module" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-wroom-1-150x150.avif 150w, https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-wroom-1-300x300.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-wroom-1.avif 635w" sizes="auto, (max-width: 150px) 100vw, 150px" data-has-transparency="false" data-dominant-color="d8d8d9" style="--dominant-color: #d8d8d9;" /></a>
<a href='https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/esp32-s3-devkitc-1-2/'><img loading="lazy" decoding="async" width="150" height="150" src="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-s3-devkitc-1-1-150x150.avif" class="attachment-thumbnail size-thumbnail not-transparent" alt="" data-has-transparency="false" data-dominant-color="d9d9d9" style="--dominant-color: #d9d9d9;" /></a>

<p>When following a tutorial, make sure the selected board in the Arduino IDE matches your actual hardware as closely as possible.</p>
<h2><strong>ESP32-S3 Board Names: What Do N8R8 and N16R8 Mean?</strong></h2>
<p>You may see ESP32-S3 boards labeled as N8R8, N16R8, N8, N16, or similar names. These labels usually describe the memory configuration.</p>
<p>For example:</p>
<table>
<thead>
<tr>
<th>Label</th>
<th>Common Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td>N8</td>
<td>8 MB flash</td>
</tr>
<tr>
<td>N16</td>
<td>16 MB flash</td>
</tr>
<tr>
<td>R8</td>
<td>8 MB PSRAM</td>
</tr>
<tr>
<td>N8R8</td>
<td>8 MB flash + 8 MB PSRAM</td>
</tr>
<tr>
<td>N16R8</td>
<td>16 MB flash + 8 MB PSRAM</td>
</tr>
</tbody>
</table>
<p>This matters because the Arduino IDE has board options for flash size, partition scheme, and PSRAM. If you select the wrong PSRAM setting, some sketches may fail or behave unpredictably, especially camera, display, or AI projects.</p>
<h2><strong>ESP32-S3 Pinout Basics</strong></h2>
<p>Click on the pins below to view information:</p>
<div align="center"><iframe loading="lazy" style="border: 1px solid #e2e8f0; border-radius: 8px;" src="https://micropinouts.com/embed/board/esp32-s3-devkitc-1" width="800" height="600" frameborder="0"></iframe></div>
<p>The ESP32-S3 has many GPIO pins, but not every pin is equally safe for beginner projects. Some pins are used for boot configuration. Some may be connected to flash or PSRAM. Some may not be exposed on your board. Some may already be used by onboard LEDs, buttons, displays, cameras, or USB circuits.</p>
<p>A safe beginner approach is:</p>
<ul>
<li>Use clearly labeled GPIO pins from your board’s pinout.</li>
<li>Avoid flash/PSRAM-related pins.</li>
<li>Be careful with strapping pins.</li>
<li>Do not connect 5V signals directly to ESP32-S3 GPIO pins.</li>
<li>Check whether your board already uses a pin for an LED, button, camera, display, or microSD card.</li>
</ul>
<h2><strong>ESP32-S3 Pins to Be Careful With</strong></h2>
<p>The exact safe pins depend on your board, but these are common ESP32-S3 pin cautions:</p>
<table>
<thead>
<tr>
<th>Pin Group</th>
<th>Caution</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPIO0</td>
<td>Boot/strapping pin; often connected to BOOT button</td>
</tr>
<tr>
<td>GPIO3</td>
<td>Strapping pin; use with caution</td>
</tr>
<tr>
<td>GPIO45</td>
<td>Strapping pin; use with caution</td>
</tr>
<tr>
<td>GPIO46</td>
<td>Strapping pin; use with caution; often input-only on some boards</td>
</tr>
<tr>
<td>GPIO19/GPIO20</td>
<td>Often used for native USB D− and D+</td>
</tr>
<tr>
<td>GPIO26–GPIO32</td>
<td>Usually used for SPI flash/PSRAM on many modules</td>
</tr>
<tr>
<td>GPIO33–GPIO37</td>
<td>May be used by Octal flash/PSRAM on some boards</td>
</tr>
<tr>
<td>GPIO43/GPIO44</td>
<td>Often used as UART TX/RX on some ESP32-S3 boards</td>
</tr>
<tr>
<td>GPIO48</td>
<td>Often connected to onboard RGB LED on some boards</td>
</tr>
</tbody>
</table>
<p>This is one reason ESP32-S3 tutorials can be confusing. A sketch that works on one ESP32-S3 board may not work on another if the LED pin, USB wiring, or PSRAM pins are different.</p>
<h2><strong>Recommended ESP32-S3 Pins for Beginner Projects</strong></h2>
<p>For simple digital input/output, PWM, sensors, and modules, start with pins that are exposed on your board and not reserved by onboard hardware.</p>
<p>Commonly usable pins on many ESP32-S3 development boards include:</p>
<table>
<thead>
<tr>
<th>Use Case</th>
<th>Suggested Pins</th>
</tr>
</thead>
<tbody>
<tr>
<td>Digital output</td>
<td>GPIO4, GPIO5, GPIO6, GPIO7, GPIO15, GPIO16, GPIO17, GPIO18</td>
</tr>
<tr>
<td>Digital input</td>
<td>GPIO4, GPIO5, GPIO6, GPIO7, GPIO15, GPIO16, GPIO17, GPIO18</td>
</tr>
<tr>
<td>I2C SDA/SCL</td>
<td>Any suitable GPIO, commonly GPIO8/GPIO9 or board-defined pins</td>
</tr>
<tr>
<td>SPI</td>
<td>Board-dependent; check the board pinout</td>
</tr>
<tr>
<td>PWM</td>
<td>Most normal output-capable GPIO pins</td>
</tr>
<tr>
<td>Analog input</td>
<td>Use ADC-capable pins shown in your board’s pinout</td>
</tr>
<tr>
<td>Built-in LED</td>
<td>Board-dependent; common examples include GPIO48, GPIO38, GPIO15, or none</td>
</tr>
</tbody>
</table>
<p>Do not assume that all ESP32-S3 boards have the same built-in LED pin. Some boards use a normal single-color LED. Some use an addressable RGB LED. Some do not have a user LED at all.</p>
<h2><strong>ESP32-S3 Logic Level and Power</strong></h2>
<p>The ESP32-S3 is a 3.3V microcontroller. This means its GPIO pins are designed for 3.3V logic, not 5V logic.</p>
<p>Important power rules:</p>
<table>
<thead>
<tr>
<th>Rule</th>
<th>Explanation</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPIO logic level is 3.3V</td>
<td>Do not feed 5V directly into GPIO pins</td>
</tr>
<tr>
<td>5V/VBUS pin is usually power input</td>
<td>It is not a 5V logic pin</td>
</tr>
<tr>
<td>3V3 pin can power small external modules</td>
<td>Do not overload the onboard regulator</td>
</tr>
<tr>
<td>Use common ground</td>
<td>External modules must share GND with the ESP32-S3</td>
</tr>
<tr>
<td>Motors and servos need separate power</td>
<td>Do not power motors directly from ESP32-S3 GPIO pins</td>
</tr>
</tbody>
</table>
<p>For sensors, always check whether the module supports 3.3V logic. Many I2C sensor modules work at 3.3V, but some 5V modules need level shifting.</p>
<h2><strong>Installing ESP32-S3 in the Arduino IDE</strong></h2>
<p>The easiest way to start using the ESP32-S3 is through the Arduino IDE.</p>
<h3><strong>Step 1: Install the Arduino IDE</strong></h3>
<p>Download and install the latest Arduino IDE from the official Arduino website.</p>
<h3><strong>Step 2: Add the ESP32 Board Package</strong></h3>
<p>Open Arduino IDE, then go to:</p>
<p><strong>File &gt; Preferences</strong></p>
<p>In <strong>Additional Boards Manager URLs</strong>, add the ESP32 board package URL from Espressif.</p>
<p>Then go to:</p>
<p><strong>Tools &gt; Board &gt; Boards Manager</strong></p>
<p>Search for:</p>
<pre><code class="language-text">esp32
</code></pre>
<p>Install the package by Espressif Systems.</p>
<h3><strong>Step 3: Select an ESP32-S3 Board</strong></h3>
<p>Go to:</p>
<p><strong>Tools &gt; Board &gt; ESP32 Arduino</strong></p>
<p>Then select the board that best matches your hardware. Common options include:</p>
<pre><code class="language-text">ESP32S3 Dev Module
ESP32-S3-DevKitC-1
XIAO_ESP32S3
</code></pre>
<p>The exact board list depends on your installed ESP32 Arduino core version.</p>
<p>If your specific board is not listed, <strong>ESP32S3 Dev Module</strong> often works for generic ESP32-S3 boards, but you may need to manually configure flash size, PSRAM, USB mode, and upload options.</p>
<h2><strong>Important Arduino IDE Settings for ESP32-S3</strong></h2>
<p>ESP32-S3 boards have more board settings than an Arduino UNO. The most important ones are:</p>
<table>
<thead>
<tr>
<th>Setting</th>
<th>What It Does</th>
</tr>
</thead>
<tbody>
<tr>
<td>Board</td>
<td>Selects the target ESP32-S3 board</td>
</tr>
<tr>
<td>Port</td>
<td>Selects the USB/serial port</td>
</tr>
<tr>
<td>USB CDC On Boot</td>
<td>Enables Serial output through native USB on many boards</td>
</tr>
<tr>
<td>Upload Mode</td>
<td>Chooses how the sketch is uploaded</td>
</tr>
<tr>
<td>Flash Size</td>
<td>Must match your board’s flash</td>
</tr>
<tr>
<td>PSRAM</td>
<td>Enable only if your board has PSRAM</td>
</tr>
<tr>
<td>Partition Scheme</td>
<td>Controls app/storage memory layout</td>
</tr>
</tbody>
</table>
<p>For many ESP32-S3 boards, if your sketch uploads but the Serial Monitor shows nothing, check <strong>USB CDC On Boot</strong>. In many cases, it must be enabled for <em>Serial.print()</em> to appear over the native USB port.</p>
<h2><strong>First ESP32-S3 Blink Sketch</strong></h2>
<p>The Blink sketch is usually the first test for any microcontroller board. However, ESP32-S3 boards do not all use the same onboard LED pin.</p>
<p>Try this sketch first:</p>
<pre><code class="language-cpp">#ifndef LED_BUILTIN
#define LED_BUILTIN 48
#endif

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(500);
  digitalWrite(LED_BUILTIN, LOW);
  delay(500);
}
</code></pre>
<p>Upload the sketch. If the onboard LED does not blink, your board may use a different LED pin.</p>
<p>Try replacing the LED pin with one of these common ESP32-S3 onboard LED pins:</p>
<pre><code class="language-cpp">#define LED_BUILTIN 48
</code></pre>
<p>or:</p>
<pre><code class="language-cpp">#define LED_BUILTIN 38
</code></pre>
<p>or:</p>
<pre><code class="language-cpp">#define LED_BUILTIN 15
</code></pre>
<p>If your board has an addressable RGB LED, a simple <em>digitalWrite()</em> blink sketch may not work. Addressable LEDs usually need a library such as Adafruit NeoPixel or FastLED.</p>
<h2><strong>ESP32-S3 Serial Monitor Test</strong></h2>
<p>After Blink, test the Serial Monitor.</p>
<pre><code class="language-cpp">void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("ESP32-S3 Serial test");
}

void loop() {
  Serial.println("Hello from ESP32-S3!");
  delay(1000);
}
</code></pre>
<p>Open the Serial Monitor and set the baud rate to:</p>
<pre><code class="language-text">115200
</code></pre>
<p>If nothing appears, check these:</p>
<ol>
<li>Is the correct port selected?</li>
<li>Is <strong>USB CDC On Boot</strong> enabled?</li>
<li>Are you using the correct USB port on the board?</li>
<li>Did you press reset after opening Serial Monitor?</li>
<li>Is the board selected correctly?</li>
<li>Are you using a charge-only USB cable?</li>
</ol>
<p>Many ESP32-S3 upload and serial problems are caused by USB settings or USB cables.</p>
<h2><strong>ESP32-S3 Native USB Explained</strong></h2>
<p>One of the best reasons to use the ESP32-S3 is native USB support. Depending on your board and firmware, the ESP32-S3 can act as a USB device.</p>
<p>This opens up projects such as:</p>
<ul>
<li>USB keyboard</li>
<li>USB mouse</li>
<li>USB MIDI device</li>
<li>USB serial device</li>
<li>USB firmware flashing</li>
<li>USB HID controller</li>
<li>USB-based debugging</li>
<li>USB host projects on supported boards</li>
</ul>
<p>Some ESP32-S3 development boards have two USB ports:</p>
<table>
<thead>
<tr>
<th>USB Port Type</th>
<th>Common Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td>USB-to-serial port</td>
<td>Uploading and serial monitor through a converter chip</td>
</tr>
<tr>
<td>Native USB port</td>
<td>Direct USB connection to the ESP32-S3</td>
</tr>
</tbody>
</table>
<p>On boards with two USB ports, make sure you know which port you are using. One port may be better for simple Arduino uploads, while the other may be used for native USB examples.</p>
<h2><strong>Reading a Button with ESP32-S3</strong></h2>
<p>Here is a simple button example using the internal pull-up resistor.</p>
<p>Connect one side of the button to the selected GPIO pin and the other side to GND.</p>
<pre><code class="language-cpp">const int buttonPin = 4;

void setup() {
  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  int buttonState = digitalRead(buttonPin);

  if (buttonState == LOW) {
    Serial.println("Button pressed");
  } else {
    Serial.println("Button released");
  }

  delay(200);
}
</code></pre>
<p>Because the internal pull-up is enabled, the pin reads HIGH when the button is not pressed and LOW when the button is pressed.</p>
<h2><strong>Fading an LED with PWM on ESP32-S3</strong></h2>
<p>The ESP32-S3 can generate PWM signals on many output-capable GPIO pins. Here is a simple LED fade example.</p>
<p>Connect an LED with a current-limiting resistor to a safe GPIO pin.</p>
<pre><code class="language-cpp">const int ledPin = 5;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  for (int brightness = 0; brightness &lt;= 255; brightness++) {
    analogWrite(ledPin, brightness);
    delay(5);
  }

  for (int brightness = 255; brightness &gt;= 0; brightness--) {
    analogWrite(ledPin, brightness);
    delay(5);
  }
}
</code></pre>
<p>If your ESP32 Arduino core does not support <em>analogWrite()</em> for your selected board/core version, use the LEDC PWM functions instead.</p>
<h2><strong>Reading an Analog Sensor</strong></h2>
<p>The ESP32-S3 has analog input pins, but you must use pins that are ADC-capable on your specific board.</p>
<p>Here is a simple analog read example:</p>
<pre><code class="language-cpp">const int analogPin = 1;

void setup() {
  Serial.begin(115200);
}

void loop() {
  int value = analogRead(analogPin);

  Serial.print("ADC value: ");
  Serial.println(value);

  delay(500);
}
</code></pre>
<p>For a potentiometer:</p>
<ul>
<li>One outer pin goes to 3.3V</li>
<li>The other outer pin goes to GND</li>
<li>The middle pin goes to an ADC-capable GPIO</li>
</ul>
<p>Do not connect the potentiometer to 5V if its wiper goes into an ESP32-S3 GPIO pin.</p>
<h2><strong>Using I2C with ESP32-S3</strong></h2>
<p>Unlike the Arduino UNO, the ESP32-S3 can usually assign I2C to different GPIO pins. That means SDA and SCL are often flexible.</p>
<p>Example I2C scanner:</p>
<pre><code class="language-cpp">#include &lt;Wire.h&gt;

#define SDA_PIN 8
#define SCL_PIN 9

void setup() {
  Serial.begin(115200);
  delay(1000);

  Wire.begin(SDA_PIN, SCL_PIN);

  Serial.println("ESP32-S3 I2C Scanner");
}

void loop() {
  byte error, address;
  int devices = 0;

  for (address = 1; address &lt; 127; address++) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at 0x");
      if (address &lt; 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
      devices++;
    }
  }

  if (devices == 0) {
    Serial.println("No I2C devices found");
  }

  delay(5000);
}
</code></pre>
<p>If the scanner finds no devices, check the wiring, I2C address, power, and pull-up resistors.</p>
<h2><strong>Common ESP32-S3 Upload Problems</strong></h2>
<h3>Problem: Arduino IDE Cannot Find the Board</h3>
<p>Try these fixes:</p>
<ul>
<li>Use a data-capable USB cable.</li>
<li>Try another USB port.</li>
<li>Install the ESP32 board package.</li>
<li>Hold BOOT while clicking Upload.</li>
<li>Press RESET after upload starts.</li>
<li>Try the other USB port if your board has two ports.</li>
</ul>
<h3>Problem: Upload Starts But Fails</h3>
<p>Possible causes:</p>
<ul>
<li>Wrong board selected</li>
<li>Wrong upload mode</li>
<li>Bad USB cable</li>
<li>BOOT button not pressed when needed</li>
<li>Driver issue for USB-to-serial converter</li>
<li>Wrong port selected</li>
<li>External circuit pulling a boot pin to the wrong level</li>
</ul>
<p>Disconnect external wiring while troubleshooting upload problems. A sensor or module connected to a strapping pin can prevent the ESP32-S3 from booting or entering upload mode.</p>
<h3>Problem: Serial Monitor Shows Nothing</h3>
<p>Try these:</p>
<ul>
<li>Enable <strong>USB CDC On Boot</strong></li>
<li>Select the correct port</li>
<li>Set Serial Monitor to 115200 baud</li>
<li>Press RESET after opening Serial Monitor</li>
<li>Use the correct USB connector</li>
<li>Make sure the sketch uses <em>Serial.begin(115200);</em></li>
</ul>
<h3>Problem: Built-in LED Does Not Blink</h3>
<p>Possible causes:</p>
<ul>
<li>Wrong LED pin</li>
<li>Board uses addressable RGB LED</li>
<li>Board has no onboard user LED</li>
<li>LED is active-low</li>
<li>Selected board definition uses a different <em>LED_BUILTIN</em></li>
</ul>
<p>Check your board’s schematic or product page.</p>
<h3>Problem: PSRAM Sketch Fails</h3>
<p>If your board has PSRAM, enable PSRAM in the Arduino IDE. If your board does not have PSRAM, do not enable it.</p>
<p>Camera, display, graphics, and AI examples often require PSRAM.</p>
<h2><strong>ESP32-S3 Project Ideas</strong></h2>
<p>Once you have the ESP32-S3 working, here are some good project ideas:</p>
<table>
<thead>
<tr>
<th>Project</th>
<th>Why ESP32-S3 is a Good Fit</th>
</tr>
</thead>
<tbody>
<tr>
<td>Wi-Fi sensor dashboard</td>
<td>Built-in Wi-Fi and enough processing power</td>
</tr>
<tr>
<td>BLE remote control</td>
<td>Bluetooth Low Energy support</td>
</tr>
<tr>
<td>USB keyboard macro pad</td>
<td>Native USB HID support</td>
</tr>
<tr>
<td>USB MIDI controller</td>
<td>Native USB device support</td>
</tr>
<tr>
<td>Web-controlled relay</td>
<td>Wi-Fi and GPIO control</td>
</tr>
<tr>
<td>Camera project</td>
<td>Many ESP32-S3 boards support camera modules</td>
</tr>
<tr>
<td>TinyML image classifier</td>
<td>PSRAM-equipped boards are useful for AI examples</td>
</tr>
<tr>
<td>Display dashboard</td>
<td>Good for SPI/TFT display projects</td>
</tr>
<tr>
<td>I2C sensor hub</td>
<td>Flexible I2C pins</td>
</tr>
<tr>
<td>Battery-powered IoT node</td>
<td>Suitable for low-power wireless projects with proper design</td>
</tr>
</tbody>
</table>
<h2><strong>ESP32-S3 Beginner Tips</strong></h2>
<ol>
<li><strong>Do not assume all ESP32-S3 boards have the same pinout.</strong><br />
Always check your exact board.</li>
<li><strong>Use 3.3V logic.</strong><br />
ESP32-S3 GPIO pins are not 5V tolerant.</li>
<li><strong>Avoid flash and PSRAM pins.</strong><br />
Some GPIO pins are used internally and should not be used for external circuits.</li>
<li><strong>Be careful with boot pins.</strong><br />
External pull-ups, pull-downs, LEDs, or modules can affect boot mode.</li>
<li><strong>Check USB CDC settings.</strong><br />
Many Serial Monitor issues are caused by USB configuration.</li>
<li><strong>Know your board’s memory.</strong><br />
Flash and PSRAM settings matter for larger projects.</li>
<li><strong>Start simple.</strong><br />
Test Blink, Serial, button input, PWM, and I2C before building a complex project.</li>
</ol>
<h2><strong>Frequently Asked Questions</strong></h2>
<h3>Is ESP32-S3 better than ESP32?</h3>
<p>For many new projects, yes. The ESP32-S3 has native USB support, modern BLE support, and features useful for AI and signal processing. However, the original ESP32 still has Bluetooth Classic support, which the ESP32-S3 does not have. If your project needs Bluetooth Classic audio or serial Bluetooth, check compatibility first.</p>
<h3>Does ESP32-S3 support Bluetooth Classic?</h3>
<p>No. The ESP32-S3 supports Bluetooth Low Energy, but not Bluetooth Classic. This is important if you are using old ESP32 Bluetooth Serial examples.</p>
<h3>Can I program ESP32-S3 with Arduino IDE?</h3>
<p>Yes. Install the ESP32 board package by Espressif in the Arduino IDE, select an ESP32-S3 board, choose the correct port, and upload your sketch.</p>
<h3>Why is my ESP32-S3 Serial Monitor blank?</h3>
<p>The most common causes are wrong port selection, USB CDC disabled, wrong USB connector, missing <em>Serial.begin(), or a charge-only USB cable.</em></p>
<h3>What is the ESP32-S3 built-in LED pin?</h3>
<p>It depends on the board. Some ESP32-S3 boards use GPIO48, others use GPIO38 or GPIO15, and some use an addressable RGB LED or no user LED at all.</p>
<h3>Can ESP32-S3 use 5V sensors?</h3>
<p>The ESP32-S3 itself uses 3.3V logic. Some sensor modules can be powered by 5V but still output 3.3V signals, while others output 5V signals. Do not connect a 5V signal directly to an ESP32-S3 GPIO pin.</p>
<h3>Which ESP32-S3 board should beginners buy?</h3>
<p>A general ESP32-S3 DevKit-style board is a good starting point because it exposes many pins and is easier to use on a breadboard. If you want camera or TinyML projects, choose a board with PSRAM and camera support.</p>
<h2><strong>Conclusion</strong></h2>
<p>The ESP32-S3 is a powerful upgrade for many ESP32 projects, especially if you want native USB, BLE, more memory options, camera support, display projects, or TinyML experiments. It is still beginner-friendly, but it has more board-specific details than an Arduino UNO or even a classic ESP32.</p>
<p>The most important things to remember are:</p>
<ul>
<li>Select the correct ESP32-S3 board in the Arduino IDE.</li>
<li>Use the correct USB CDC and upload settings.</li>
<li>Check the actual pinout of your board.</li>
<li>Avoid pins used by flash, PSRAM, USB, or boot configuration.</li>
<li>Use 3.3V logic only.</li>
<li>Do not assume the onboard LED pin is the same on every ESP32-S3 board.</li>
</ul>
<p>Once you understand these details, the ESP32-S3 becomes one of the most flexible microcontroller boards for Wi-Fi, BLE, USB, sensor, display, and embedded AI projects.</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-s3-getting-started-guide-pinout-arduino-ide-usb-and-first-project/">ESP32-S3 Getting Started Guide: Pinout, Arduino IDE, USB, and First Project</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>ESP32 Async Web Server Event Handler Tutorial</title>
		<link>https://www.teachmemicro.com/esp32-async-web-server-event-handler-tutorial/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=esp32-async-web-server-event-handler-tutorial</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 21:00:20 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=11819</guid>

					<description><![CDATA[<p>The ESP32 is powerful enough to host a small web server directly on the board. This means you can control LEDs, read sensor values, update a webpage, or build a simple browser-based dashboard without needing a separate computer or cloud server. One of the most useful libraries for this is the ESP32 Async Web Server &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-async-web-server-event-handler-tutorial/">ESP32 Async Web Server Event Handler Tutorial</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The ESP32 is powerful enough to host a small web server directly on the board. This means you can control LEDs, read sensor values, update a webpage, or build a simple browser-based dashboard without needing a separate computer or cloud server.</p>
<p>One of the most useful libraries for this is the ESP32 Async Web Server library. Unlike a <a href="https://www.teachmemicro.com/sensor-display-on-esp32-web-server/">basic web server</a> that handles one request at a time in a more blocking way, an asynchronous web server can respond to browser requests more efficiently while your ESP32 continues running the rest of your program.</p>
<p>In this tutorial, you will learn how to use an ESP32 Async Web Server event handler. We will start with simple route handlers, then control an LED from a web page, handle missing pages with <em>onNotFound()</em>, and finally use Server-Sent Events to push live data from the ESP32 to the browser.</p>
<p><span id="more-11819"></span></p>
<h2>What Is an ESP32 Async Web Server Event Handler?</h2>
<p>In an ESP32 Async Web Server sketch, an <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function">event handler is a callback function</a> that runs when something happens.</p>
<p>For example:</p>
<ul>
<li>A browser requests the homepage</li>
<li>A user clicks a button on the webpage</li>
<li>The browser requests a URL like <em>/led/on</em></li>
<li>A page is not found</li>
<li>The ESP32 sends live sensor data to the browser</li>
</ul>
<p>In the ESPAsyncWebServer library, the most common event handlers are route handlers. A route handler tells the ESP32 what to do when a specific URL is requested.</p>
<p>For example:</p>
<pre><code class="language-cpp">server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
  request-&gt;send(200, "text/plain", "Hello from ESP32!");
});
</code></pre>
<p>This handler runs when a browser visits the root URL <em>/</em>.</p>
<h2>Required Libraries</h2>
<p>For this tutorial, you need:</p>
<ul>
<li>ESP32 board package installed in Arduino IDE</li>
<li>ESPAsyncWebServer library</li>
<li>AsyncTCP library</li>
</ul>
<p>In Arduino IDE, install the required libraries using the Library Manager or by adding the libraries manually from their repositories.</p>
<p>You will also need an ESP32 development board such as an ESP32 DevKit, ESP32-WROOM board, or similar.</p>
<h2>Circuit Diagram</h2>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-async-webserver-led-circuit.avif"><img data-dominant-color="d0cfcf" data-has-transparency="false" style="--dominant-color: #d0cfcf;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-11867 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-async-webserver-led-circuit.avif" alt="ESP32 Async Webserver LED circuit" width="783" height="617" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-async-webserver-led-circuit.avif 783w, https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-async-webserver-led-circuit-300x236.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/06/esp32-async-webserver-led-circuit-768x605.avif 768w" sizes="auto, (max-width: 783px) 100vw, 783px" /></a></p>
<p>For the example project, connect one LED to the ESP32.</p>
<table>
<thead>
<tr>
<th>LED Pin</th>
<th>ESP32 Connection</th>
</tr>
</thead>
<tbody>
<tr>
<td>LED anode through resistor</td>
<td>GPIO 2</td>
</tr>
<tr>
<td>LED cathode</td>
<td>GND</td>
</tr>
</tbody>
</table>
<p>Most ESP32 development boards also have a built-in LED, but the pin can vary depending on the board. GPIO 2 is commonly used in examples, but check your specific board if the LED does not turn on.</p>
<p>Use a 220 ohm to 330 ohm resistor in series with the LED.</p>
<h2>Basic ESP32 Async Web Server Example</h2>
<p>Let us start with a very simple Async Web Server sketch.</p>
<p>Replace <em>YOUR_WIFI_SSID</em> and <em>YOUR_WIFI_PASSWORD</em> with your Wi-Fi credentials.</p>
<pre><code class="language-cpp">#include &lt;WiFi.h&gt;
#include &lt;AsyncTCP.h&gt;
#include &lt;ESPAsyncWebServer.h&gt;

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

AsyncWebServer server(80);

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request-&gt;send(200, "text/plain", "Hello from ESP32 Async Web Server!");
  });

  server.begin();
}

void loop() {
}
</code></pre>
<p>Upload the sketch to your ESP32 and open the Serial Monitor. After the ESP32 connects to Wi-Fi, it will print its IP address.</p>
<p>Open that IP address in your browser. You should see:</p>
<pre><code class="language-text">Hello from ESP32 Async Web Server!
</code></pre>
<p>Notice that the <em>loop()</em> is empty. With ESPAsyncWebServer, you do not need to call <em>server.handleClient()</em> inside the loop.</p>
<h2>Handling Web Page Requests</h2>
<p>The most common use of an event handler is responding to a page request.</p>
<p>This line creates a handler for the homepage:</p>
<pre><code class="language-cpp">server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
  request-&gt;send(200, "text/plain", "Hello from ESP32!");
});
</code></pre>
<p>Here is what each part means:</p>
<table>
<thead>
<tr>
<th>Code</th>
<th>Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td><em>server.on()</em></td>
<td>Creates a route handler</td>
</tr>
<tr>
<td><em>"/"</em></td>
<td>The URL path</td>
</tr>
<tr>
<td><em>HTTP_GET</em></td>
<td>The HTTP method</td>
</tr>
<tr>
<td><em>AsyncWebServerRequest *request</em></td>
<td>The browser request object</td>
</tr>
<tr>
<td><em>request-&gt;send()</em></td>
<td>Sends a response back to the browser</td>
</tr>
</tbody>
</table>
<p>You can add more routes like this:</p>
<pre><code class="language-cpp">server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request) {
  request-&gt;send(200, "text/plain", "ESP32 is running");
});

server.on("/about", HTTP_GET, [](AsyncWebServerRequest *request) {
  request-&gt;send(200, "text/plain", "ESP32 Async Web Server Tutorial");
});
</code></pre>
<p>When the browser visits <em>/status</em>, the ESP32 responds with <em>ESP32 is running</em>.</p>
<h2>ESP32 Async Web Server LED Control Example</h2>
<p>Now let us make a web page with two buttons: one to turn the LED on and another to turn it off.</p>
<pre><code class="language-cpp">#include &lt;WiFi.h&gt;
#include &lt;AsyncTCP.h&gt;
#include &lt;ESPAsyncWebServer.h&gt;

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

const int ledPin = 2;
bool ledState = false;

AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;ESP32 Async Web Server&lt;/title&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
  &lt;style&gt;
    body {
      font-family: Arial;
      text-align: center;
      margin-top: 50px;
    }

    button {
      font-size: 20px;
      padding: 12px 24px;
      margin: 10px;
      cursor: pointer;
    }

    .on {
      background-color: #4CAF50;
      color: white;
    }

    .off {
      background-color: #f44336;
      color: white;
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;ESP32 Async Web Server&lt;/h1&gt;
  &lt;p&gt;LED State: &lt;span id="ledState"&gt;Unknown&lt;/span&gt;&lt;/p&gt;

  &lt;button class="on" onclick="controlLED('on')"&gt;Turn ON&lt;/button&gt;
  &lt;button class="off" onclick="controlLED('off')"&gt;Turn OFF&lt;/button&gt;

  &lt;script&gt;
    function controlLED(state) {
      fetch('/led/' + state)
        .then(response =&gt; response.text())
        .then(data =&gt; {
          document.getElementById('ledState').innerText = data;
        });
    }
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
)rawliteral";

void setup() {
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("ESP32 IP Address: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request-&gt;send_P(200, "text/html", index_html);
  });

  server.on("/led/on", HTTP_GET, [](AsyncWebServerRequest *request) {
    ledState = true;
    digitalWrite(ledPin, HIGH);
    request-&gt;send(200, "text/plain", "ON");
  });

  server.on("/led/off", HTTP_GET, [](AsyncWebServerRequest *request) {
    ledState = false;
    digitalWrite(ledPin, LOW);
    request-&gt;send(200, "text/plain", "OFF");
  });

  server.begin();
}

void loop() {
}
</code></pre>
<p>In this example, the web page does not reload when you click the buttons. The browser uses <em>fetch()</em> to send a request to the ESP32.</p>
<p>When you click the ON button, the browser requests:</p>
<pre><code class="language-text">/led/on
</code></pre>
<p>The ESP32 runs this event handler:</p>
<pre><code class="language-cpp">server.on("/led/on", HTTP_GET, [](AsyncWebServerRequest *request) {
  ledState = true;
  digitalWrite(ledPin, HIGH);
  request-&gt;send(200, "text/plain", "ON");
});
</code></pre>
<p>When you click the OFF button, the browser requests:</p>
<pre><code class="language-text">/led/off
</code></pre>
<p>The ESP32 then turns the LED off and sends a response back to the browser.</p>
<h2>Handling URL Parameters</h2>
<p>You can also send values through the URL. For example:</p>
<pre><code class="language-text">/set?led=on
</code></pre>
<p>Here is an example handler:</p>
<pre><code class="language-cpp">server.on("/set", HTTP_GET, [](AsyncWebServerRequest *request) {
  if (request-&gt;hasParam("led")) {
    String value = request-&gt;getParam("led")-&gt;value();

    if (value == "on") {
      digitalWrite(ledPin, HIGH);
      request-&gt;send(200, "text/plain", "LED turned ON");
    } 
    else if (value == "off") {
      digitalWrite(ledPin, LOW);
      request-&gt;send(200, "text/plain", "LED turned OFF");
    } 
    else {
      request-&gt;send(400, "text/plain", "Invalid LED value");
    }
  } 
  else {
    request-&gt;send(400, "text/plain", "Missing led parameter");
  }
});
</code></pre>
<p>Now you can control the LED using these URLs:</p>
<pre><code class="language-text">http://ESP32-IP-ADDRESS/set?led=on
http://ESP32-IP-ADDRESS/set?led=off
</code></pre>
<p>This is useful when you want to pass values such as PWM brightness, servo angle, relay state, or sensor settings.</p>
<h2>Using onNotFound()</h2>
<p>The <em>onNotFound()</em> handler runs when the browser requests a URL that does not exist.</p>
<p>Add this before <em>server.begin()</em>:</p>
<pre><code class="language-cpp">server.onNotFound([](AsyncWebServerRequest *request) {
  request-&gt;send(404, "text/plain", "Page not found");
});
</code></pre>
<p>Now if the browser visits a wrong URL, such as:</p>
<pre><code class="language-text">/random-page
</code></pre>
<p>The ESP32 responds with:</p>
<pre><code class="language-text">Page not found
</code></pre>
<p>This is useful for debugging because you can immediately see when the browser is requesting a route that your sketch does not handle.</p>
<p>You can also print the missing URL to the Serial Monitor:</p>
<pre><code class="language-cpp">server.onNotFound([](AsyncWebServerRequest *request) {
  Serial.print("Not found: ");
  Serial.println(request-&gt;url());
  request-&gt;send(404, "text/plain", "Page not found");
});
</code></pre>
<h2>Using Server-Sent Events with ESP32</h2>
<p>Route handlers are useful when the browser asks the ESP32 for something. But what if the ESP32 needs to send live updates to the browser automatically?</p>
<p>For that, you can use Server-Sent Events, also called SSE.</p>
<p>Server-Sent Events allow the ESP32 to push data to the browser. This is useful for:</p>
<ul>
<li>Live temperature readings</li>
<li>Sensor dashboards</li>
<li>GPIO status updates</li>
<li>ADC values</li>
<li>Distance sensor readings</li>
<li>System status messages</li>
</ul>
<p>In ESPAsyncWebServer, this is done using <em>AsyncEventSource</em>.</p>
<h2>ESP32 Async Web Server EventSource Example</h2>
<p>The following example sends a counter value from the ESP32 to the browser every second.</p>
<pre><code class="language-cpp">#include &lt;WiFi.h&gt;
#include &lt;AsyncTCP.h&gt;
#include &lt;ESPAsyncWebServer.h&gt;

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

AsyncWebServer server(80);
AsyncEventSource events("/events");

unsigned long lastEventTime = 0;
int counter = 0;

const char index_html[] PROGMEM = R"rawliteral(
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;ESP32 Server-Sent Events&lt;/title&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
  &lt;style&gt;
    body {
      font-family: Arial;
      text-align: center;
      margin-top: 50px;
    }

    #counter {
      font-size: 48px;
      font-weight: bold;
      color: #0066cc;
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;ESP32 Async Web Server Events&lt;/h1&gt;
  &lt;p&gt;Live Counter:&lt;/p&gt;
  &lt;div id="counter"&gt;0&lt;/div&gt;

  &lt;script&gt;
    const source = new EventSource('/events');

    source.addEventListener('counter', function(event) {
      document.getElementById('counter').innerText = event.data;
    });

    source.onerror = function(error) {
      console.log('EventSource error:', error);
    };
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
)rawliteral";

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("ESP32 IP Address: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request-&gt;send_P(200, "text/html", index_html);
  });

  events.onConnect([](AsyncEventSourceClient *client) {
    Serial.println("Client connected to events");

    if (client-&gt;lastId()) {
      Serial.printf("Client reconnected. Last message ID: %u\n", client-&gt;lastId());
    }

    client-&gt;send("Connected to ESP32 events", NULL, millis(), 1000);
  });

  server.addHandler(&amp;events);

  server.begin();
}

void loop() {
  if (millis() - lastEventTime &gt; 1000) {
    lastEventTime = millis();
    counter++;

    String counterString = String(counter);
    events.send(counterString.c_str(), "counter", millis());
  }
}
</code></pre>
<p>In the browser, this line creates a connection to the ESP32 event source:</p>
<pre><code class="language-javascript">const source = new EventSource('/events');
</code></pre>
<p>This JavaScript event handler listens for events named <em>counter</em>:</p>
<pre><code class="language-javascript">source.addEventListener('counter', function(event) {
  document.getElementById('counter').innerText = event.data;
});
</code></pre>
<p>On the ESP32 side, this line sends the event:</p>
<pre><code class="language-cpp">events.send(counterString.c_str(), "counter", millis());
</code></pre>
<p>The second argument, <em>"counter"</em>, is the event name. The browser listens for the same event name using <em>addEventListener()</em>.</p>
<h2>Complete LED and Event Handler Example</h2>
<p>Here is a more complete example that combines LED control and live status updates.</p>
<pre><code class="language-cpp">#include &lt;WiFi.h&gt;
#include &lt;AsyncTCP.h&gt;
#include &lt;ESPAsyncWebServer.h&gt;

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

const int ledPin = 2;
bool ledState = false;

AsyncWebServer server(80);
AsyncEventSource events("/events");

unsigned long lastStatusTime = 0;

const char index_html[] PROGMEM = R"rawliteral(
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;ESP32 LED Event Handler&lt;/title&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
  &lt;style&gt;
    body {
      font-family: Arial;
      text-align: center;
      margin-top: 40px;
    }

    button {
      font-size: 20px;
      padding: 12px 24px;
      margin: 10px;
    }

    #status {
      font-size: 28px;
      font-weight: bold;
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;ESP32 Async Web Server Event Handler&lt;/h1&gt;

  &lt;p&gt;LED Status:&lt;/p&gt;
  &lt;div id="status"&gt;Waiting...&lt;/div&gt;

  &lt;button onclick="setLED('on')"&gt;Turn ON&lt;/button&gt;
  &lt;button onclick="setLED('off')"&gt;Turn OFF&lt;/button&gt;

  &lt;script&gt;
    function setLED(state) {
      fetch('/led/' + state);
    }

    const source = new EventSource('/events');

    source.addEventListener('led', function(event) {
      document.getElementById('status').innerText = event.data;
    });
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
)rawliteral";

void sendLedStatus() {
  if (ledState) {
    events.send("ON", "led", millis());
  } else {
    events.send("OFF", "led", millis());
  }
}

void setup() {
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.print("ESP32 IP Address: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
    request-&gt;send_P(200, "text/html", index_html);
  });

  server.on("/led/on", HTTP_GET, [](AsyncWebServerRequest *request) {
    ledState = true;
    digitalWrite(ledPin, HIGH);
    sendLedStatus();
    request-&gt;send(200, "text/plain", "LED ON");
  });

  server.on("/led/off", HTTP_GET, [](AsyncWebServerRequest *request) {
    ledState = false;
    digitalWrite(ledPin, LOW);
    sendLedStatus();
    request-&gt;send(200, "text/plain", "LED OFF");
  });

  events.onConnect([](AsyncEventSourceClient *client) {
    Serial.println("Event client connected");
    client-&gt;send(ledState ? "ON" : "OFF", "led", millis(), 1000);
  });

  server.addHandler(&amp;events);

  server.onNotFound([](AsyncWebServerRequest *request) {
    request-&gt;send(404, "text/plain", "Page not found");
  });

  server.begin();
}

void loop() {
  if (millis() - lastStatusTime &gt; 5000) {
    lastStatusTime = millis();
    sendLedStatus();
  }
}
</code></pre>
<p>In this version, clicking a button sends a request to the ESP32. The ESP32 changes the LED state, then sends an event back to the browser. The browser updates the displayed LED status without reloading the page.</p>
<h2>ESP32 Async Web Server vs Regular WebServer</h2>
<p>The regular ESP32 <em>WebServer</em> library usually requires code like this inside the loop:</p>
<pre><code class="language-cpp">server.handleClient();
</code></pre>
<p>With ESPAsyncWebServer, you normally do not need that. The server handles requests asynchronously using callbacks.</p>
<p>For simple projects, the regular <em>WebServer</em> library is easier to understand. But for more responsive pages, real-time updates, dashboards, and projects with multiple clients, ESPAsyncWebServer is often a better choice.</p>
<p>Use the regular <em>WebServer</em> library if:</p>
<ul>
<li>You are building a very simple project</li>
<li>You want fewer dependencies</li>
<li>You are still learning basic web server concepts</li>
</ul>
<p>Use ESPAsyncWebServer if:</p>
<ul>
<li>You want responsive web controls</li>
<li>You want Server-Sent Events</li>
<li>You want WebSocket support</li>
<li>You want to serve pages without blocking your main code</li>
<li>You are building an ESP32 dashboard or control panel</li>
</ul>
<h2>Common Errors and Fixes</h2>
<h3>ESPAsyncWebServer.h: No such file or directory</h3>
<p>This means the ESPAsyncWebServer library is not installed correctly.</p>
<p>Make sure you installed:</p>
<pre><code class="language-cpp">#include &lt;ESPAsyncWebServer.h&gt;
</code></pre>
<p>You also need the AsyncTCP library for ESP32:</p>
<pre><code class="language-cpp">#include &lt;AsyncTCP.h&gt;
</code></pre>
<h3>AsyncTCP.h: No such file or directory</h3>
<p>Install the AsyncTCP library. ESPAsyncWebServer depends on it when used with ESP32.</p>
<h3>The Web Page Does Not Open</h3>
<p>Check the Serial Monitor and make sure the ESP32 is connected to Wi-Fi. The ESP32 and your computer or phone must usually be connected to the same network.</p>
<p>Also make sure you are entering the correct IP address printed by the ESP32.</p>
<h3>The LED Does Not Turn On</h3>
<p>Check your LED polarity. The longer leg is usually the anode and should go through a resistor to the GPIO pin. The shorter leg is usually the cathode and should go to GND.</p>
<p>Also check if your board really uses GPIO 2 for the LED. Some ESP32 boards use a different built-in LED pin.</p>
<h3>Buttons Work But Page Does Not Update</h3>
<p>If the LED changes but the text on the page does not update, check the browser console. There may be a JavaScript error.</p>
<p>Also make sure the event name in the ESP32 code matches the JavaScript listener.</p>
<p>For example, this ESP32 event:</p>
<pre><code class="language-cpp">events.send("ON", "led", millis());
</code></pre>
<p>must match this JavaScript event listener:</p>
<pre><code class="language-javascript">source.addEventListener('led', function(event) {
  document.getElementById('status').innerText = event.data;
});
</code></pre>
<h3>Do Not Use Long delay() Calls</h3>
<p>Although the web server itself is asynchronous, you should still avoid long blocking code in your sketch. Long <em>delay()</em> calls, slow loops, or blocking sensor reads can make your project feel less responsive.</p>
<p>Use <em>millis()</em> timing instead when possible.</p>
<h2>Final Thoughts</h2>
<p>The ESP32 Async Web Server library is a powerful way to build web-controlled ESP32 projects. With event handlers, you can respond to browser requests, control GPIO pins, read URL parameters, handle missing pages, and push live updates to the browser using Server-Sent Events.</p>
<p>For beginner projects, start with simple route handlers like <em>/led/on</em> and <em>/led/off</em>. Once that works, add a web page with buttons. After that, try Server-Sent Events to display live sensor readings without refreshing the page.</p>
<p>This makes ESP32 Async Web Server useful for home automation, IoT dashboards, sensor monitors, relay controls, robotics projects, and browser-based embedded interfaces.</p>
<p>The post <a href="https://www.teachmemicro.com/esp32-async-web-server-event-handler-tutorial/">ESP32 Async Web Server Event Handler Tutorial</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Arduino ESP32 Autobaud Example: Detect UART Baud Rate Automatically</title>
		<link>https://www.teachmemicro.com/arduino-esp32-autobaud-example-detect-uart-baud-rate-automatically/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=arduino-esp32-autobaud-example-detect-uart-baud-rate-automatically</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Thu, 18 Jun 2026 01:00:50 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=11816</guid>

					<description><![CDATA[<p>If you are connecting an ESP32 to a UART device, both sides normally need to use the same baud rate. For example, if your external module is sending data at 9600 baud but your ESP32 is listening at 115200 baud, the data will not be read correctly. Fortunately, some ESP32 chips support automatic baud rate &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/arduino-esp32-autobaud-example-detect-uart-baud-rate-automatically/">Arduino ESP32 Autobaud Example: Detect UART Baud Rate Automatically</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>If you are connecting an ESP32 to a <a href="https://www.teachmemicro.com/microcontroller-serial-communication/">UART</a> device, both sides normally need to use the same baud rate. For example, if your external module is sending data at 9600 baud but your ESP32 is listening at 115200 baud, the data will not be read correctly.</p>
<p>Fortunately, some ESP32 chips support automatic baud rate detection. In this <strong>Arduino ESP32 autobaud example</strong>, we’ll use the Arduino-ESP32 Serial API to detect the baud rate of incoming UART data automatically.</p>
<p>This is useful when working with unknown serial devices, <a href="https://www.teachmemicro.com/esp8266-wifi-command-list/">AT command</a> modules, <a href="https://www.teachmemicro.com/arduino-gps-tutorial/">GPS</a> modules, <a href="https://www.teachmemicro.com/arduino-sim800l-tutorial/">GSM</a> modules, <a href="https://www.teachmemicro.com/arduino-bluetooth/">Bluetooth</a> serial modules, or any UART device where the baud rate is not immediately known.</p>
<hr />
<h2>What Is Autobaud?</h2>
<p>Autobaud is a feature that allows a UART peripheral to detect the baud rate of incoming serial data.</p>
<p>Instead of writing:</p>
<pre><code class="language-cpp">Serial.begin(9600);
</code></pre>
<p>or:</p>
<pre><code class="language-cpp">Serial.begin(115200);
</code></pre>
<p>you can start the serial port with a baud rate of <em>0</em> on supported ESP32 chips:</p>
<pre><code class="language-cpp">Serial.begin(0);
</code></pre>
<p>This tells the ESP32 to wait for incoming UART data and try to determine the correct baud rate automatically.</p>
<hr />
<h2>Important: ESP32 Autobaud Is Not Supported on All ESP32 Chips</h2>
<p>According to the Arduino-ESP32 Serial API, baud rate detection is supported only on:</p>
<ul>
<li>ESP32</li>
<li>ESP32-S2</li>
</ul>
<p>It does not work on all newer ESP32 variants such as ESP32-C3, ESP32-S3, ESP32-C6, and other SoCs.</p>
<p>This is important because many boards are called “ESP32” even if they use a different chip. For example:</p>
<table>
<thead>
<tr>
<th>Board / Chip</th>
<th>Autobaud Support</th>
</tr>
</thead>
<tbody>
<tr>
<td>ESP32-WROOM-32</td>
<td>Yes</td>
</tr>
<tr>
<td>ESP32-S2</td>
<td>Yes</td>
</tr>
<tr>
<td>ESP32-S3</td>
<td>No</td>
</tr>
<tr>
<td>ESP32-C3</td>
<td>No</td>
</tr>
<tr>
<td>ESP32-C6</td>
<td>No</td>
</tr>
</tbody>
</table>
<p>So before using this feature, check the actual chip on your board.</p>
<hr />
<h2>Basic Arduino ESP32 Autobaud Example</h2>
<p>Here is the simplest Arduino ESP32 autobaud example:</p>
<pre><code class="language-cpp">void setup() {
  Serial.begin(0);

  Serial.print("Detected baud rate: ");
  Serial.println(Serial.baudRate());

  if (Serial.baudRate() == 0) {
    Serial.end();
    Serial.begin(115200);
    delay(1000);

    Serial.println("Baud rate detection failed.");
    Serial.println("Serial reset to 115200.");
  }
}

void loop() {
}
</code></pre>
<p>In this example, Serial.begin(0) starts baud rate detection.</p>
<p>The ESP32 waits for incoming serial data. If it detects the baud rate successfully, Serial.baudRate() returns the detected baud rate.</p>
<p>If detection fails, Serial.baudRate() returns 0. The sketch then restarts Serial at 115200 baud so you can still see a message in the Arduino Serial Monitor.</p>
<hr />
<h2>How to Test ESP32 Autobaud</h2>
<p>To test this sketch, upload it to an ESP32 or ESP32-S2 board.</p>
<p>Then open the Arduino IDE Serial Monitor and choose a baud rate such as:</p>
<ul>
<li>9600</li>
<li>19200</li>
<li>38400</li>
<li>57600</li>
<li>115200</li>
</ul>
<p>Type a few characters and send them.</p>
<p>The ESP32 should detect the baud rate and print the detected value.</p>
<p>The detected value may not always be exactly the same number. For example, 115200 may appear as 115201. This small difference is normal.</p>
<hr />
<h2>Autobaud on Serial1 or Serial2</h2>
<p>In many real projects, you probably do not want to use Serial for autobaud because Serial is usually connected to the USB port and used for debugging.</p>
<p>Instead, you can use Serial1 or Serial2 for the external UART device.</p>
<p>Here is an example using Serial2 on GPIO16 and GPIO17:</p>
<pre><code class="language-cpp">#define RXD2 16
#define TXD2 17

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("Arduino ESP32 Autobaud Example on Serial2");
  Serial.println("Waiting for incoming UART data...");

  Serial2.begin(0, SERIAL_8N1, RXD2, TXD2, false, 20000);

  uint32_t detectedBaud = Serial2.baudRate();

  if (detectedBaud == 0) {
    Serial.println("Baud rate detection failed.");
    Serial.println("Make sure the external device is sending data.");
  } else {
    Serial.print("Detected baud rate: ");
    Serial.println(detectedBaud);
  }
}

void loop() {
  while (Serial2.available()) {
    Serial.write(Serial2.read());
  }
}
</code></pre>
<p>This sketch uses:</p>
<pre><code class="language-cpp">Serial2.begin(0, SERIAL_8N1, RXD2, TXD2, false, 20000);
</code></pre>
<p>The first parameter is 0, which enables baud rate detection.</p>
<p>The last parameter, 20000, is the timeout in milliseconds. That means the ESP32 waits up to 20 seconds for incoming data.</p>
<hr />
<h2>ESP32 Serial2 Wiring</h2>
<p>For the example above, connect the external UART device like this:</p>
<table>
<thead>
<tr>
<th>ESP32 Pin</th>
<th>External UART Device</th>
</tr>
</thead>
<tbody>
<tr>
<td>GPIO16 RX2</td>
<td>TX</td>
</tr>
<tr>
<td>GPIO17 TX2</td>
<td>RX</td>
</tr>
<tr>
<td>GND</td>
<td>GND</td>
</tr>
</tbody>
</table>
<p>Make sure both devices share a common ground.</p>
<p>Also remember that the ESP32 uses 3.3V logic. If the external device uses 5V UART signals, use a logic level shifter or voltage divider before connecting to the ESP32 RX pin.</p>
<hr />
<h2>Example: ESP32 Autobaud Serial Bridge</h2>
<p>After detecting the baud rate, you can use the ESP32 as a simple USB-to-UART bridge.</p>
<pre><code class="language-cpp">#define RXD2 16
#define TXD2 17

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println();
  Serial.println("ESP32 Autobaud Serial Bridge");
  Serial.println("Waiting for external UART data...");

  Serial2.begin(0, SERIAL_8N1, RXD2, TXD2, false, 20000);

  uint32_t baud = Serial2.baudRate();

  if (baud == 0) {
    Serial.println("Autobaud failed. Restarting Serial2 at 9600.");
    Serial2.end();
    Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);
  } else {
    Serial.print("Detected baud rate: ");
    Serial.println(baud);
  }

  Serial.println("Bridge ready.");
}

void loop() {
  while (Serial.available()) {
    Serial2.write(Serial.read());
  }

  while (Serial2.available()) {
    Serial.write(Serial2.read());
  }
}
</code></pre>
<p>This is useful for testing UART modules. Once the ESP32 detects the baud rate, anything you type in the Serial Monitor is forwarded to the external device. Any response from the external device is printed back to the Serial Monitor.</p>
<hr />
<h2>Using ESP32 Autobaud with AT Command Modules</h2>
<p>Autobaud is useful for AT command modules because some modules may be configured to different baud rates.</p>
<p>Examples include:</p>
<ul>
<li>GSM modules</li>
<li>Bluetooth serial modules</li>
<li>Wi-Fi modem modules</li>
<li>LoRa UART modules</li>
<li>GPS modules with command interfaces</li>
</ul>
<p>After detecting the baud rate, you can send an AT command:</p>
<pre><code class="language-cpp">Serial2.println("AT");
</code></pre>
<p>Then read the response:</p>
<pre><code class="language-cpp">if (Serial2.available()) {
  String response = Serial2.readString();
  Serial.println(response);
}
</code></pre>
<p>If the module replies with:</p>
<pre><code class="language-text">OK
</code></pre>
<p>then your wiring and baud rate are likely correct.</p>
<hr />
<h2>Why Autobaud Detection May Fail</h2>
<p>Autobaud detection needs incoming UART data. If the external device is silent during the timeout period, the ESP32 will not be able to detect the baud rate.</p>
<p>Detection may fail if:</p>
<ul>
<li>The connected device is not sending data</li>
<li>RX and TX are wired incorrectly</li>
<li>GND is not connected</li>
<li>The UART signal level is wrong</li>
<li>The selected ESP32 chip does not support autobaud</li>
<li>The baud rate is outside the supported detection range</li>
<li>The signal is noisy or inverted</li>
</ul>
<p>For testing, use a device that repeatedly sends text data.</p>
<hr />
<h2>Fallback Method for ESP32-C3, ESP32-S3, and Other Chips</h2>
<p>If your ESP32 chip does not support Serial.begin(0) autobaud detection, you can still make a software-style workaround by scanning common baud rates.</p>
<p>This is not true hardware autobaud, but it can work if the device sends readable text.</p>
<pre><code class="language-cpp">#define RXD2 16
#define TXD2 17

const long baudRates[] = {
  9600,
  19200,
  38400,
  57600,
  115200,
  230400
};

const int baudCount = sizeof(baudRates) / sizeof(baudRates[0]);

bool looksReadable(String data) {
  int readable = 0;

  for (int i = 0; i &lt; data.length(); i++) {
    char c = data[i];

    if ((c &gt;= 32 &amp;&amp; c &lt;= 126) || c == '\r' || c == '\n') {
      readable++;
    }
  }

  return data.length() &gt; 0 &amp;&amp; readable &gt; data.length() * 0.8;
}

long scanBaudRate() {
  for (int i = 0; i &lt; baudCount; i++) {
    long baud = baudRates[i];

    Serial.print("Testing ");
    Serial.println(baud);

    Serial2.end();
    delay(100);
    Serial2.begin(baud, SERIAL_8N1, RXD2, TXD2);
    delay(300);

    String sample = "";
    unsigned long start = millis();

    while (millis() - start &lt; 1000) {
      while (Serial2.available()) {
        sample += (char)Serial2.read();
      }
    }

    if (looksReadable(sample)) {
      return baud;
    }
  }

  return -1;
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("Software baud scan fallback example");

  long baud = scanBaudRate();

  if (baud &gt; 0) {
    Serial.print("Likely baud rate: ");
    Serial.println(baud);

    Serial2.end();
    Serial2.begin(baud, SERIAL_8N1, RXD2, TXD2);
  } else {
    Serial.println("Could not detect baud rate.");
  }
}

void loop() {
  while (Serial2.available()) {
    Serial.write(Serial2.read());
  }
}
</code></pre>
<p>Use this fallback method only when the hardware autobaud feature is not available.</p>
<hr />
<h2>Hardware Autobaud vs Baud Rate Scanning</h2>
<table>
<thead>
<tr>
<th>Method</th>
<th>Best For</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td>Serial.begin(0)</td>
<td>ESP32 and ESP32-S2</td>
<td>Uses Arduino-ESP32 baud detection</td>
</tr>
<tr>
<td>Manual baud scanning</td>
<td>ESP32-C3, ESP32-S3, and other chips</td>
<td>Software workaround</td>
</tr>
<tr>
<td>Fixed baud rate</td>
<td>Known devices</td>
<td>Most reliable option</td>
</tr>
</tbody>
</table>
<p>If you already know the baud rate, use a fixed baud rate. Autobaud is mainly useful when the baud rate is unknown.</p>
<hr />
<h2>Final Thoughts</h2>
<p>This Arduino ESP32 autobaud example shows how to detect the UART baud rate automatically using the Arduino-ESP32 Serial API.</p>
<p>On supported chips, such as ESP32 and ESP32-S2, you can call:</p>
<pre><code class="language-cpp">Serial.begin(0);
</code></pre>
<p>or:</p>
<pre><code class="language-cpp">Serial2.begin(0, SERIAL_8N1, RXD2, TXD2);
</code></pre>
<p>Then read the detected baud rate using:</p>
<pre><code class="language-cpp">Serial.baudRate();
</code></pre>
<p>For unsupported ESP32 variants, such as ESP32-C3 or ESP32-S3, use a manual baud-rate scanning method instead.</p>
<p>Autobaud is not needed for every project, but it is very useful when debugging unknown UART devices or building flexible ESP32 serial tools.</p>
<p>The post <a href="https://www.teachmemicro.com/arduino-esp32-autobaud-example-detect-uart-baud-rate-automatically/">Arduino ESP32 Autobaud Example: Detect UART Baud Rate Automatically</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Solving BLE and Wi-Fi Concurrency on ESP32 Using FreeRTOS</title>
		<link>https://www.teachmemicro.com/solving-ble-and-wi-fi-concurrency-on-esp32-using-freertos/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=solving-ble-and-wi-fi-concurrency-on-esp32-using-freertos</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Mon, 26 Jan 2026 23:30:45 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=10454</guid>

					<description><![CDATA[<p>Running Bluetooth Low Energy (BLE) scanning and Wi-Fi networking at the same time on an ESP32 sounds straightforward—until it isn’t. Many developers discover that once BLE scanning starts, their Wi-Fi web server becomes unreliable. Pages load intermittently, HTTP requests time out, or BLE scans suddenly stop when Wi-Fi traffic increases. In this article, we’ll walk &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/solving-ble-and-wi-fi-concurrency-on-esp32-using-freertos/">Solving BLE and Wi-Fi Concurrency on ESP32 Using FreeRTOS</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Running <strong>Bluetooth Low Energy (BLE) scanning</strong> and <strong>Wi-Fi networking</strong> at the same time on an ESP32 sounds straightforward—until it isn’t.</p>
<p>Many developers discover that once BLE scanning starts, their Wi-Fi web server becomes unreliable. Pages load intermittently, HTTP requests time out, or BLE scans suddenly stop when Wi-Fi traffic increases.</p>
<p>In this article, we’ll walk through <strong>why this happens</strong> and <strong>how using FreeRTOS (RTOS) on the ESP32 cleanly solves the concurrency problem</strong>.</p>
<p><span id="more-10454"></span></p>
<hr />
<h3>The Core Problem</h3>
<p>On ESP32, <strong>BLE and Wi-Fi share critical hardware resources</strong>:</p>
<ul>
<li>The same radio</li>
<li>The same CPU</li>
<li>The same event loop when using blocking APIs</li>
</ul>
<p>A naïve implementation often looks like this:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr">
<pre><code class="whitespace-pre! language-cpp">void loop() {
  bleScan();        // blocks
  handleHttp();     // blocks
}
</code></pre>
</div>
</div>
<p>This design <em>appears</em> to work at first, but quickly degrades under real usage.</p>
<h4>Typical Symptoms</h4>
<ul>
<li>BLE scanning pauses while HTTP requests are active</li>
<li>The web server becomes unreachable during scans</li>
<li>Random socket resets</li>
<li>BLE callbacks fire late or not at all</li>
</ul>
<p>These issues are <strong>not bugs in BLE or Wi-Fi</strong>—they are architectural problems.</p>
<hr />
<h3>Why Delays and Timers Don’t Fix It</h3>
<p>A common attempt is to add <em>delay() </em>calls:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr">
<pre><code class="whitespace-pre! language-cpp">bleScan();
delay(100);
</code></pre>
</div>
</div>
<p>Unfortunately:</p>
<ul>
<li><em>delay() </em>blocks the current task</li>
<li>BLE callbacks still execute in the same context</li>
<li>Wi-Fi servicing becomes opportunistic</li>
</ul>
<p>This approach does not guarantee fairness between BLE and Wi-Fi.</p>
<blockquote><p><strong>The problem is not timing — it’s task isolation.</strong></p></blockquote>
<hr />
<h3>The ESP32 Advantage: Built-in FreeRTOS</h3>
<p>Unlike simpler microcontrollers, the ESP32 runs <strong>FreeRTOS natively</strong>, offering:</p>
<ul>
<li>Preemptive multitasking</li>
<li>Two CPU cores</li>
<li>Task prioritization</li>
<li>Core pinning</li>
</ul>
<p>Instead of fighting the scheduler, we can <strong>design with it</strong>.</p>
<hr />
<h3>High-Level Design</h3>
<p>The solution is to split responsibilities into <strong>independent RTOS tasks</strong>, each with a single purpose.</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2"></div>
</div>
<div class="overflow-y-auto p-4" dir="ltr">
<pre><a href="https://www.teachmemicro.com/wp-content/uploads/2026/01/ble-wifi-concurrency.avif"><img data-dominant-color="dbd6da" data-has-transparency="false" style="--dominant-color: #dbd6da;" loading="lazy" decoding="async" class="aligncenter wp-image-10455 size-large not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2026/01/ble-wifi-concurrency-1024x571.avif" alt="BLE Wifi ESP32 Concurrency" width="618" height="345" srcset="https://www.teachmemicro.com/wp-content/uploads/2026/01/ble-wifi-concurrency-1024x571.avif 1024w, https://www.teachmemicro.com/wp-content/uploads/2026/01/ble-wifi-concurrency-300x167.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2026/01/ble-wifi-concurrency-768x428.avif 768w, https://www.teachmemicro.com/wp-content/uploads/2026/01/ble-wifi-concurrency.avif 1409w" sizes="auto, (max-width: 618px) 100vw, 618px" /></a></pre>
</div>
</div>
<p>Each subsystem runs independently, coordinated only through minimal shared state.</p>
<hr />
<h3>Task Responsibilities</h3>
<h4>BLE Task (Core 0)</h4>
<p>The BLE task is responsible for:</p>
<ul>
<li>Running short, non-blocking BLE scans</li>
<li>Parsing advertisement payloads</li>
<li>Updating shared state when new data arrives</li>
</ul>
<p>Example:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr">
<pre><code class="whitespace-pre! language-cpp">void bleTask(void* param) {
  for (;;) {
    startBleScan();
    updateSharedState();
    vTaskDelay(pdMS_TO_TICKS(100));
  }
}
</code></pre>
</div>
</div>
<p>Key characteristics:</p>
<ul>
<li>No networking code</li>
<li>No JSON formatting</li>
<li>Frequent yielding to the scheduler</li>
</ul>
<hr />
<h4>HTTP Task (Core 1)</h4>
<p>The HTTP task handles all networking:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr">
<pre><code class="whitespace-pre! language-cpp">void httpTask(void* param) {
  server.begin();

  for (;;) {
    WiFiClient client = server.available();
    if (client) {
      handleRequest(client);
      client.stop();
    }
    vTaskDelay(pdMS_TO_TICKS(10));
  }
}
</code></pre>
</div>
</div>
<p>This task remains responsive regardless of BLE activity.</p>
<hr />
<h3>Shared State: Keep It Simple</h3>
<p>Shared data between tasks is intentionally minimal:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr">
<pre><code class="whitespace-pre! language-cpp">char lastData[32];
int lastSignalStrength;
volatile bool hasData;
volatile bool hasNewData;
unsigned long lastSeenMillis;
</code></pre>
</div>
</div>
<p>Why this works safely:</p>
<ul>
<li>Single-writer pattern</li>
<li>No dynamic allocation</li>
<li>No STL containers</li>
<li>Short critical sections</li>
<li><em>volatile</em> ensures cross-core visibility</li>
</ul>
<p>This approach avoids the need for mutexes while maintaining robustness.</p>
<hr />
<h3>Handling Data Expiration (TTL)</h3>
<p>To prevent stale data from being served indefinitely, we added a <strong>time-to-live (TTL)</strong> mechanism:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr">
<pre><code class="whitespace-pre! language-cpp">if (hasData &amp;&amp; millis() - lastSeenMillis &gt; DATA_TTL_MS) {
  hasData = false;
  hasNewData = false;
}
</code></pre>
</div>
</div>
<p>This mirrors real-world scanning behavior:</p>
<ul>
<li>Data appears</li>
<li>Data ages out</li>
<li>Clients stop receiving old results</li>
</ul>
<hr />
<h3>Why This Architecture Works</h3>
<h4>1. True Concurrency</h4>
<p>BLE scanning and Wi-Fi networking never block each other.</p>
<h4>2. Predictable Scheduling</h4>
<p>FreeRTOS ensures both tasks get CPU time when needed.</p>
<h4>3. Improved Stability</h4>
<p>No more random disconnects or missed scans.</p>
<h4>4. Easy Scalability</h4>
<p>Additional tasks (MQTT, OTA, logging) can be added cleanly.</p>
<hr />
<h3>Results</h3>
<p>After moving to an RTOS-based design:</p>
<ul>
<li>BLE scanning runs continuously</li>
<li>The web server is always reachable</li>
<li>No socket resets under load</li>
<li>No missed BLE advertisements</li>
<li>System behavior is deterministic and production-ready</li>
</ul>
<hr />
<h3>Key Takeaways</h3>
<ul>
<li>BLE + Wi-Fi issues on ESP32 are <strong>architectural</strong>, not library bugs</li>
<li>Blocking code in <em>loop()</em> does not scale</li>
<li>FreeRTOS is not optional for serious ESP32 applications</li>
<li>Separate concerns into dedicated tasks</li>
<li>Keep shared state small and explicit</li>
</ul>
<hr />
<h3>Final Thoughts</h3>
<p>If you’re building an ESP32 application that combines BLE scanning and Wi-Fi networking, <strong>embracing FreeRTOS is the turning point</strong> between a fragile prototype and a reliable system.</p>
<p>Once tasks are isolated and scheduled correctly, the ESP32 becomes an extremely capable concurrent platform.</p>
<p>The post <a href="https://www.teachmemicro.com/solving-ble-and-wi-fi-concurrency-on-esp32-using-freertos/">Solving BLE and Wi-Fi Concurrency on ESP32 Using FreeRTOS</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Send ESP32 Sensor Data to Google Sheets (2026) </title>
		<link>https://www.teachmemicro.com/how-to-send-esp32-sensor-data-to-google-sheets-2026/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-send-esp32-sensor-data-to-google-sheets-2026</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 30 Dec 2025 01:00:39 +0000</pubDate>
				<category><![CDATA[ESP32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=10289</guid>

					<description><![CDATA[<p>In this tutorial, I’ll show you exactly how I send sensor data from an ESP32 to Google Sheets every hour. This setup is: Free Reliable No Firebase No OAuth No paid APIs If you just want a clean Google Sheet logging your ESP32 sensor data, this is one of the simplest and most stable methods &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-send-esp32-sensor-data-to-google-sheets-2026/">How to Send ESP32 Sensor Data to Google Sheets (2026) </a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<div class="no-scrollbar flex min-h-36 flex-nowrap gap-0.5 overflow-auto sm:gap-1 sm:overflow-hidden xl:min-h-44 mt-1 mb-5 [&amp;:not(:first-child)]:mt-4">
<div class="border-token-border-default relative w-32 shrink-0 overflow-hidden rounded-xl border-[0.5px] md:shrink max-h-64 sm:w-[calc((100%-0.5rem)/3)] rounded-s-xl">In this tutorial, I’ll show you <strong>exactly how I send sensor data from an ESP32 to Google Sheets every hour</strong>.</div>
<div></div>
</div>
<p><span id="more-10289"></span></p>
<p>This setup is:</p>
<ul>
<li><strong>Free</strong></li>
<li><strong>Reliable</strong></li>
<li><strong>No Firebase</strong></li>
<li><strong>No OAuth</strong></li>
<li><strong>No paid APIs</strong></li>
</ul>
<p>If you just want a clean Google Sheet logging your ESP32 sensor data, this is one of the simplest and most stable methods available today.</p>
<hr />
<h3><strong>How This Works</strong></h3>
<p>Here’s the flow I’m using:</p>
<ol>
<li>ESP32 reads sensor data</li>
<li>ESP32 connects to Wi-Fi</li>
<li>Every <strong>1 hour</strong>, ESP32 sends an <strong>HTTP POST request</strong></li>
<li>Google Apps Script receives the data</li>
<li>The data is appended to Google Sheets</li>
</ol>
<p>That’s it.</p>
<hr />
<h3><strong>What I’m Using</strong></h3>
<h4><strong>Hardware</strong></h4>
<ul>
<li>ESP32 (any variant works)</li>
<li>Any sensor (DHT22, DS18B20, analog sensor, etc.)</li>
</ul>
<h4><strong>Software &amp; Accounts</strong></h4>
<ul>
<li>Arduino IDE</li>
<li>Google account</li>
<li>Internet connection</li>
</ul>
<hr />
<h3><strong>Step 1: Create the Google Sheet</strong></h3>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-create.avif"><img data-dominant-color="f5f6f7" data-has-transparency="false" style="--dominant-color: #f5f6f7;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-10290 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-create.avif" alt="ESP32 Google Sheets Create" width="661" height="382" srcset="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-create.avif 661w, https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-create-300x173.avif 300w" sizes="auto, (max-width: 661px) 100vw, 661px" /></a></p>
<div class="no-scrollbar flex min-h-36 flex-nowrap gap-0.5 overflow-auto sm:gap-1 sm:overflow-hidden xl:min-h-44 mt-1 mb-5 [&amp;:not(:first-child)]:mt-4">
<div class="border-token-border-default relative w-32 shrink-0 overflow-hidden rounded-xl border-[0.5px] md:shrink max-h-64 sm:w-[calc((100%-0.5rem)/3)] rounded-e-xl">First, I create a new Google Sheet.</div>
</div>
<ol>
<li>Go to <strong>Google Sheets</strong></li>
<li>Click <strong>Blank</strong></li>
<li>Rename the file (I use something like <em>ESP32 Sensor Log</em>)</li>
<li>Add these headers in <strong>Row 1</strong>:  <em>Timestamp | Temperature | Humidity | Device_ID</em></li>
</ol>
<p>You can add more columns later if you want more sensors.</p>
<hr />
<h3><strong>Step 2: Create the Google Apps Script</strong></h3>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-add-script.avif"><img data-dominant-color="f5f6f7" data-has-transparency="false" style="--dominant-color: #f5f6f7;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-10291 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-add-script.avif" alt="ESP32 Google Sheets Add Script" width="773" height="307" srcset="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-add-script.avif 773w, https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-add-script-300x119.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-sheets-add-script-768x305.avif 768w" sizes="auto, (max-width: 773px) 100vw, 773px" /></a></p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-script.avif"><img data-dominant-color="f6f6f8" data-has-transparency="false" style="--dominant-color: #f6f6f8;" loading="lazy" decoding="async" class="aligncenter size-full wp-image-10292 not-transparent" src="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-script.avif" alt="ESP32 Google Script" width="956" height="623" srcset="https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-script.avif 956w, https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-script-300x196.avif 300w, https://www.teachmemicro.com/wp-content/uploads/2025/12/ESP32-google-script-768x500.avif 768w" sizes="auto, (max-width: 956px) 100vw, 956px" /></a></p>
<div class="no-scrollbar flex min-h-36 flex-nowrap gap-0.5 overflow-auto sm:gap-1 sm:overflow-hidden xl:min-h-44 mt-1 mb-5 [&amp;:not(:first-child)]:mt-4">
<div class="border-token-border-default relative w-32 shrink-0 overflow-hidden rounded-xl border-[0.5px] md:shrink max-h-64 sm:w-[calc((100%-0.5rem)/3)] rounded-e-xl">This script will receive data from the ESP32.</div>
</div>
<ol>
<li>Inside your Google Sheet, click<br />
<strong>Extensions → Apps Script</strong></li>
<li>Delete everything inside the editor</li>
<li>Paste this code:
<div class="overflow-y-auto p-4" dir="ltr">
<pre><code class="whitespace-pre! language-javascript">function doPost(e) {
  try {
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    const data = JSON.parse(e.postData.contents);

    sheet.appendRow([
      new Date(),
      data.temperature,
      data.humidity,
      data.device_id
    ]);

    return ContentService
      .createTextOutput("OK")
      .setMimeType(ContentService.MimeType.TEXT);

  } catch (err) {
    return ContentService
      .createTextOutput("ERROR: " + err)
      .setMimeType(ContentService.MimeType.TEXT);
  }
}
</code></pre>
</div>
</li>
</ol>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">
<div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"></div>
</div>
</div>
</div>
<ol start="4">
<li>Click <strong>Save</strong></li>
<li>Name the project (I usually call it <em>ESP32 Sensor Log</em> )</li>
</ol>
<hr />
<h3><strong>Step 3: Deploy the Script as a Web App</strong></h3>
<div class="no-scrollbar flex min-h-36 flex-nowrap gap-0.5 overflow-auto sm:gap-1 sm:overflow-hidden xl:min-h-44 mt-1 mb-5 [&amp;:not(:first-child)]:mt-4">
<div class="border-token-border-default relative w-32 shrink-0 overflow-hidden rounded-xl border-[0.5px] md:shrink max-h-64 sm:w-[calc((100%-0.5rem)/3)] rounded-e-xl">This step is critical.</div>
</div>
<ol>
<li>Click <strong>Deploy → New deployment</strong></li>
<li>Select <strong>Web app</strong></li>
<li>Set the options:
<ul>
<li><strong>Execute as:</strong> Me</li>
<li><strong>Who has access:</strong> Anyone</li>
</ul>
</li>
<li>Click <strong>Deploy</strong></li>
<li>Authorize permissions</li>
<li>Copy the <strong>Web App URL</strong></li>
</ol>
<p>I’ll use this URL in the ESP32 code.</p>
<hr />
<h3><strong>Step 4: ESP32 Arduino Code (Hourly Upload)</strong></h3>
<h4><strong>Libraries I’m Using</strong></h4>
<p>These are already included with ESP32 Arduino core:</p>
<ul>
<li><em>WiFi.h</em></li>
<li><em>HTTPClient.h</em></li>
</ul>
<hr />
<h4><strong>Example ESP32 Code</strong></h4>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="overflow-y-auto p-4" dir="ltr">
<pre><pre><code class="language-cpp">#include &lt;WiFi.h&gt;
#include &lt;HTTPClient.h&gt;

const char* ssid = &quot;YOUR_WIFI_NAME&quot;;
const char* password = &quot;YOUR_WIFI_PASSWORD&quot;;

const char* serverUrl =
  &quot;https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec&quot;;

unsigned long lastSend = 0;
const unsigned long interval = 3600000; // 1 hour

void setup() {
  Serial.begin(115200);

  WiFi.begin(ssid, password);
  Serial.print(&quot;Connecting to WiFi&quot;);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(&quot;.&quot;);
  }

  Serial.println(&quot;\nWiFi connected&quot;);
}

void loop() {
  if (millis() - lastSend &gt;= interval) {
    lastSend = millis();
    sendData();
  }
}

void sendData() {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(serverUrl);
    http.addHeader(&quot;Content-Type&quot;, &quot;application/json&quot;);

    float temperature = 25.4; // replace with sensor read
    float humidity    = 60.2; // replace with sensor read

    String json =
      &quot;{&quot;
      &quot;\&quot;temperature\&quot;:&quot; + String(temperature) + &quot;,&quot;
      &quot;\&quot;humidity\&quot;:&quot; + String(humidity) + &quot;,&quot;
      &quot;\&quot;device_id\&quot;:\&quot;ESP32_01\&quot;&quot;
      &quot;}&quot;;

    int responseCode = http.POST(json);
    Serial.println(&quot;HTTP Response: &quot; + String(responseCode));

    http.end();
  }
}</code></pre></pre>
</div>
<hr />
<h3><strong>Step 5: Check Google Sheets</strong></h3>
<div class="no-scrollbar flex min-h-36 flex-nowrap gap-0.5 overflow-auto sm:gap-1 sm:overflow-hidden xl:min-h-44 mt-1 mb-5 [&amp;:not(:first-child)]:mt-4">
<div class="border-token-border-default relative w-32 shrink-0 overflow-hidden rounded-xl border-[0.5px] md:shrink max-h-64 sm:w-[calc((100%-0.5rem)/3)] rounded-s-xl"></div>
<div class="border-token-border-default relative w-32 shrink-0 overflow-hidden rounded-xl border-[0.5px] md:shrink max-h-64 sm:w-[calc((100%-0.5rem)/3)] rounded-e-xl">Once the ESP32 sends data:</div>
</div>
<ul>
<li>A <strong>new row appears</strong></li>
<li>Timestamp is automatic</li>
<li>Sensor values are logged</li>
</ul>
<p>For testing, I usually reduce the interval first.</p>
<hr />
<h3><strong>Development Tip: Faster Testing</strong></h3>
<p>Change this:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">
<div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"></div>
</div>
</div>
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-cpp">const unsigned long interval = 10000; // 10 seconds<br />
</code></div>
</div>
<p>&nbsp;</p>
<p>Once everything works, change it back to:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">
<div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"></div>
</div>
</div>
<div class="overflow-y-auto p-4" dir="ltr"><code class="whitespace-pre! language-cpp">3600000 // 1 hour<br />
</code></div>
</div>
<hr />
<h3><strong>Common Problems I Encountered</strong></h3>
<h4><strong>ESP32 returns -1</strong></h4>
<ul>
<li>Wi-Fi not connected</li>
<li>Wrong Web App URL</li>
<li>HTTPS blocked</li>
</ul>
<h4><strong>Google Sheet stays empty</strong></h4>
<ul>
<li>Field names don’t match JSON</li>
<li>Forgot to redeploy script</li>
</ul>
<h4><strong>Permission errors</strong></h4>
<ul>
<li>Web App access not set to <strong>Anyone</strong></li>
</ul>
<hr />
<h3><strong>Optional: Battery-Powered Hourly Logging</strong></h3>
<p>If I’m running on battery, I use deep sleep:</p>
<div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary">
<div class="sticky top-[calc(--spacing(9)+var(--header-height))] @w-xl/main:top-9">
<div class="absolute end-0 bottom-0 flex h-9 items-center pe-2">
<div class="bg-token-bg-elevated-secondary text-token-text-secondary flex items-center gap-4 rounded-sm px-2 font-sans text-xs"></div>
</div>
</div>
<div class="overflow-y-auto p-4" dir="ltr">
<pre><code class="whitespace-pre! language-cpp">esp_sleep_enable_timer_wakeup(3600ULL * 1000000ULL);
esp_deep_sleep_start();
</code></pre>
</div>
</div>
<p>This wakes the ESP32 every hour, sends data, then sleeps again.</p>
<hr />
<h3><strong>Final Thoughts</strong></h3>
<p>This method has been extremely reliable for me:</p>
<ul>
<li>No third-party services</li>
<li>No monthly fees</li>
<li>Easy to debug</li>
<li>Scales well for multiple ESP32 devices</li>
</ul>
</div>
<p>The post <a href="https://www.teachmemicro.com/how-to-send-esp32-sensor-data-to-google-sheets-2026/">How to Send ESP32 Sensor Data to Google Sheets (2026) </a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</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-07-28 11:16:53 by W3 Total Cache
-->