A DREAM Robotics Agent
- Robot Type: Tank
Windows controller: builds/MILA-Controller.exe shows MILA's own web page in a window, so it always matches the page. Source: scripts/web_controller.
ARIA |
KIDA-01 |
KIDA-00 |
WHIP |
NORA |
NIKA |
IDA |
|
ARM |
DREAM |
RIFT |
RITA |
NINA |
TINA |
AURA |
FLORA |
Website: https://cursedprograms.github.io/MILA-Robot-v00/
MILA is a small tank-chassis robot you can drive over WiFi. She hosts her own web dashboard, streams live sensor data, and can steer itself around obstacles.
- Drive modes: WASD (car-style), TANK (independent tracks) and OBSTACLE (autonomous).
- Live telemetry: front, left and right distance, temperature, humidity and the last IR remote key.
- WiFi dashboard: served by the ESP8266 on port
5010. She joins the fleet's "RIFT" network first, NORA's "NORA" network if there's no RIFT, and hosts her own "MILA" access point at192.168.4.1if neither is around - and keeps looking, so she moves to RIFT (or NORA) as soon as it appears. - Safety: a collision guard force-stops her in manual modes, and a watchdog stops her if the connection drops.
- Speed: cycle 100 / 75 / 50 / 25 % from the IR remote, or set an exact value from the dashboard slider (see the pinout warning below).
- MIND mode: the LLM on her PC controller drives her, one short move at a time: she looks (her distances, or a SCAN left / ahead / right), thinks out loud on screen, says what she's doing in her own voice (playful and quick), moves, and remembers. M on the Python controller, or MIND on her dashboard while it's connected. Only on an x86 PC with 8 GB+ RAM and a GTX 1050 or better.
- Ticks near walls: driving forward, the buzzer ticks faster as a wall gets close, like a parking sensor (ticks, not a tone: the buzzer shares a timer with the IR receiver).
- Lights in the dark: driving forward in the dark (the light sensor), her LED goes white instead of blue to light the way.
- Android app (
Android App/, packagecom.synthwomb): FIND gets her address from the fleet list, or type it in. Hold the stick to drive, let go to stop, and her modes, HELLO, DANCE, speed and the wheel lock are buttons. Build it withgradlew assembleDebug. Install:MILA-debug-2026-10-30.apk. - FOLLOW: she keeps your hand 30 cm from her sonar, in short pulses. A button on her dashboard, F on the Python controller, 4 on the remote (a guessed code).
- HELLO and DANCE: on the spot, in WASD or TANK: her sonar head shakes and she beeps. Buttons on her dashboard, H and N on the Python controller, 5 and 6 on the remote (guessed codes). Anything from her ESP cuts them short.
- Live view: her Python controller draws her from above, tracks turning, sonar reaching out.
- Several devices: up to four can be unlocked at once, her page follows a mode changed elsewhere, and
/statuscarriesrev, her settings in one number. - Gamepad rumble: the pad buzzes harder as she nears a wall while you drive her forward.
- The Python controller unlocks her with her PIN when it connects (it didn't before, so every drive command came back LOCKED).
- Small metal tank robot chassis
- UNO + WiFi R3 style board (ATmega328P + integrated ESP8266)
- 2S 18650 battery pack
- L298N motor driver
- MG95 servo
- HC-SR04 ultrasonic sensor
- 2× 5 V DC motors
- RGB LED
- AHT10 temperature & humidity sensor (I2C)
- Photoresistor module (digital out)
- Buzzer
- NEC IR receiver + remote
| Signal | Pin | Signal | Pin | |
|---|---|---|---|---|
| Ultrasonic TRIG / ECHO | 2 / 3 | Servo | 11 | |
| Right motor IN1 / IN2 / ENA | 4 / 5 / 9 | Buzzer | 8 | |
| Left motor IN3 / IN4 / ENB | 6 / 7 / 10 | Light sensor (DO) | 12 | |
| RGB LED R / G / B | A0 / A1 / A2 | IR receiver | A3 | |
| AHT10 SDA / SCL | A4 / A5 | ESP8266 link | Serial (DIP 1 + 2) |
Warning
Speed control and the servo share a timer. ENA and ENB are on pins 9 and 10, which the Servo library takes over (Timer1). At 100 % speed the motors are switched fully on and work. At 75 / 50 / 25 % the PWM is lost, so the motors may barely move or stop, and the servo can twitch. Fix it by swapping four wires and the matching constants in MILA.ino: ENA ↔ IN2 (ENA → 5, IN2 → 9) and ENB ↔ IN3 (ENB → 6, IN3 → 10). Pins 5 and 6 are Timer0 PWM, which nothing else uses.
Every controller lives in scripts/ and has a build-and-run script in .sh, .bat and .ps1 form.
| Language | UI toolkit | Run | Radar & graphs | Gamepad |
|---|---|---|---|---|
| Python | pygame | run.sh |
||
| C++ | Win32 / GDI | scripts/run-cpp.* |
✓ | ✓ |
| C# | WinForms | scripts/run-csharp.* |
✓ | ✓ |
| F# (preview) | WinForms | scripts/run-fsharp.* |
✓ | ✓ |
| Rust | egui | scripts/run-rust.* |
||
| Go | Ebitengine | scripts/run-go.* |
||
| Julia | GLMakie | scripts/run-julia.* |
Every key, gamepad button and remote button. The same list opens in its own window: F1 in the Python controller, KEYS on her web page (/keys), or python scripts/keymap.py.
Keyboard: web page and every desktop controller
| Key | Does |
|---|---|
| 1 / 2 / 3 | WASD / TANK / OBSTACLE mode |
| F | FOLLOW mode: she keeps your hand 30 cm from her sonar (Python controller; a button on her web page) |
| H / N | HELLO / DANCE, on the spot, in WASD or TANK (Python controller; buttons on her web page) |
| W / Up, S / Down | forward / back (WASD mode, hold) |
| A / Left, D / Right | turn left / right (WASD mode, hold) |
| W+A, W+D, S+A, S+D | curve: one track drives (Python controller; the stick too) |
| Python controller: driving in OBSTACLE takes over (WASD); she stops when you let go | |
| Q / A | left track forward / back (TANK mode, hold) |
| E / D | right track forward / back (TANK mode, hold) |
| Space | stop, in any mode (controllers; the STOP button too) |
| EMULATE / VOICE / THEME | Python controller: a pretend MILA on this PC / her voice / colours |
| M | her MIND mode on / off: the LLM on this PC drives her (Python controller; x86 PC, GTX 1050+) |
| X | speed preset: 100, 75, 50, 25 % (web page) |
| F1 | this window (Python controller; KEYS on the web page) |
| Esc | quit (controllers) |
| driving from the web page needs her PIN first (UNLOCK) |
Gamepad or joystick (ARM's flight stick works too)
| Key | Does |
|---|---|
| Left stick / D-pad | drive, half-way round curves (WASD mode; takes over from OBSTACLE) |
| LB / RB, right stick, twist | turn left / right (Python controller) |
| A (button 0) | stop (any mode) |
| B (1) / X (2) / Y (3) | OBSTACLE / WASD / TANK mode (Python controller) |
| Start (7) | speed + 25 % (Python controller) |
| C++, C# and F# controllers: stick or D-pad drive, triggers speed, A stops, bumpers change mode |
Her IR remote
| Key | Does |
|---|---|
| 1 / 2 / 3 | WASD / TANK / OBSTACLE mode |
| 4 / 5 / 6 | FOLLOW mode / HELLO / DANCE (codes guessed: 0x3, 0x4, 0x5) |
| Up / Down / Left / Right | drive (WASD mode, hold) |
| Red / Green | left track forward / back (TANK mode) |
| Yellow / Blue | right track forward / back (TANK mode) |
| OK | speed preset: 100, 75, 50, 25 % |
NORA's IR link (her web page, controller or Bluetooth)
| Key | Does |
|---|---|
| arrows / I J K L | drive |
| STOP | stop, and out of obstacle mode |
| AUTO / MANUAL | OBSTACLE / WASD mode |
| SPEED | speed preset |
| LOCK / UNLOCK | lock her wheels (nothing drives her) / free them |
| the same lock over WiFi: /motorlockOn, /motorlockOff?pw= (RIFT's Lock all / Unlock all) |
The C++, C# and F# controllers also have a proximity radar, live distance / temperature / humidity graphs with CSV export, an exact speed slider, network discovery (--host auto), remembered host / port / mode, and Xbox gamepad support (stick or D-pad to drive, triggers for speed, A to stop, bumpers to change mode).
The board's 8 DIP switches choose what the USB port and the two chips are connected to. Switch 8 is unused.
| What you're doing | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| Running MILA (Arduino ↔ ESP8266) | ON | ON | off | off | off | off | off | off |
Upload scripts/MILA (USB → ATmega328) |
off | off | ON | ON | off | off | off | off |
Upload scripts/esp8266 (USB → ESP8266, flash mode) |
off | off | off | off | ON | ON | ON | off |
| ESP8266 Serial Monitor (USB ↔ ESP8266) | off | off | off | off | ON | ON | off | off |
| USB ↔ ATmega328 ↔ ESP8266 all linked | ON | ON | ON | ON | off | off | off | off |
- Set 3 + 4 ON and upload
scripts/MILA. - Set 5 + 6 + 7 ON and upload
scripts/esp8266(press reset if the upload won't start). - Set 1 + 2 ON, everything else off, and power-cycle to drive her.
In the running setting the Arduino's serial goes to the ESP, not USB, so the Serial Monitor shows nothing. Use 3 + 4 to read her DIST: / IR: lines over USB.
Note
Check this against the table printed on your board, as some clones differ slightly.
The ESP8266's WiFi passwords and her control PIN are in scripts/esp8266/secrets.h (the fleet's: RIFT and NORA mmvbkbak, PIN 1234; secrets.example.h shows the layout). Flash the firmware from scripts/esp8266 and scripts/MILA with the Arduino IDE (set the DIP switches above first):
| Sketch | Board (Tools > Board) | Needs |
|---|---|---|
scripts/MILA |
Arduino UNO | Libraries: IRremote (4.x), Servo |
scripts/esp8266 |
Generic ESP8266 Module (Flash Size 4MB, FS 2MB OTA) | The esp8266 boards: add https://arduino.esp8266.com/stable/package_esp8266com_index.json under File > Preferences > Additional boards manager URLs, then install esp8266 in the Boards Manager |
Then power the robot on, and join her WiFi network. Then open the dashboard in a browser, or run a desktop controller:
./run.sh # macOS / Linux / Git Bash
run.bat # Windows (cmd)
.\run.ps1 # Windows (PowerShell)
./run.sh --host 192.168.4.1 --port 5010
./run.sh --emulate # a pretend MILA on this PC (scripts/fake_mila.py), no robot
The Python controller (run.*, scripts/mila_controller.py):
- Two keys curve her: W+A drives only her right track, so she arcs forward-left (W+D, S+A, S+D the same way), and so does the stick half-way round. Her UNO has no speed control yet (section 12 of the fleet TODO), so a curve is one track on, the other off.
- Driving takes over: driving in OBSTACLE puts her in WASD, and she stops when you let go. STOP (Space, the pad's A) works in any mode, TANK too.
- Settings stick: mode and speed are sent again until her
/statussays so. - EMULATE: a pretend MILA in a pretend room: her sonar reading drops as she drives at a wall, and her
UNO's guard stops her within 20 cm. VOICE: she talks through this PC (Zira, quick and cheeky): modes,
walls, her wheel lock, speed. THEME: DEFAULT, LIGHT or CUSTOM, from
colour_scheme.xml. - Pads: Xbox, PlayStation and Switch pads all have the same buttons (
gamepad_drive.py, SDL's controller layer). Her MIND log:~/.robot_minds/logs/MILA-<date>.log.
If MILA joined the RIFT or NORA network (fleet mode) she won't be at 192.168.4.1: RIFT's dashboard lists her address. Press SCAN NETWORK in the C++, C# or F# controller, or start it with --host auto.
The robots talk in Brainfuck: every phrase is a Brainfuck program that prints its words (hello is ++++++++[>+++++++++++++<-]>.+. → hi), and a robot says it by beeping the program on its buzzer, one tone per symbol, pitched to its own voice. The phrasebook (talk_bf.h) is generated by RIFT's Fleet/brainfuck_talk.py: phrases 0–6 (hello, how are you, happy, curious, sleepy, let's play, bye) and their replies 7–13. RIFT runs the conversations between robots over WiFi and logs them on its dashboard; it asks MILA to speak with GET /chirp?u=<0-13>. Her UNO plays TALK:<u> (from the ESP8266) through her own beep(), a little higher than NORA, and respects MUTE; she only talks while stopped outside OBSTACLE mode. The ESP8266 forwards /chirp to the UNO and registers talk:5010, so RIFT includes her in the conversations.
NORA can drive MILA through her IR transmitter, from her web page, Python controller or Bluetooth. The frames are Samsung-format IR at address 0x0DA2, with the fleet link's commands: 0x48 forward, 0x49 back, 0x4A left, 0x4B right, 0x4C stop, 0x4D obstacle mode, 0x4E manual, 0x4F speed. Driving switches her into WASD mode; stop also leaves OBSTACLE mode, and speed cycles the speed like the OK button. She stops once the link has been quiet for 500 ms. Link frames print as LINK:0x...
The EXPORT CSV button saves the whole session to Documents\MILA-telemetry. scripts/fortran/telemetry_stats.f90 turns an export into a report: sample rate, time in each drive mode, per-sensor min / max / mean / standard deviation, outliers (beyond 3σ), connection gaps and close calls (front distance under 20 cm). It needs gfortran and picks the newest export by default:
scripts/run-fortran.sh # or run-fortran.bat / run-fortran.ps1
scripts/run-fortran.sh my_run.csv
Drive her around by hand (IR remote, keyboard, gamepad or the dashboard), press EXPORT CSV, and scripts/fortran/make_dataset.f90 turns the sessions into a dataset for imitation learning: each row pairs what the ultrasonic sensor saw with what the human was doing.
scripts/run-dataset.sh # every export -> Documents\MILA-telemetry\datasets\drive_policy.csv
scripts/run-dataset.sh out.csv session1.csv session2.csv
- Only human driving is kept (
wasdandtankmodes). Obstacle mode (she drives herself) and moments the collision guard overrode you are dropped. - The label is the real motor state. The Arduino now reports
MOTOR:<left>,<right>(each -1 / 0 / +1), so the label is the same whichever input you used. The IR remote moves the motors directly and never shows up as a web command, which is why this is needed. It maps to 9 classes:STOP,FORWARD,BACKWARD,LEFT,RIGHT,L_FWD,L_BWD,R_FWD,R_BWD. - Features: front distance, how it is changing (
d_dist, 5-sample average and minimum), speed and mode.dist_validis 0 when the sensor returned no echo, in which casedistis carried over from the last good reading. - No data leakage: every export is its own session, and the last 20 % of each is marked
split=val. - Left/right distances are not features: the firmware only measures them during the obstacle-mode scan, so in human-driven data they are stale.
Columns: session,t_s,split,mode,mode_id,dist,dist_valid,d_dist,dist_avg5,dist_min5,speed,motor_l,motor_r,action,action_id,next_action,next_action_id. action is what you were doing at that sample and next_action what you did one sample (about 0.4 s) later, so use whichever suits your model. Load it with pandas, or anything that reads CSV.
Reflash both boards (scripts/MILA and scripts/esp8266) to get the motor columns. Exports from older firmware still work, but their labels come from the last web command only, so IR driving is invisible in them. The same reflash also makes the front sensor sample continuously in manual modes, so the distance no longer goes stale while you are stopped or turning.
scripts/sim holds a mock MILA that serves the same endpoints as the firmware with simulated sensor data:
scripts/run-sim.sh
scripts/run-cpp.sh --host 127.0.0.1
Her /status has wifi_rssi (her signal, 0 while she hosts), which RITA's STATS shows. Her ESP8266 has no Bluetooth: Bluetooth controllers reach her through NORA's IR link.
Every robot sends a small "I'm here" beacon twice a second and listens for the others'. From the signal strength it knows roughly how close each one is, and from how that changes over time whether it's coming closer, steady or leaving:
| Signal | Zone |
|---|---|
| above −45 dBm | very close |
| −45 to −60 dBm | near |
| −60 to −75 dBm | medium |
| below −75 dBm | far |
It's coarse (walls, bodies and antenna angle all change it): for "who's around", not distance. Precise collision avoidance stays with the ultrasonic and ToF sensors.
They tell each other what they're doing, because a rising signal looks the same from both sides even when only one robot moves. Over ESP-NOW they say it in Brainfuck, like the fleet's conversations: each beacon carries a program that prints park, go, wait or hand (a human is driving), and the receiver runs it. Bluetooth adverts are too small for a program, so BLE carries the same state as one byte.
Who makes way, in self-driving modes only:
| The other robot... | So this one... |
|---|---|
| is parked | is the one closing in: steers away |
| is yielding | carries on, carefully |
| is driven by a human | makes way (it's unpredictable) |
| drives itself | follows the alphabet: KIDA00, KIDA01, NORA, WHIP; everyone makes way for MILA, who can't hear the others |
| is leaving | carries on |
Making way = stop for 2 s, turn away, then drive on (and not yield again for 5 s, so two robots that stay close don't take turns forever). While another robot is near, or coming closer, it drives slower with wider margins.
MILA's ESP8266 sends the beacon over ESP-NOW, with what she's doing said in Brainfuck (go in obstacle mode, hand when you drive her, park when she's stopped), so NORA and WHIP know where she is. Her ESP8266 can't report the signal strength of what it receives, so she can't avoid the others: they always make way for her. Code: scripts/esp8266/esp8266.ino.
C++ controller, C# controller, Python controller, Web dashboard. Running against MockMila (scripts/sim), so the telemetry is live.
Minds — DREAM companion · TINA engineer · NINA caretaker · RIFT fleet hub · LYCEA training school
Robots — NORA · MILA · WHIP · KIDA-00 · KIDA-01 · IDA · ARM · NIKA · ARIA · AURA weather · FLORA gardener
Remotes — RITA handheld remote








