Skip to content

HamzaYslmn/python-esp-bridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

python-esp-bridge

Connect an ESP32 to a Raspberry Pi (or any PC) over USB or Bluetooth and drive every ESP32 peripheral live from Python — GPIO, PWM, ADC, DAC, capacitive touch, I2C, SPI, extra UARTs, RMT pulse trains (NeoPixels, IR, DHT, ultrasonic, steppers), 1-Wire, CAN bus, I2S audio, files (LittleFS/SD), NVS storage, deep sleep, Wi-Fi (including TCP/UDP sockets through the ESP32 radio), Ethernet, camera, BLE and ESP-NOW — plus firmware updates over the link itself. Flash the bridge firmware once; after that, everything is Python on the host. No reflashing per project.

The design rule: the firmware exposes minimal hardware primitives; device protocols (WS2812 timing, NEC IR, DHT decoding, 1-Wire search, stepper ramps) are implemented in Python where they are easy to read, test and extend.

┌────────────────┐  USB serial (≤921600 Bd) or BLE  ┌─────────────────────┐
│ Pi / PC        │ ───────────────────────────────► │ ESP32 (bridge fw)   │
│ Python:        │   binary protocol, COBS+CRC16    │ FreeRTOS tasks:     │
│  espbridge     │ ◄─────────────────────────────── │  tx / rx / network  │
└────────────────┘        replies + async events    └─────────────────────┘

Oled

Quick start

  1. Flash the firmware once — open firmware/firmware.ino in Arduino IDE (esp32 core 3.x, partition scheme Huge APP), hit Upload. Details: firmware/README.md.

  2. Install the Python library on the Pi/PC:

    pip install python-esp-bridge          # USB only
    pip install "python-esp-bridge[ble]"   # + Bluetooth support
  3. Go:

    from espbridge import Bridge
    
    with Bridge() as esp:                      # auto-detects the USB port
        print(esp.info)                        # chip, MAC, capabilities
    
        esp.gpio.mode(2, "output")             # like RPi GPIO, but on the ESP32
        esp.gpio.write(2, 1)
        print(esp.adc.read_mv(34), "mV")
        esp.dac.write(25, 128)                 # true analog out (classic ESP32)
        esp.pwm.servo(13, angle=90)
    
        esp.i2c.init(sda=21, scl=22)
        print([hex(a) for a in esp.i2c.scan()])
    
        esp.wifi.connect("ssid", "password")   # the ESP32's radio...
        status, body = esp.net.http_get("http://example.com/")  # ...as your modem

    Or with no USB cable at all — boards advertise as espbridge_<mac> (plus your custom name) and require a password (default espbridge, change it at the top of firmware.ino):

    with Bridge(ble=True, password="espbridge") as esp:   # over Bluetooth
        esp.gpio.write(2, 1)

    espbridge on the command line prints connection info; espbridge ports lists candidate serial ports; espbridge scan probes every attached board and espbridge scan --ble finds bridges advertising over Bluetooth.

Features

