Team: Ekojoe Covenant Lemom (AlphaCode) & Juan Sebastian Valencia Londoño Repo: https://github.com/anidroid1184/uart-command-rust Working style: Fully async and remote. No live sessions required. Progress is measured by branch readiness, not by time spent online together.
- Each milestone has one owner who drives it to completion.
- The other person is the reviewer — they read the PR, leave comments, ask questions, and approve before merging.
- All communication happens over Telegram (async — no expectation of immediate replies) or GitHub PR comments.
- Neither person should ever be blocked waiting for the other to be online. If you're blocked, open a draft PR and describe the blocker in the description — the other person will respond when available.
developbranch is always buildable. Never push broken code todevelop.mainonly receives merges at the end of a completed milestone.
main ← stable, demo-ready snapshots only (tagged per milestone)
develop ← integration branch, always buildable
feat/uart-echo ← Milestone 1 (in progress)
feat/interrupt-rx ← Milestone 2 (in progress)
feat/ring-buffer ← Milestone 3 (not started)
feat/command-parser ← Milestone 4 (not started)
feat/polish ← Milestone 5 (not started)
PR flow: feat/* → develop (reviewed and approved by the other person) → at milestone completion, develop → main (tagged).
Branch: feat/uart-echo
Owner: Ekojoe
Reviewer: Juan
Target: develop, then tag main as v0.1.0
Goal: Configure UART0 on the ESP32. Read one byte when it arrives, write it straight back out. Verify in Wokwi simulation using the serial monitor.
Why this comes first: Peripheral configuration (baud rate, TX/RX pins, FIFO settings) is a separate skill from interrupt handling. We prove the hardware wiring works on its own before adding concurrency on top of it.
Definition of done:
- UART0 configured with correct baud rate and pins
- Blocking echo working in Wokwi simulation (type a character, see it returned)
- Code reviewed and approved by Juan
- Merged into
develop -
developmerged intomain, taggedv0.1.0
Juan's role this milestone: Review the PR. Read through the UART config code and make sure you understand how the baud rate is calculated and which pins are used — you'll be building on top of this in Milestone 2.
Branch: feat/interrupt-rx
Owner: Juan
Reviewer: Ekojoe
Target: develop, then tag main as v0.2.0
Goal: Move UART RX from blocking to interrupt-driven. When a byte arrives, the UART peripheral fires an interrupt service routine (ISR) that captures the byte and stores it in a temporary static buffer. The main loop reads from that buffer.
Why Juan owns this: Juan has the physical ESP32 board. Interrupt behavior on real silicon can differ from simulation in subtle ways — having the board owner drive this milestone means we catch those differences early rather than after merging.
Why this matters: Blocking RX means the main loop can do nothing else while waiting for input. Interrupt-driven RX frees the main loop to stay responsive — this is the core of what makes the CLI "real-time."
The hard part, clearly stated: An ISR runs outside normal program flow at an unpredictable time. Rust will not allow a plain mutable reference to shared data inside an ISR — and for good reason, as it would create a data race. For this milestone we use a critical_section::Mutex<RefCell<Option<u8>>> as a temporary safe shared state. Milestone 3 replaces this with a proper lock-free solution.
Definition of done:
- UART RX handled via ISR (not blocking)
- Received bytes accessible in the main loop via the temporary static buffer
- Tested on Juan's physical ESP32 (not simulation only)
- Code reviewed and approved by Ekojoe
- Merged into
develop -
developmerged intomain, taggedv0.2.0
Ekojoe's role this milestone: Review the PR carefully. Focus on understanding the ISR registration pattern and the critical_section usage — this is the pattern that Milestone 3 replaces, and you need to understand it to write the replacement correctly.
Branch: feat/ring-buffer
Owner: Ekojoe
Reviewer: Juan
Target: develop, then tag main as v0.3.0
Goal: Replace the temporary static buffer from Milestone 2 with a proper bbqueue producer/consumer split. The ISR writes bytes to the Producer handle. The main loop reads bytes from the Consumer handle.
Why Ekojoe owns this: This milestone is pure Rust concurrency reasoning — no hardware dependency, fully testable in Wokwi simulation. It builds directly on the ring buffer and lock-free concurrency concepts Ekojoe has been developing.
Why bbqueue specifically:
- The producer/consumer split means the ISR and the main loop each have their own non-overlapping handle to the buffer. The type system itself prevents concurrent access — no mutex, no disabling interrupts, no runtime cost.
no_stdrules out heap-based structures likeVecDequeunless we add an allocator, which adds complexity we don't need.- Manual ring buffer implementations frequently have subtle wraparound bugs.
bbqueuehas already solved this correctly.
Definition of done:
-
bbqueueadded toCargo.toml -
Producerhandle passed to ISR,Consumerhandle used in main loop - Temporary static buffer from Milestone 2 fully removed
- Wokwi simulation: bytes typed in serial monitor arrive correctly in main loop
- Code reviewed and approved by Juan
- Merged into
develop -
developmerged intomain, taggedv0.3.0
Juan's role this milestone: Review the PR. Focus on understanding why the producer/consumer split removes the need for the critical_section mutex from Milestone 2 — this is a key concept for the defense.
Branch: feat/command-parser
Owner: Both (split as described below)
Target: develop, then tag main as v0.4.0
Goal: The main loop drains bytes from the Consumer, accumulates them into a line buffer until it sees a newline (\n), splits on whitespace, matches the first word against known commands, and writes a response back over TX.
Split within this milestone:
- Ekojoe: Line accumulator + command dispatcher scaffolding. Write the function signatures and the dispatch table that maps command names to handler functions. Commit this to the branch first so Juan has a clear interface to build against.
- Juan: Implement the actual command handlers. At minimum:
ping→ respondspong,help→ lists available commands,status→ responds with a short chip status message.
Why fixed-size buffers: no_std has no heap-backed String by default. We use heapless::String<64> or a plain [u8; 64] array as the line buffer — fixed size, stack-allocated, no allocator needed.
Definition of done:
- Line accumulator working (bytes accumulate until
\n) - At least 3 commands implemented and responding correctly (
ping,help,status) - Unknown command returns a clear error response
- Tested in Wokwi simulation
- Both sides reviewed and approved before merging
- Merged into
develop -
developmerged intomain, taggedv0.4.0
Branch: feat/polish
Owner: Both
Target: develop → main, tagged v1.0.0-demo
Goal: Make the project demo-ready and defensible.
Tasks:
- Backspace handling in the line accumulator
- Character echo as the user types (terminal feels alive)
- Clean, informative responses to all edge cases
-
README.mdupdated with setup instructions, demo instructions, and project description -
ARCHITECTURE.mdfilled in with the final data flow diagram and design decision rationale - Wokwi
diagram.jsoncleaned up and presentable - Demo video recorded (1-2 minutes, showing the CLI responding to commands in simulation)
- Final merge to
main, taggedv1.0.0-demo
Before Juan starts Milestone 2 and Ekojoe starts Milestone 3, we agree on this interface in writing:
The ISR will call:
// producer is a bbqueue Producer handle accessible to the ISR
if let Ok(mut grant) = producer.grant_exact(1) {
grant[0] = received_byte;
grant.commit(1);
}The main loop will call:
if let Ok(grant) = consumer.read() {
for byte in grant.buf() {
// process byte
}
let len = grant.buf().len();
grant.release(len);
}Both of us commit to this interface before either branch diverges significantly — it means Juan can write the ISR side and Ekojoe can write the consumer side without needing to sync up live.
| Milestone | Branch | Owner | Reviewer | Main Tag |
|---|---|---|---|---|
| 1 — UART Echo | feat/uart-echo | Ekojoe | Juan | v0.1.0 |
| 2 — Interrupt RX | feat/interrupt-rx | Juan | Ekojoe | v0.2.0 |
| 3 — Ring Buffer | feat/ring-buffer | Ekojoe | Juan | v0.3.0 |
| 4 — Parser | feat/command-parser | Both | Both | v0.4.0 |
| 5 — Polish | feat/polish | Both | Both | v1.0.0-demo |
A place to capture ideas that could extend or enhance the demo, outside the core milestone scope.
Visualise a UART frame as it is transmitted by wiring 10 LEDs to a second ESP32 and lighting them up bit by bit (start bit, 8 data bits, stop bit). This would show the serial protocol "in flight" — a concrete visual aid for understanding UART at the electrical level.
Constraints to explore:
- The receiving ESP32 sees the fully reconstructed byte from its hardware UART, not individual bits. Bit‑level capture would need a different approach (bit‑ banging on a GPIO, or tapping the TX line directly).
- Adding a second ESP32 to the Wokwi simulation requires updating
diagram.jsonandwokwi.toml. - This is a visual polish / demo enhancement, not a core feature — best scoped to milestone 5 or as a stretch goal after the CLI is functional.