RisalDash RisalDash

Guide · ESP32 · Tips

5 things your ESP32 already does

Most people use an ESP32 like a small Arduino with Wi-Fi and never touch the rest. But five powerful features are already built into the chip — no extra hardware, no libraries to hunt down. Here’s what each one is, why it’s worth using, and a copy-paste code example for all five.

ESP32ESP-NOWDeep sleepOTAPWM
Five hidden ESP32 features: ESP-NOW, touch GPIO, deep sleep, OTA and PWM
All five ship inside the chip — you just have to switch them on.

1. ESP-NOW — talk to another ESP32 without a router

ESP-NOW lets two (or many) ESP boards exchange small packets directly, with no Wi-Fi router and no internet — just their MAC addresses. It’s fast (~1 ms), low-power, and perfect for remotes, sensor nodes and robot-to-robot links. It rides on the Wi-Fi radio but skips the whole TCP/IP stack; see where it sits next to Wi-Fi and BLE in the protocols guide.

ESP-NOW: a sender ESP32 talking directly to a receiver ESP32, no Wi-Fi router
#include <esp_now.h>
#include <WiFi.h>

uint8_t peer[] = {0x24, 0x6F, 0x28, 0xAA, 0xBB, 0xCC};  // receiver's MAC
typedef struct { int id; float temp; } Msg;
Msg data;

void setup() {
  WiFi.mode(WIFI_STA);
  esp_now_init();
  esp_now_peer_info_t p = {};
  memcpy(p.peer_addr, peer, 6);
  esp_now_add_peer(&p);
}

void loop() {
  data.id = 1; data.temp = 24.5;
  esp_now_send(peer, (uint8_t *)&data, sizeof(data));  // no router, ~1 ms
  delay(2000);
}

2. Capacitive touch — any pad becomes a button

The classic ESP32 has 10 touch-capable GPIOs. Wire a pin to a pad of copper, foil, or a screw head and touchRead() returns a number that drops when a finger is near — no button, no extra chip. Great for sleek panels, hidden controls and wake-on-touch. (Note: the S2/S3 use a slightly different touch API; the plain ESP32 uses touchRead(T0..T9).)

Capacitive touch: touchRead drops from ~72 to ~18 when a finger touches the pad
void setup() {
  Serial.begin(115200);
}

void loop() {
  int v = touchRead(T0);            // T0 is GPIO4 on the classic ESP32
  if (v < 40) Serial.println("touched!");   // lower = a finger is there
  delay(100);
}

3. Deep sleep — run for months on a battery

Between bursts of work, an ESP32 can drop to deep sleep at ~10 µA and wake on a timer or a pin. That’s the difference between a battery node lasting a day and lasting a year. The catch: on wake the chip reboots and setup() runs again, so you save state in RTC memory. Full details in the deep sleep guide.

Deep sleep current vs time: brief active spikes, long low-power valleys
#define uS_PER_S 1000000ULL

void setup() {
  Serial.begin(115200);
  Serial.println("awake — do the work, then sleep");
  // ... read a sensor, send it ...
  esp_sleep_enable_timer_wakeup(10 * uS_PER_S);  // wake in 10 seconds
  esp_deep_sleep_start();                        // ~10 µA until then
}

void loop() {}   // never runs — setup() runs again on each wake

4. OTA — update firmware over the air

Once a board is on the wall you don’t want to unplug it to reflash. OTA pushes new firmware over Wi-Fi: add a few lines and a network port appears in the Arduino IDE. It’s a must for deployed devices — and if you want it truly one-line, a RisalDash dashboard exposes an “Update firmware” button with dash.enableOTA().

OTA: a PC pushes a .bin over Wi-Fi to an ESP32 on the wall, no USB
#include <WiFi.h>
#include <ArduinoOTA.h>

void setup() {
  WiFi.begin("your-ssid", "your-pass");
  while (WiFi.status() != WL_CONNECTED) delay(300);
  ArduinoOTA.begin();          // the board is now flashable over Wi-Fi
}

void loop() {
  ArduinoOTA.handle();         // pick the network port in the IDE
}

5. PWM on (almost) any pin — brightness, speed, servos

The ESP32’s LEDC peripheral generates hardware PWM on almost any GPIO — dim an LED, set a motor’s speed, or position a servo, all without blocking the CPU. You pick a pin, a frequency and a resolution, then just write a duty value. On Arduino-ESP32 core 3.x it’s the tidy ledcAttach() API below (older 2.x used ledcSetup() + ledcAttachPin()).

PWM duty cycle vs LED brightness: 0%, 50% and 100%
void setup() {
  ledcAttach(18, 5000, 8);     // pin 18, 5 kHz, 8-bit (0..255)  [core 3.x]
}

void loop() {
  for (int d = 0; d <= 255; d++) { ledcWrite(18, d); delay(4); }  // fade up
  for (int d = 255; d >= 0; d--) { ledcWrite(18, d); delay(4); }  // fade down
}

Put them together

None of these need a shield or a shopping list — they’re silicon you already own. A battery sensor that touch-wakes, reads, fires the value over ESP-NOW, and drops back to deep sleep — updated in the field by OTA, with a PWM status LED — is five “hidden” features and zero extra parts. That’s the point: your ESP32 can do far more than blink.

Want a UI on top? A full live dashboard — with OTA built in — in a few lines of C++.