An alternate brain board. The main build uses a Raspberry Pi Pico 2 W with a one-click browser flasher โ this path is for people who'd rather use an ESP32. Same body, same firmware, a few extra command-line steps.
With help by olie-o, who first tested on ESP32. ๐
The body, servos, wiring and gluing are identical to the main build โ only the brain board and the flashing steps change.
Download the kit from the main build page (Step 05 โ Download the kit) and unzip it. You'll work inside the growbot-body-kit/firmware/ folder.
Then grab a MicroPython firmware image for your ESP32 from micropython.org/download โ โ pick your exact board, download the .bin, and save it into that firmware/ folder.
In a terminal, in the growbot-body-kit/firmware/ folder:
pip install esptool mpremote
Optional but tidy: make a virtualenv first โ python3 -m venv .venv && source .venv/bin/activate (macOS/Linux) or .venv\Scripts\Activate.ps1 (Windows).
Plug in the ESP32 and find its serial port:
mpremote connect list # any OS โ shows the port # macOS: ls /dev/cu.* # Linux: ls /dev/ttyUSB* /dev/ttyACM* # Windows: a COMx port
Erase, then flash the .bin you downloaded (replace the port and filename):
esptool --chip esp32 --port PORT erase-flash esptool --chip esp32 --port PORT write-flash 0x1000 YOUR_FIRMWARE.bin
0x1000 (above). ESP32-S3 / C3 flash at 0x0 instead โ use --chip esp32s3 (or esp32c3) and write-flash 0x0 โฆ. Some boards need you to hold the BOOT button while flashing starts.The servo driver auto-detects a Pico carrier board; on a bare ESP32 you tell it to drive the pins directly. Open firmware/PicoRobotics.py, find the config block near the top, and set FORCE + your two pins ({left, right}):
FORCE = "gpio" # was None
GPIO_PINS = {1: 18, 3: 19} # left, right โ pick from the table below
Use two free PWM-capable output GPIOs, then wire each servo's signal (orange) to its pin. The safe defaults differ by chip โ the big trap is the S3/C3 native-USB pins:
| chip | good pins (left, right) | avoid for servos |
|---|---|---|
| ESP32 (classic) | {1: 18, 3: 19} | 34โ39 (input-only), 0/2/12/15 (strapping) |
| ESP32-S3 | {1: 4, 3: 5} | 19/20 = USB! ยท 0/3/45/46 (strapping) |
| ESP32-C3 | {1: 4, 3: 5} | 18/19 = USB! ยท 2/8/9 (strapping) |
On the S3/C3, putting a servo on a USB pin fights the board's USB connection (random resets / disconnects) โ use the pins above instead. Servos draw power from the battery rail, never from an ESP32 pin (a GPIO can't source servo current โ it'll brown out or reset).
Copy the template, fill in your 2.4 GHz network (the ESP32 is 2.4 GHz only):
"2.4 GHz" is the Wi-Fi band, not your internet type โ 5G home, fiber, cable all work. Most routers broadcast a 2.4 GHz network; if you only see one, enable or "split" it in your router settings.
cp secrets.example.py secrets.py # then edit secrets.py: # WIFI_SSID = "YourNetwork" # WIFI_PASSWORD = "YourPassword"
Upload all four files (relay_chip.py becomes main.py):
mpremote cp secrets.py :secrets.py mpremote cp PicoRobotics.py :PicoRobotics.py mpremote cp act_engine.py :act_engine.py mpremote cp relay_chip.py :main.py mpremote reset
On reset the ESP32 runs the leg-check, then joins your Wi-Fi and dials the GrowBot relay.
The code prints at boot, but ESP32 dev boards reset when the serial port opens, so it's easy to miss. You don't need to catch it โ the code is just gb- + the chip's hardware id, so print it any time:
mpremote exec "import binascii,machine; print('gb-'+binascii.hexlify(machine.unique_id()).decode()[-6:])"
That prints something like gb-7a3f0c. It's the same every boot, so write it down once. (If you'd rather watch the live boot log: mpremote repl, then tap the board's reset.)
Your phone reaches the robot over Wi-Fi, through the relay โ not over the USB cable. On your phone, open the controller, type your pairing code, and connect:
Green dot = connected. Then drive it โ Manual, Policy & voice. Pairing code shown + phone connected = โ done. Send a clip!
| symptom | fix |
|---|---|
| "No module named PicoRobotics" | a file didn't upload โ repeat the mpremote cp step and check each succeeds |
| no green dot / won't connect | Wi-Fi is 2.4 GHz? secrets.py correct? code typed exactly (lowercase)? Confirm it joined: mpremote exec "import network;print(network.WLAN(network.STA_IF).isconnected())" |
| servos don't move | signal wires on the GPIOs from your GPIO_PINS, shared ground, servos on the battery (not a GPIO), and the Step 05 edit saved |
| board resets / USB drops when a servo moves | a servo is on a USB pin โ S3 = GPIO19/20, C3 = GPIO18/19. Move it to a safe pin (Step 05) and update GPIO_PINS |
| flashing fails | right port? right --chip + offset for your board? data USB cable? close other serial apps; some boards need BOOT held |