Environment setup
2026-03-16Development is done on the k3s control-plane RPi (raspberrypi, 192.168.100.1) via VS Code Remote SSH. The ESP32 is connected via USB to that same node.
Directory layout
~/esp32/ ├── projects/ │ ├── blink/ │ └── wifi-test/ ├── lib/ # reusable MicroPython modules ├── firmware/ # .bin firmware files ├── scripts/ # host-side helpers └── venv/ # esptool + mpremote
Tooling
tmux session
Added esp32 session to ~/shbin/tmux-init.sh. Activates the venv automatically on attach.
create_session esp32 "cd $HOME/esp32 && source $HOME/esp32/venv/bin/activate"
Deploy script
# scripts/deploy.sh mpremote connect $PORT cp ~/esp32/projects/$PROJECT/main.py :main.py mpremote connect $PORT reset
Board identification
2026-03-16Firmware
Flash commands
esptool --chip esp32 --port /dev/ttyUSB0 erase-flash esptool --chip esp32 --port /dev/ttyUSB0 --baud 460800 \ write-flash -z 0x1000 ESP32_GENERIC-20241129-v1.24.1.bin
Project: blink
2026-03-16First MicroPython project. Blinks the onboard RGB NeoPixel LED red at 0.5s intervals.
Code
from machine import Pin import neopixel import time np = neopixel.NeoPixel(Pin(16), 1) while True: np[0] = (255, 0, 0) # Red np.write() time.sleep(0.5) np[0] = (0, 0, 0) # Off np.write() time.sleep(0.5)
Run
mpremote run ~/esp32/projects/blink/main.py
Project: wifi-test + Telegram
2026-03-16Connects to WiFi and sends a Telegram notification via the /notify/telegram endpoint on api.rforssen.net. The ESP32 never holds a Telegram token — it calls the internal API instead.
Gotchas discovered
The API expects field text, not message. The urequests.post() data parameter must be bytes, not a plain string — encode with .encode("utf-8").
main.py
import network, time, urequests, json SSID = "otraFruta" PASSWORD = "***" API = "https://api.rforssen.net/notify/telegram" def connect_wifi(): wlan = network.WLAN(network.STA_IF) wlan.active(True) wlan.connect(SSID, PASSWORD) while not wlan.isconnected(): time.sleep(0.5) print("IP:", wlan.ifconfig()[0]) def send_telegram(text): payload = json.dumps({"text": text}).encode("utf-8") headers = {"Content-Type": "application/json"} r = urequests.post(API, data=payload, headers=headers) r.close() connect_wifi() send_telegram("Hello from ESP32!")
Reusable library modules
- ~/esp32/lib/wifi.py — connect(ssid, password) → wlan
- ~/esp32/lib/telegram.py — send(text) via api.rforssen.net
Usage in any project
import wifi import telegram wifi.connect("otraFruta", "your-password") telegram.send("Hello from ESP32!")
WLED installation
2026-03-16WLED is a ready-made firmware for ESP32 that provides a full web UI, app control, and 100+ effects for addressable LED strips (WS2812B/NeoPixel). No coding required — the ESP32 becomes a dedicated LED controller.
Download firmware
cd ~/esp32/firmware wget https://github.com/wled/WLED/releases/download/v0.15.0/WLED_0.15.0_ESP32.bin
Flash
WLED firmware is flashed at 0x10000 on top of an existing bootloader. The MicroPython bootloader at 0x1000 is compatible — no erase needed.
esptool --chip esp32 --port /dev/ttyUSB0 --baud 460800 \
write-flash 0x10000 WLED_0.15.0_ESP32.bin
First boot
On first boot WLED creates a WiFi AP called WLED-AP (password: wled1234). Connect to it, enter your home WiFi credentials, save. The device reboots and joins your network.
LED configuration
In the WLED web UI go to Config → LED Preferences and set the data pin to 16. Set the LED type to WS2812B and configure the number of LEDs in your strip.
OTA updates
Once running, future firmware updates can be done via OTA directly from the WLED web UI — no USB cable needed.