module highlights
GPIO modes incl. pull-up/down & open-drain, batch writes, edge interrupts with debounce → Python callbacks
ADC raw + calibrated mV, attenuation config (ADC2/Wi-Fi conflict guarded)
DAC 8-bit output + hardware cosine generator (classic ESP32 / S2)
PWM LEDC: any pin, freq/resolution, duty_pct, tone, servo
Touch capacitive touch pad reads
I2C 2 buses, scan, write/read, register helpers, repeated-start
SPI 2 hosts, full-duplex transfers, CS handling
UART UART1/2 bridged: write from Python, RX streamed back as events
Wi-Fi scan, STA join, AP mode, status/RSSI, state events
NET TCP client/server + UDP through the ESP32 radio, socket-like API, credit-window flow control
BLE scan, advertise, GATT server (notify/write callbacks), GATT client
ESP-NOW connectionless ESP32↔ESP32 messaging: peers + broadcast, delivery ACKs, RX with RSSI, PMK/LMK encryption — coexists with Wi-Fi and BLE
RMT generic pulse-train play/capture — the one primitive behind neopixel, ir, dht, hcsr04, stepper (below)
1-Wire bus primitives on any pin; ROM search + CRC8 in Python (esp.onewire, DS18B20 driver included)
CAN TWAI controller: 25k–1M bit/s, filters, send/recv + callbacks (esp.can; transceiver chip required)
I2S PCM in/out for MEMS mics & DACs/amps (esp.i2s; link bandwidth caps rates ~16-bit/32 kHz mono)
Files LittleFS on internal flash + SD cards: open/read/write/list/… (esp.fs)
NVS persistent key/value storage on the board (esp.nvs)
Sleep deep + light sleep with timer/GPIO wake (esp.deep_sleep(); see chip notes)
OTA reflash the firmware over USB or Bluetooth (esp.ota.flash("fw.bin"); dual-app partition scheme)
Ethernet RMII (WT32-ETH01, Olimex POE…) or SPI (W5500) — NET sockets ride it automatically (firmware opt-in)
Camera JPEG snapshots from ESP32-CAM / XIAO-S3-Sense / ESP-EYE (firmware opt-in, PSRAM)
MCPWM complementary PWM pair with hardware deadtime for H-bridges (esp.mcpwm; not on S2/C3)

Device drivers in pure Python (over the RMT/1-Wire primitives — no firmware changes to add your own):

from espbridge.neopixel import NeoPixel   # WS2812/SK6812 strips
from espbridge.dht import DHT             # DHT11/DHT22 temp+humidity
from espbridge.ds18b20 import DS18B20     # 1-Wire thermometers (multi-drop)
from espbridge.hcsr04 import HCSR04       # ultrasonic ranging
from espbridge.ir import IrSender, IrReceiver  # NEC remotes + raw IR
from espbridge.stepper import Stepper     # A4988/DRV8825 with ramps

NeoPixel(esp, pin=5, n=30).fill((0, 0, 64))
print(DHT(esp, 4).read())                 # (23.1, 65.5)
Stepper(esp, step_pin=12, dir_pin=14).move(400, speed=800, accel=1600)

The firmware is fully event-driven on FreeRTOS: serial TX, command handling and the network stack run as separate tasks, so a blocking Wi-Fi/BLE operation never delays a GPIO read (~1 ms round-trips at 921600 Bd).

Use the libraries you already know

espbridge speaks the wire protocols of the popular Python hardware ecosystems, so existing code, drivers and tutorials run unchanged — the ESP32's pins just take the place of the Pi's:

gpiozero — full pin factory (LED, Button, PWMLED, edge callbacks, …):

from gpiozero import LED, Button
from espbridge.compat.gpiozero import EspBridgeFactory

factory = EspBridgeFactory(esp)
led, btn = LED(2, pin_factory=factory), Button(4, pin_factory=factory)
btn.when_pressed = led.toggle

Adafruit CircuitPython drivers (hundreds of sensors/displays) — busio/digitalio-compatible I2C, SPI and DigitalInOut:

from adafruit_bme280.basic import Adafruit_BME280_I2C
from espbridge.compat.blinka import I2C

bme = Adafruit_BME280_I2C(I2C(esp))     # the driver doesn't know it's bridged
print(bme.temperature)

smbus2 — classic Pi I2C code, unchanged:

from espbridge.compat.smbus import SMBus
bus = SMBus(esp)                        # instead of smbus2.SMBus(1)
temp = bus.read_byte_data(0x48, 0x00)

luma.oled / luma.lcd — I2C and SPI display interfaces (LumaI2C, LumaSPI), RPi.GPIOespbridge.compat.rpi_gpio shim, and the native objects follow stdlib conventions too: UART ports are pyserial-like (in_waiting, readline), bridged TCP/UDP sockets support settimeout/recv/sendall.

