KevsRobots Learning Platform
By Kevin McAleer, 9 Minutes
Time to put everything you’ve learned to work. We’re going to write the README.md for a small robot called PicoBot: a two-wheeled robot with a Raspberry Pi Pico W for a brain, a distance sensor on the front, and a phone app that can drive it over Wi-Fi.
A good README answers the questions every visitor has: What is it? How is it wired? How does it think? How does the code fit together? We’ll answer each one with a diagram.
By the end you’ll have a README you can drop into a GitHub repository, and a template you can reuse for your own projects.
Start with the headings. Each ## section will get one diagram and a sentence or two of explanation:
# PicoBot
A two-wheeled Pico W robot that avoids obstacles and can be
driven from a phone over Wi-Fi.
## Parts
## How it's wired
## How it thinks
## Its modes
## Driving it from your phone
## How the code is organised
A flowchart with subgraphs makes a great block diagram. We’ll group the parts by what they do, label each link with the pins used, and colour the parts by type:
flowchart LR
classDef brain fill:#ffd166,stroke:#333,color:#000
classDef part fill:#a8dadc,stroke:#333,color:#000
subgraph power [Power]
bat[4 x AA batteries]:::part
end
subgraph inputs [Inputs]
tof["VL53L0X distance sensor"]:::part
btn[Start button]:::part
end
pico[Raspberry Pi Pico W]:::brain
subgraph outputs [Outputs]
drv[DRV8833 motor driver]:::part
ml[Left motor]:::part
mr[Right motor]:::part
end
bat -->|VSYS| pico
bat --> drv
tof -->|"I2C: GP0 SDA, GP1 SCL"| pico
btn -->|GP15| pico
pico -->|GP16 to GP19| drv
drv --> ml & mr
Here’s what it draws:
flowchart LR
classDef brain fill:#ffd166,stroke:#333,color:#000
classDef part fill:#a8dadc,stroke:#333,color:#000
subgraph power [Power]
bat[4 x AA batteries]:::part
end
subgraph inputs [Inputs]
tof["VL53L0X distance sensor"]:::part
btn[Start button]:::part
end
pico[Raspberry Pi Pico W]:::brain
subgraph outputs [Outputs]
drv[DRV8833 motor driver]:::part
ml[Left motor]:::part
mr[Right motor]:::part
end
bat -->|VSYS| pico
bat --> drv
tof -->|"I2C: GP0 SDA, GP1 SCL"| pico
btn -->|GP15| pico
pico -->|GP16 to GP19| drv
drv --> ml & mr
Notice the label with a colon and commas is in double quotes. Without them, Mermaid could get confused by the punctuation.
This diagram shows what connects to what, not every wire. Ground connections and power rails would clutter it, so leave those for a proper wiring diagram or a photo.
Next, a flowchart of the main loop. This is the diagram that’ll help you most when you come back to the code in six months:
flowchart TD
start([Power on]) --> wifi[Connect to Wi-Fi]
wifi --> read[/Read distance/]
read --> cmd{Command from phone?}
cmd -->|Yes| drive[Drive as commanded]
cmd -->|No| near{Closer than 15 cm?}
near -->|Yes| avoid[Reverse, then turn]
near -->|No| fwd[Drive forward]
drive & avoid & fwd --> wait[Wait 50 ms]
wait --> read
Here’s what it draws:
flowchart TD
start([Power on]) --> wifi[Connect to Wi-Fi]
wifi --> read[/Read distance/]
read --> cmd{Command from phone?}
cmd -->|Yes| drive[Drive as commanded]
cmd -->|No| near{Closer than 15 cm?}
near -->|Yes| avoid[Reverse, then turn]
near -->|No| fwd[Drive forward]
drive & avoid & fwd --> wait[Wait 50 ms]
wait --> read
PicoBot can be idle, exploring on its own, avoiding something, or under remote control. A state diagram shows how it moves between those modes:
stateDiagram-v2
state "Remote control" as Remote
[*] --> Idle
Idle --> Exploring : Start button
Exploring --> Avoiding : Obstacle under 15 cm
state Avoiding {
[*] --> Reversing
Reversing --> Turning : After 0.5 s
Turning --> [*] : After 90 degrees
}
Avoiding --> Exploring
Exploring --> Remote : Command received
Remote --> Exploring : No command for 5 s
Exploring --> Idle : Start button
Here’s what it draws:
stateDiagram-v2
state "Remote control" as Remote
[*] --> Idle
Idle --> Exploring : Start button
Exploring --> Avoiding : Obstacle under 15 cm
state Avoiding {
[*] --> Reversing
Reversing --> Turning : After 0.5 s
Turning --> [*] : After 90 degrees
}
Avoiding --> Exploring
Exploring --> Remote : Command received
Remote --> Exploring : No command for 5 s
Exploring --> Idle : Start button
The phone app sends commands to a tiny web server running on the Pico W. A sequence diagram shows the conversation, including what happens when the robot says no:
sequenceDiagram
autonumber
actor You
participant Phone as Phone browser
participant Pico as PicoBot web server
participant Sensor as VL53L0X
You->>Phone: Tap "Forward"
Phone->>+Pico: GET /drive?dir=forward
Pico->>Sensor: Read distance (I2C)
Sensor-->>Pico: 42 cm
alt Path is clear
Pico-->>Phone: 200 OK "Driving"
else Something in the way
Pico-->>Phone: 409 "Blocked"
end
deactivate Pico
Phone-->>You: Show status
Here’s what it draws:
sequenceDiagram
autonumber
actor You
participant Phone as Phone browser
participant Pico as PicoBot web server
participant Sensor as VL53L0X
You->>Phone: Tap "Forward"
Phone->>+Pico: GET /drive?dir=forward
Pico->>Sensor: Read distance (I2C)
Sensor-->>Pico: 42 cm
alt Path is clear
Pico-->>Phone: 200 OK "Driving"
else Something in the way
Pico-->>Phone: 409 "Blocked"
end
deactivate Pico
Phone-->>You: Show status
Because the reply happens inside an alt block, we switch the activation off with a separate deactivate Pico line after the block, rather than with - on one of the replies.
Finally, a class diagram helps anyone who wants to change the code find their way around:
classDiagram
class PicoBot {
+str state
+run()
+explore()
+avoid()
+handle_command(cmd)
}
class Motor {
+int pin_a
+int pin_b
+forward(speed)
+backward(speed)
+stop()
}
class DistanceSensor {
+I2C i2c
+read_cm() int
}
class WebRemote {
+int port
+start()
+next_command() str
}
PicoBot "1" *-- "2" Motor : left and right
PicoBot "1" *-- "1" DistanceSensor
PicoBot "1" --> "1" WebRemote : listens to
Here’s what it draws:
classDiagram
class PicoBot {
+str state
+run()
+explore()
+avoid()
+handle_command(cmd)
}
class Motor {
+int pin_a
+int pin_b
+forward(speed)
+backward(speed)
+stop()
}
class DistanceSensor {
+I2C i2c
+read_cm() int
}
class WebRemote {
+int port
+start()
+next_command() str
}
PicoBot "1" *-- "2" Motor : left and right
PicoBot "1" *-- "1" DistanceSensor
PicoBot "1" --> "1" WebRemote : listens to
Two small extras finish the README off nicely. A pie chart of the build cost:
pie showData
title PicoBot parts cost (£)
"Pico W" : 6
"DRV8833 driver" : 4
"Motors and wheels" : 8
"VL53L0X sensor" : 6
"Battery box and button" : 3
pie showData
title PicoBot parts cost (£)
"Pico W" : 6
"DRV8833 driver" : 4
"Motors and wheels" : 8
"VL53L0X sensor" : 6
"Battery box and button" : 3
And a timeline of the project’s history, which you can add to as it grows:
timeline
title PicoBot changelog
v0.1 : Drives forward : Stops at walls
v0.2 : Turns away from obstacles
v1.0 : Phone remote control over Wi-Fi
timeline
title PicoBot changelog
v0.1 : Drives forward : Stops at walls
v0.2 : Turns away from obstacles
v1.0 : Phone remote control over Wi-Fi
The prices are just examples, so swap in what you actually paid.
README.md, inside a mermaid code block, with a sentence explaining what it shows.From now on, whenever you change the robot, update the diagram in the same commit as the code. Your README will never go out of date again.
LineSensor class to the class diagram.Why: Each code block is drawn separately, so one mistake doesn’t affect the rest
LR to TD, or split it into two smaller diagramsWhy: Wide diagrams are shrunk to fit, which can make the text tiny
Quick quiz 1 of 3
Why do link labels like "I2C: GP0 SDA, GP1 SCL" need double quotes?
Quick quiz 2 of 3
Which diagram best shows the robot’s modes and what switches between them?
Quick quiz 3 of 3
What’s the best way to keep README diagrams up to date?
You can use the arrows ← → on your keyboard to navigate between lessons.
Comments