Vibe-coded Python controller for Wonder Workshop's Dash robot
Connect over Bluetooth LE. Drive, spin, light up, play sounds, read sensors. All from Python.
![]() A project by Brent Rager β founder of Smoo AI AI agents, dev tools, CRM, support & campaigns that integrate with everything you build. Let's talk. |
Dash is a $190 kids' coding robot by Wonder Workshop. It drives, spins, lights up, plays sounds, and has proximity/accelerometer/microphone sensors β all accessible over Bluetooth LE.
The official apps are drag-and-drop block coding. This repo skips all that and talks directly to Dash over BLE using Python and bleak. Write real async Python scripts to control your robot.
I got one for free and decided to vibe code it with Claude Code.
# Clone and install
git clone https://github.com/brentrager/dash.git
cd dash
uv sync
# Turn on your Dash robot, then:
uv run python examples/lightshow.pygraph LR
PY["π Python Script<br/>async/await"]
BLEAK["π‘ Bleak<br/>BLE GATT client"]
DASH["π€ Dash Robot<br/>BLE peripheral"]
PY -->|"commands"| BLEAK
BLEAK -->|"GATT writes"| DASH
DASH -->|"sensor notifications"| BLEAK
BLEAK -->|"decoded state"| PY
style PY fill:#1a1a2e,stroke:#3776AB,color:#fff
style BLEAK fill:#1a1a2e,stroke:#0082FC,color:#fff
style DASH fill:#1a1a2e,stroke:#F59E0B,color:#fff
| Layer | UUID | Direction | Purpose |
|---|---|---|---|
| Service | AF237777-879D-... |
β | Robot BLE service |
| Command | AF230002-879D-... |
Write | Send commands (drive, lights, sound) |
| Dash Sensors | AF230006-879D-... |
Notify | Proximity, wheels, head position |
| Dot Sensors | AF230003-879D-... |
Notify | Accelerometer, buttons, mic, clap |
Commands are a single byte ID followed by a payload. The protocol was reverse-engineered by the community using BLE sniffers.
import asyncio
from dash_robot import discover_and_connect
async def main():
robot = await discover_and_connect()
# Lights
await robot.neck_color("red")
await robot.ear_color("#00ff00")
await robot.eye_brightness(255)
await robot.all_lights("purple")
# Movement
await robot.drive(200) # Forward
await robot.move(500) # Move 500mm and stop
await robot.turn(90) # Turn 90 degrees
await robot.spin(150) # Spin in place
await robot.stop()
# Head
await robot.look(yaw=30, pitch=5)
# Sound (40+ built-in sounds)
await robot.say("hi")
await robot.say("laser")
await robot.say("elephant")
# Sensors (updated in real-time via BLE notify)
print(robot.proximity) # {'left': 0, 'right': 0, 'rear': 0}
print(robot.is_picked_up) # False
print(robot.heard_clap) # False
print(robot.buttons) # {'main': False, '1': False, ...}
await robot.disconnect()
asyncio.run(main())Context manager works too:
async with await discover_and_connect() as robot:
await robot.say("hi")
await robot.move(300)uv run python cli.pydash> connect
Scanning for Dash...
Connected to XX:XX:XX:XX:XX:XX
dash> neck red
dash> say hi
dash> drive 200
dash> stop
dash> move 500
dash> turn 90
dash> sensors
Proximity: L=0 R=0 Rear=0
Picked up: False
Clap: False
dash> sounds
beep, boat, bragging, buzz, bye, cat, charge, ...
dash> quit
| Script | What It Does |
|---|---|
lightshow.py |
Rainbow color cycle on all LEDs |
drive_square.py |
Drive in a square pattern |
react_to_clap.py |
Random sound + color on clap detection |
obstacle_avoid.py |
Autonomous obstacle avoidance using proximity sensors |
uv run python examples/lightshow.py
uv run python examples/drive_square.py
uv run python examples/react_to_clap.py
uv run python examples/obstacle_avoid.py40+ built-in sounds
| Category | Sounds |
|---|---|
| Voices | hi, bragging, ohno, ayayay, confused, huh, okay, yawn, tada, wee, bye, charge |
| Animals | elephant, horse, cat, dog, dino, lion, goat, croc |
| Vehicles | siren, horn, engine, tires, helicopter, jet, boat, train |
| Effects | beep, laser, gobble, buzz, squeek |
All sensor fields
| Sensor | Field | Type | Source |
|---|---|---|---|
| Proximity | prox_left, prox_right, prox_rear |
int | Dash stream |
| Wheels | left_wheel, right_wheel, wheel_distance |
int | Dash stream |
| Head | head_yaw, head_pitch |
int | Dash stream |
| Orientation | yaw, yaw_delta, pitch_delta, roll_delta |
int | Dash stream |
| Accelerometer | pitch, roll, acceleration |
int | Dot stream |
| Buttons | button_main, button_1, button_2, button_3 |
bool | Dot stream |
| State | moving, picked_up, hit, on_side |
bool | Dot stream |
| Audio | clap, mic_level, sound_direction |
mixed | Dot stream |
Access via robot.state["field_name"] or convenience properties like robot.proximity.
Python 3.13 |
Bleak (BLE) |
Ruff |
uv |
# Format + lint
uv run ruff format . && uv run ruff check .
# Type check
uv run ty checkBLE protocol knowledge from the community:
- bleak-dash by mewmix
- morseapi by Ilya Sukhanov
- WonderPy (official, Python 2 only)
Built by Brent Rager at Smoo AI with Claude Code