I2C OLEDs (SSD1306 / SH1106 / the ubiquitous clones) are supported directly — pip install "python-esp-bridge[oled]", draw with PIL:

from espbridge.oled import OLED

oled = OLED(esp)                # bus init + auto-detect + clone-safe power-up
with oled.draw() as d:          # d is a PIL ImageDraw
    d.text((0, 10), "Hello!", fill="white")

Multiple ESP32s

Give each board a persistent name once (espbridge -p COM7 set-name relays — stored in the ESP32's flash, survives reboots and port renumbering), then:

import espbridge
from espbridge import Bridge

esp = Bridge(name="relays")                  # or Bridge(mac="aa:bb:cc:dd:ee:ff")

with espbridge.connect_all() as boards:    # or just open all of them
    boards.by_name("sensors").adc.read(34)
    boards.by_name("relays").gpio.write(2, 1)

Troubleshooting

Errors name the command and say what to check (I2C_WRITE (0x4003) failed: IO — no ACK on the wire — check wiring, power, device address and pull-ups). A timeout additionally pings the board so the message tells you whether the link itself died or a single frame got lost. Useful knobs:

esp = Bridge(retries=1)         # default: re-send safe commands once on timeout
esp.free_heap()                 # heap + dropped-frame counters from the firmware
ESPBRIDGE_DEBUG=1 python app.py   # trace every request/response with names

Lost frames on a busy link are also prevented now: pipelined bursts (OLED frames, NeoPixel updates) are automatically throttled to what the firmware's link buffer can absorb, on both USB serial and Bluetooth.

Repo layout

path what
firmware/ Arduino firmware (flash once; Bluetooth password lives at the top of firmware.ino)
src/ Python package python-esp-bridge (import espbridge; transports: USB serial + BLE)
examples/ grouped: basics/, devices/ (NeoPixel, DHT, DS18B20, HC-SR04, IR, stepper, CAN, I2S), system/ (files, NVS, deep sleep, OTA), wireless/, network/, displays/, compat/ (gpiozero, adafruit, luma, smbus, rpi_gpio)
tests/ hardware-free protocol/bridge tests (pytest tests/)
docs/PROTOCOL.md binary wire protocol spec (framing, transports, auth)

Supported hardware

Primary target: classic ESP32 DevKits (ESP-32S / ESP-32D, 30- and 38-pin, CP2102/CH340 USB). ESP32-S2/S3/C3 build via the same sketch (native USB; ESP-NOW works everywhere; no DAC on S3/C3). Capabilities are reported by the firmware at connect time, so the Python API fails fast with a clear error for anything your chip lacks.

Bluetooth note: arduino-esp32 core 3.3.x ships the NimBLE host on S3/C3/C6 — the bridge's Bluetooth code (BLE link + esp.ble) speaks Bluedroid, so on those chips the firmware currently builds USB-only. Classic ESP32 keeps Bluedroid: full BLE link + Wi-Fi + ESP-NOW coexistence.

Classic-ESP32 IRAM trade-off: with Wi-Fi + Bluetooth both loaded the chip's instruction RAM is full, so the default classic build skips SD-card support (LittleFS still works) and deep/light sleep. Build with BRIDGE_ENABLE_BLE 0 (USB-only) to get SD + sleep back; S2/S3/C3/C6 have everything regardless. The Python API raises a clear UnsupportedError either way (Cap.SLEEP, Cap.SDMMC probing).

Per-chip support matrix (v0.3.0 modules)

ESP32 S2 S3 C3 C6
RMT / 1-Wire / CAN / I2S / NVS / OTA
LittleFS
SD (SPI) / sleep BLE off only
SDMMC slot BLE off only
MCPWM (deadtime pair)
Camera (opt-in) ✓ (PSRAM) ✓ (PSRAM) ✓ (PSRAM)
Ethernet RMII (opt-in)
Ethernet SPI W5500 (opt-in)

About

Esp32 Python bridge, RPI bridge, its bridge

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors