## 1. Project Overview & Quickstart (makerspet/oomwoo)

## File: README.md

OOMWOO is an *open-source home robot vacuum* you can build yourself, built using
Raspberry Pi, 3D-printing, Home Assistant, Arduino and ROS2. It uses an
affordable 2D LiDAR to map your home and navigate on its own. Local, no
cloud required for regular functionality, no vendor lock-in. Follow us building in public
[Discord](https://discord.gg/3y2JKz5T25) | [X](https://x.com/@0OMWO0) | [Instagram](https://www.instagram.com/oomw0o/) | [Facebook](https://www.facebook.com/profile.php?id=61591466775035) | [Reddit](https://www.reddit.com/r/oomwoo/) | [newsletter](https://stats.sender.net/forms/bo2rAK/view) | [YouTube](https://www.youtube.com/@makerspet) | [oomwoo.com](https://oomwoo.com/) | [Tutorials](https://makerspet.com/learn/)

> Early [build instructions](docs/BUILD_INSTRUCTIONS.md) will be available in Fall 2026.

Reference design images - this is approximately how the finished design will look:





## Goals

- Affordable, fully open hardware, software and firmware
- Home appliance product quality - not a throwaway build
- Easy to build, with step-by-step zero-to-hero instructions
- 2D LiDAR mapping and autonomous navigation (ROS2 / Nav2)
- Native Home Assistant integration for local control
- 3D-printable, documented, and hackable chassis
- Buildable from parts you source yourself
- Local, no cloud required for regular functionality
- Optional extra functionality when connected cloud
- Apps on top of ROS2 to customize vacuum operation
- Stretch goal: App store
- Stretch goal: LeRobot integration, OpenClaw

*v0 target: bare-bones build:*

- 3D-printed chassis ([browse](https://github.com/makerspet/oomwoo-install))
- ROS2 Gazebo sim ([install](https://github.com/makerspet/oomwoo-install))
- Basic cleaning, mapping
- Raspberry Pi CM4/CM5 running ROS2 ([install](https://github.com/makerspet/oomwoo-install))

Open Source Deliverables:

- [x] [Software development environment](https://github.com/makerspet/oomwoo-install), robot [description package](https://github.com/makerspet/oomwoo-one/) and [tutorials](https://makerspet.com/blog/simulate-oomwoo-one-robot-vacuum-in-gazebo-with-ros-2/) (ROS2)
- [x] Placeholder real [vacuum cleaner](https://github.com/makerspet/proscenic-m6pro) and [tutorials](https://makerspet.com/blog/tutorial-connect-robot-vacuum-cleaner-to-ros-2-proscenic-m6-pro/) (temporary while OOMWOO is being designed)
- [x] [Bill of materials (BoM)](BOM.md) (rough version available)
- [x] 3D-scanned [sourced parts](https://github.com/makerspet/oomwoo-one-cad/tree/main/lib)
- [ ] 3D-printable [files](https://github.com/makerspet/oomwoo-one-cad)
- [ ] Raspberry Pi [software](https://github.com/makerspet/oomwoo-install)
- [ ] Motor drivers, sensors [PCB boards](https://github.com/makerspet/oomwoo-pcb)
- [ ] I/O PCB [firmware](https://github.com/makerspet/oomwoo-io-firmware)
- [ ] Build, setup, bringup and troubleshooting [instructions](docs/BUILD_INSTRUCTIONS.md)
- [ ] Demo video(s)

## Contributing

Would you like to contribute? See [CONTRIBUTING](docs/CONTRIBUTING.md) for the full guide.

OOMWOO is organized to built by the community, massively *in parallel*.
The vacuum and its software are subdivided into [modules](#requests-for-contributions), see list below.

A volunteer picks whatever module she wants and works on it whenever she wants.
For *code and simulation* modules she builds her package in her *own repo* and sends
a short PR *linking* it from the module; for *docs and specs* she contributes files
in-tree under `contributions/module-name/<her-github-username>`. See
[CONTRIBUTING](docs/CONTRIBUTING.md) for how this works.

Multiple developers are welcome to work on the same module.
The best solution for each module surfaces over time, with the project master having the last call.

1. Pick a contribution from the [list below](#requests-for-contributions).
2. [Let us know](https://github.com/makerspet/oomwoo/discussions) you're working on it and your progress.
3. Check [ARCHITECTURE.md](docs/ARCHITECTURE.md) and
   [SOFTWARE_INTERFACES.md](docs/SOFTWARE_INTERFACES.md) for the system design
   and ROS2 interfaces.

## Requests for Contributions

Every module below is *actionable now* — build it against the Gazebo simulation
([oomwoo-one](https://github.com/makerspet/oomwoo-one)) or a real *placeholder robot*
(a [Proscenic M6 Pro connected to ROS2](https://makerspet.com/blog/tutorial-connect-robot-vacuum-cleaner-to-ros-2-proscenic-m6-pro/)),
until OOMWOO hardware is ready. Pick one, tell us in
[Discussions](https://github.com/makerspet/oomwoo/discussions), build it in your own
repo (docs and specs go in-tree), and send a short PR linking it from the module.

| Module | ID | Status | Notes |
|---|---|---|---|
| ROS2 URDF + Gazebo sim | [urdf-gazebo-sim](./contributions/urdf-gazebo-sim) | Mostly complete | Placeholder URDF + Gazebo sim (reference: [oomwoo-one](https://github.com/makerspet/oomwoo-one); [@alvarosamudio](https://github.com/alvarosamudio/oomwoo_gazebo) featured), refined when hardware lands |
| First clean: coverage + mapping + exploration | [clean-and-map](./contributions/clean-and-map) | In progress | Coverage cleaning while SLAM-mapping and exploring |
| Auto cleaning |  | In progress | Clean the entire room using an existing map (using coverage path planning) |
| Regression tests |  | In progress | Set up simulatior regression test framework (auto cleaning in Gazebo) |
| Localization & navigation on a known map | [nav-localize](./contributions/nav-localize) | In progress | Nav2 nav, AMCL localization, relocalize when lost, resume map |
| Dock cycle: undock, dock, recharge | [dock-cycle](./contributions/dock-cycle) | Ready to start work | Undock, return-to-dock, precise docking, station services, find dock when lost |
| Recovery behaviors & safety | [recovery-safety](./contributions/recovery-safety) | Ready to start work | Recovery ladder, escalation, pause-and-alert, safety sensors, status reporting |
| Stack health monitor & software watchdog | [health-monitor](./contributions/health-monitor) | In progress | Per-component alive-and-well heartbeats + a roster-aware aggregator that feeds the MCU deadman; stops the robot when any critical node crashes / hangs / is missing — the soft-fault layer above the MCU's hard reflexes |
| Near-field obstacle avoidance (camera + ToF) | [obstacle-avoidance](./contributions/obstacle-avoidance) | In progress | v2 "never gets stuck": front stereo camera + 2x VL53L7CX ToF detect below-LiDAR obstacles (cables, socks); detect-then-classify |
| Compute benchmark & memory reduction | [compute-benchmark](./contributions/compute-benchmark) | In progress | Measure ROS2/Nav2/SLAM memory, compare composable nodes, and track the 4 GB -> 2 GB target |
| Floor-surface handling & edge cleaning | [floor-care](./contributions/floor-care) | In progress | Wall/edge following, carpet vs hardwood, mop lift/lower |
| Cleaning modes, zones & job orchestration | [cleaning-jobs](./contributions/cleaning-jobs) | Ready to start work | Modes (regular/spot), virtual walls, room segmentation, job splitting + resume |
| Control app & UX (design-first) | [control-app](./contributions/control-app) | Ready to start work | Local-first control app/UI — a client of the ROS2 stack + Home Assistant; design-led (welcomes designers): prioritize features, concept the MVP surface (start/stop, live map, status, zones) |
| Live robot bring-up & validation | [live-robot-bringup](./contributions/live-robot-bringup) | Ready to start work | Connect the placeholder Proscenic M6 Pro to ROS2, re-run sim tests on hardware |
| Source 3D models (STEP) for BOM parts | [source-3d-models](./contributions/source-3d-models) | Mostly complete | Obtain / measure / model STEP files of off-the-shelf parts (wheels, fans, caster…) so mounts fit |
| Procure part specs & datasheets | [part-specs](./contributions/part-specs) | In progress | Find/measure/reverse-engineer specs (pinouts, encoder PPR, torque, how to drive fans…) for sourced parts |
| I/O + motor-driver PCB | [io-pcb](./contributions/io-pcb) | In progress | I/O board with CM4/CM5 socket, STM32 MCU - motors, sensors, 4S2P charging, safety, FreeRTOS, custom serial to CM4/CM5, 2D LiDAR header, IMU, audio serial/amp/speaker, MIPI camera(s) i/f; KiCad, JLCPCB |
| I/O board software interface | [io-board-interface](./contributions/io-board-interface) | Ready to start work | CPU/MCU serial contract, ROS2 bridge mapping, safety watchdog behavior, hardware signal ownership, and bringup validation |
| MCU I/O board firmware | [mcu-io-firmware](./contributions/mcu-io-firmware) | In progress | STM32G473 firmware: Arduino (STM32duino) API + FreeRTOS + a HAL/ISR real-time safety core; motors, sensors, charging, custom serial to the CPU; [repo](https://github.com/makerspet/oomwoo-io-firmware) |
| Fit software into 2GB RAM | [compute-benchmark](./contributions/compute-benchmark) | 2GB achieved | ROS2 node composition, Rust; remove Gazebo, desktop UI |

> Planned and on-hold modules (mechanical design, later-phase software) live in the
> [RFC backlog](docs/RFC_BACKLOG.md).

## Source code reference

- [OOMWOO ROS2 and Ubuntu installation](https://github.com/makerspet/oomwoo-install/) source code
- [OOMWOO ROS2 URDF package and config](https://github.com/makerspet/oomwoo_urdf/) source code
- [remakeai reference vacuum teardown](https://github.com/remakeai/vacuum_cleaner_teardown) — a consumer LiDAR vacuum with a basic dock and stationary mop.

## Related prior art

- [Valetudo](https://github.com/Hypfer/Valetudo) — cloud-free firmware replacement for commercial vacuums (local app-level control, not ROS2)
- [codetiger/VacuumTiger](https://github.com/codetiger/VacuumTiger) - 3irobotix CRL-200-based vacuum low-level control reverse engineered
- [kaiaai/LDS](https://github.com/kaiaai/LDS), [kaiaai/lds2d](https://github.com/kaiaai/lds2d) — open-source 2D LiDAR libraries (C++, Python) supporting 23+ LiDAR models
- [remakeai/vacuum_ros2_bridge](https://github.com/remakeai/vacuum_ros2_bridge) — ROS2 bridge for a 3irobotix CRL-200-based vacuum (Proscenic), full ROS2 control
- [Dennis Giese / robotinfo.dev](https://robotinfo.dev) — teardowns and rootability of commercial robot vacuums.
- [Build a ROS2/LiDAR robot crash course](https://makerspet.com/blog/build-arduino-self-driving-robot-video-instructions/) - watch this if you have no robotics experience
- [Open Mower](openmower.de) - open-source outdoor lawn mower
- [AlieksieievYurii/vacuum-cleaner](https://github.com/AlieksieievYurii/vacuum-cleaner) — a DIY 3D-printed robot vacuum (Raspberry
  Pi Zero W, gyroscope-based, Fusion 360, Android control app, no dock)

## User requirements &amp; Design research

Anecdotal prospective user requirements - collected mostly in [r/RobotVacuums](https://www.reddit.com/r/RobotVacuums/), [r/ROS](https://www.reddit.com/r/ROS/) and as [announcement post](https://makerspet.com/blog/building-an-open-source-robot-vacuum-meet-oomwoo/) comments:

- open-source
- 3D printed first
- cleans well, a first-class appliance, not a throwaway build
  - never gets stuck
  - hair tangle-free
  - modern capabilities - no legacy drag mop; dual-spinning rotary mop or better/roller
  - bagless
- hackable, DIY fixable, upgrade-able
  - examples: vacuum-only version, mop-only version
  - Raspberry Pi
  - Home Assistant integration
- operates without cloud
- later: ability to retrofit existing legacy vacuum cleaner robots

## About

The project name "OOMWOO" is a rotational ambigram - it reads the same flipped 180°, like the robot itself, roaming your floor in every direction.

The project is sponsored by makerspet.com and remake.ai. We are reusing their open-source solutions.
- If you'd rather skip the parts hunt, a kit (motors, PCB, brushes, gaskets, LiDAR) will be available at [makerspet.com](https://makerspet.com), from the same maker behind this project. The kit is a convenience, never a requirement. *Everything here stays open.*
- When we get to apps, [remake.ai](https://remake.ai) will be providing its robot apps platform and app store. Using the app store will be entirely optional. The vacuum will *always support cloud-free, local operation for regular functionality out-of-the-box*. 

## License

Code is released under the [Apache License 2.0](LICENSE).

Hardware design files, once added, to be released under an open hardware
license (TBD).

---

## File: contributions/vacuum-fan/README.md

# Blower Fan Assembly (mechanical module)

> *On hold.* Fan options are *already sourced* — see the suction-fan rows in
> [BOM.md](../../BOM.md) (multiple Pa/price options: Dreame, Nidec, Roborock-family blowers).
> The project has also moved *away from the old teardown reference vacuum* (its parts are
> hard to source vs. big names like Roborock/Dreame/Xiaomi). This module resumes once enough
> components are sourced and a *3D reference design with the key parts* is sketched, so the
> housing can be designed to fit real parts.
>
> *Useful upstream work now:* model the sourced fans in
> [source-3d-models](../source-3d-models), and gather fan specs / how to drive them in
> [part-specs](../part-specs). Those unblock this design.

The vacuum fan assembly provides air suction to the main brush and streams air into the dust bin.
It consists of:
- a high-speed *BLDC blower motor + impeller* (already sourced — see BOM)
- a blower housing (volute) — to be designed
- a blower exhaust gasket
- blower housing rubber mounts

> *Design research note:* real-world cleaning does *not* track raw suction (Pa) —
> mid-range sealed motors match flagships. Prioritise a *well-sealed* airflow path
> (no leaks at the bin / fan / brush seams) and a good brush over chasing maximum Pa.
> See [design research](../../README.md#design-research).

# Request for Contribution — Instructions (resumes when off hold)

When this reopens, the task is to design a *3D-printable volute housing + gasket around a
sourced BLDC blower* (chosen from the [BOM](../../BOM.md)) that mates cleanly with the dust
bin and the chassis. Design goals:
- 3D-printable (assume PETG); ideally no supports; reliably reproducible
- sliced part fits ~20 × 25 cm, 20 cm height (split if needed)
- *well-sealed* airflow path (no leaks); mates with the dust bin + chassis
- low vibration; secure motor mounting
- gasket: find an off-the-shelf one, else print TPU
- submit STEP + native CAD + 3MF/STL + a sub-component BoM + docs/photos/video to
  `contributions/vacuum-fan/<your-github-username>/`

## Acceptance criteria (when it resumes)

- Fits the (forthcoming) chassis + dust-bin interfaces without forcing changes to other modules
- Well-sealed, reasonably quiet, low-vibration, strong suction with the chosen sourced blower
- 3D-printable and reliably reproducible by someone else
- Documented; STEP + native CAD source provided
- TBD, expect criteria to evolve

The maintainer selects among compliant candidates using these criteria. Multiple attempts
are welcome and useful even if not selected — modules are swappable, and a non-selected
design is still a valid learning exercise and a fallback.

---

## File: contributions/urdf-gazebo-sim/README.md

# oomwoo URDF + Gazebo Simulation

Self-hosted package: [github.com/alvarosamudio/oomwoo_gazebo](https://github.com/alvarosamudio/oomwoo_gazebo)

ROS2 package with URDF model, Gazebo simulation worlds, bump recovery node, and Nav2/SLAM configuration for the OOMWOO robot vacuum. Built against the [ROS2 Software Interfaces](../../docs/SOFTWARE_INTERFACES.md) contract.

See the repo above for full documentation, setup instructions, and usage.

---

## File: contributions/source-3d-models/README.md

# Source 3D Models (STEP) for Off-the-Shelf BOM Parts (procurement)

To design printed mounts and the chassis that *fit real sourced parts*, we need accurate
3D models (*STEP*) of the off-the-shelf components in the [BOM](../../BOM.md) — the
drive-wheel assembly, the suction fan/blower (several candidate models), the caster wheel,
the side-brush motor, and more. Manufacturers rarely publish these, so the community needs
to *obtain, measure, and model* them. Every printed mount and the chassis references this
geometry — see [ARCHITECTURE.md](../../docs/ARCHITECTURE.md) §5.2 (mechanical interface
standard). Missing models block or force guesswork on the mechanical modules.

This is a great *first contribution* for anyone with CAD skills and calipers — no
robotics or programming needed, and the modules can be modeled *in parallel*.

> *Please check the [STEP models library](https://github.com/makerspet/oomwoo-one-cad/tree/main/lib) before you pick a part.*
> STEP models live in the [makerspet/oomwoo-one-cad](https://github.com/makerspet/oomwoo-one-cad) repo, in the `lib` folder.

# Request for Contribution - Instructions

- *Pick a part* from the [BOM](../../BOM.md) and *claim it* in
  [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=) or
  [Discord](https://discord.gg/3y2JKz5T25) so two people don't model the same thing. Post progress too.
  - Please consider submitting a PR (pull request) to `makerspet/oomwoo` in `contributions/source-3d-models/<your-GitHub-username>/README.md`
  with a brief description of which part you have picked, relevant links (to your repo containing the part) and files if any.
- *Get an accurate model* — three paths:
  1. *Find an existing STEP* (manufacturer site, GrabCAD, etc.) and *verify it against the
     real part* (dimensions match). Fastest — but confirm it's the same model/revision.
  2. *Model it yourself*: order the exact part (AliExpress links are in the BOM), measure
     with calipers, optionally use a 3D scanner and build an accurate STEP.
  3. 3D scan the part, convert the mesh into a STEP file. As an example, I use Creality Raptor.
- *Capture the interface-relevant geometry* (external only — internal detail is not needed):
  - Overall *bounding envelope* (so it fits the chassis space / height budget)
  - *Mounting features*: holes, bosses, clips, bolt pattern — and their exact positions
  - *Functional interfaces*: axle/shaft position + axis, wheel contact plane, fan air
    inlet/outlet, connector / wire-exit locations
  - *Mating faces* where other modules attach
  - Note the *key overall dimensions*
- *Record provenance* — the exact vendor + link + which model/revision you measured. Parts
  vary between sellers and revisions; say which one this model represents.
- *Submit a PR* to `contributions/source-3d-models/<your-github-username>//`:
  - the *STEP* file (primary deliverable) + native CAD source (Fusion/SolidWorks/etc.) if you modeled it
  - a *photo* of the real part
  - the *source link* (AliExpress/vendor) and *key dimensions*
  - notes on tolerances and how you measured - if any
  - announce it in [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=)
- Iterate with review.
- TBD, expect the RFC to evolve.

(*) If you have a strong reason to model a different variant than the BOM lists, post your
rationale in [discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=) first.

## Acceptance criteria

Objective, measurable. Examples:
- Accurate *STEP* of the specified part — bounding envelope, mounting features, and
  functional interfaces match the measured real part (within a stated tolerance)
- Includes the *exact source link*, *key dimensions*, and a *photo* of the real part
  - Please make sure the model is licensed as open-source
- *Verifiable/reproducible* by someone else who buys the same part
- STEP provided (+ native CAD source if self-modeled)
- Documented well enough that a mount designer can build against it with confidence
- TBD, expect criteria to evolve

The maintainer selects among compliant candidates using these criteria. Multiple attempts
are welcome and useful even if not selected — models are swappable, and a non-selected
model is still a valid reference and a fallback.

---

## File: contributions/recovery-safety/xbattlax/README.md

# Recovery Safety Prototype by xbattlax

This contribution provides a first ROS2 recovery-and-safety package for the
OOMWOO recovery-safety RFC. It is deliberately small, deterministic, and
simulation-first:

- a bounded recovery ladder for bumper, wedged, no-path, and lost-localization
  situations
- immediate safe stop for e-stop, cliff, wheel-drop, and pickup events
- pause-and-alert when the ladder is exhausted
- a structured JSON status topic suitable for later Home Assistant integration
- unit tests for guaranteed termination and safety responses

The core ladder lives in pure Python, so it can be regression-tested without a
running ROS graph. The ROS2 node is a thin adapter around that core.

## Package

`oomwoo_recovery_safety`

Location:

```text
contributions/recovery-safety/xbattlax/oomwoo_recovery_safety
```

## Interfaces

### Subscribed topics

| Topic | Type | Purpose |
|---|---|---|
| `/bumper_left` | `ros_gz_interfaces/msg/Contacts` | Trigger left-bumper recovery after filtering ground-plane contacts. |
| `/bumper_right` | `ros_gz_interfaces/msg/Contacts` | Trigger right-bumper recovery after filtering ground-plane contacts. |
| `/oomwoo/recovery/event` | `std_msgs/msg/String` | Manual/test trigger. Payload examples: `wedged`, `no_valid_path`, `localization_lost`, `bumper_front`. |
| `/oomwoo/recovery/behavior_result` | `std_msgs/msg/String` | Optional external result for the current behavior: `succeeded`, `failed`, or JSON `{"outcome":"succeeded"}`. |
| `/oomwoo/safety/e_stop` | `std_msgs/msg/Bool` | Immediate non-recoverable pause. |
| `/oomwoo/safety/cliff` | `std_msgs/msg/Bool` | Immediate safety pause. |
| `/oomwoo/safety/wheel_drop` | `std_msgs/msg/Bool` | Immediate safety pause. |
| `/oomwoo/safety/pickup` | `std_msgs/msg/Bool` | Immediate safety pause for pickup/kidnap handoff. |
| `/oomwoo/recovery/reset` | `std_msgs/msg/Bool` | Reset a paused/recovered controller when true. |

### Published topics

| Topic | Type | Purpose |
|---|---|---|
| `/cmd_vel` | `geometry_msgs/msg/Twist` | Short bounded motion commands for recovery. |
| `/oomwoo/status` | `std_msgs/msg/String` | JSON status with `state`, `reason_code`, `message`, `recoverable`, `source`, and recovery metadata. |
| `/oomwoo/recovery/command` | `std_msgs/msg/String` | JSON command for non-motion actions such as `clear_costmap`. |

## Build

From the OOMWOO ROS2 container:

```bash
source /opt/ros/jazzy/setup.bash
cd /workspace
colcon build \
  --base-paths contributions/recovery-safety/xbattlax/oomwoo_recovery_safety \
  --packages-select oomwoo_recovery_safety
```

## Test

```bash
source /opt/ros/jazzy/setup.bash
cd /workspace
colcon test \
  --base-paths contributions/recovery-safety/xbattlax/oomwoo_recovery_safety \
  --packages-select oomwoo_recovery_safety
colcon test-result --verbose
```

The tests cover:

- bounded escalation to pause-and-alert
- successful recovery stopping the ladder
- e-stop and safety events entering safe pause
- ignored duplicate triggers while already recovering
- JSON status shape
- held `/cmd_vel` publishing so motion recoveries outlive base watchdog timeouts
- a separate completion timeout for delegated commands such as `clear_costmap`

## Run

```bash
source /opt/ros/jazzy/setup.bash
source install/setup.bash
ros2 launch oomwoo_recovery_safety recovery_safety.launch.py
```

Manual trigger example:

```bash
ros2 topic pub --once /oomwoo/recovery/event std_msgs/msg/String "{data: bumper_front}"
```

Mark the current behavior as successful:

```bash
ros2 topic pub --once /oomwoo/recovery/behavior_result std_msgs/msg/String "{data: succeeded}"
```

Trigger e-stop:

```bash
ros2 topic pub --once /oomwoo/safety/e_stop std_msgs/msg/Bool "{data: true}"
```

## Current limitations

- This is a first integration scaffold, not a full Nav2 behavior-server plugin.
- `clear_costmap` is published as an intent on `/oomwoo/recovery/command`; a
  future adapter should call Nav2 costmap clear services directly.
- Success detection is external for now. If no `succeeded` result arrives before
  a behavior's timeout, the node escalates to the next behavior and eventually
  pauses. Twist motions use their motion duration as the timeout; delegated
  commands can use a longer completion timeout.
- Cliff, wheel-drop, and pickup are represented as boolean topics until the
  hardware/simulation message contract is finalized.

---

## File: contributions/recovery-safety/README.md

# Recovery Behaviors & Safety (ROS2 package)

Keep the robot *unstuck and safe*. This package provides a *recovery ladder*
(back up, wiggle, shake free, rotate, nudge, clear costmap, try an alternate path),
*escalation* when a recovery attempt fails, and a final *pause-and-alert* state
when everything fails. It also covers *safety*: cliff / wheel-drop / pickup
detection, an e-stop, and *status / error reporting* so a human knows what
happened. Because the physical robot isn't built yet, this is a *Gazebo
simulation*; it is later re-validated on hardware in the
[live-robot-bringup RFC](../live-robot-bringup).

> *Status — ready to start work.* No need to wait for OOMWOO hardware — develop it in the
> Gazebo sim ([urdf-gazebo-sim](../urdf-gazebo-sim)) or on the real
> [placeholder Proscenic M6 Pro](https://makerspet.com/blog/tutorial-connect-robot-vacuum-cleaner-to-ros-2-proscenic-m6-pro/).
> Say so in the [discussions](https://github.com/makerspet/oomwoo/discussions) so we can coordinate.

# Design direction: reactive, contact-aware recovery (recommended)

Prior art is unanimous: real robot vacuums do **not** escape wedges with
navigation-stack recoveries. Nav2's `spin` / `backup` are *collision-averse* — they
check the costmap and refuse to move into the very cells the robot is wedged
against, so they stall exactly when needed (OOMWOO's own coverage planner already
documents *"spin/backup recoveries refuse to move there"*). A vacuum is
*contact-tolerant*: it has a bumper and is meant to touch walls and furniture. The
recovery it needs is a small **reactive, bumper-driven behavior layer** that runs
*open-loop and ignores the costmap*, layered **under** the planner and overriding it
while in contact (subsumption — Brooks, 1986). Coverage plans the open floor; this
handles near-obstacle. If it grows, this reactive layer can later be split into its
own `reactive-control` module.

**Proven escape heuristics** — a starting point from iRobot's *multi-mode coverage*
patents; tune the thresholds in sim. All are keyed on the *bumper pattern*, not the
costmap:

- *Wedge — bumper held.* If a bumper stays pressed for ~5 s, rotate **away from the
  pressed side** (a "panic turn") and retry; repeat until it releases. Back off and
  turn toward the free side — not a blind straight reverse.
- *Confined pocket — frequent bumps.* Track the running distance between bumps; when
  it drops below a threshold the robot is boxed in — switch to **bumper
  edge-following** (touch, small turns into-and-away) to worm out; below a lower
  threshold, panic-turn.
- *Stuck / wheels spinning — no bumps.* If the robot travels a long distance with no
  bump at all, assume it is high-centered / spinning — **spiral**, then panic-turn if
  still no contact.
- *Don't re-enter.* Mark the escaped pocket no-go so the sweep / gap-fill doesn't
  route straight back in (OOMWOO's coverage planner already does a version of this).

The simulated bumpers now publish (`/bumper_left|right/contact`), so this is
buildable and testable **headless today** — see
[oomwoo-one sim-bumpers](https://github.com/makerspet/oomwoo-one/blob/main/docs/sim-bumpers.md).
The same bumper-contact mechanism drives edge cleaning in [floor-care](../floor-care),
and stack-liveness safety lives in [health-monitor](../health-monitor).

# Important References
- [clean-and-map RFC](../clean-and-map) and [urdf-gazebo-sim RFC](../urdf-gazebo-sim) — the *bumper* (left / right / front) and any cliff / wheel-drop sensors this package reacts to.
- [nav-localize RFC](../nav-localize) — pickup / kidnap detection ties into relocalization.
- [ROS2 software interfaces](../../docs/SOFTWARE_INTERFACES.md) — shared topic/action/service contract for simulation-first modules.
- [OOMWOO ROS2 development](https://github.com/makerspet/oomwoo-install) — build OOMWOO ROS2 Docker image(s) with your packages.
- Nav2's behavior/recovery server can *compose* recoveries, but note its `spin` / `backup` are collision-averse (see *Design direction* above) — for wedges, prefer contact-aware escapes.
- *Prior art — the reactive layer everyone uses:* iRobot behavior-based vacuum control — [multi-mode coverage](https://image-ppubs.uspto.gov/dirsearch-public/print/downloadPdf/6809490) ([7173391](https://image-ppubs.uspto.gov/dirsearch-public/print/downloadPdf/7173391)) and [learned escape behaviors](https://image-ppubs.uspto.gov/dirsearch-public/print/downloadPdf/11656628); Brooks, *A Robust Layered Control System for a Mobile Robot* (subsumption, 1986).
- *Coverage-planning prior art:* [opennav_coverage](https://github.com/open-navigation/opennav_coverage) (Nav2 / Fields2Cover, open-field focused), Fraunhofer [ipa_room_exploration](https://medium.com/@jjbecomespheh/ros-coverage-path-planning-32ec97ce3c21), and online sensor-based coverage (BA\*, Morse decomposition). [VFH+](https://www.mdpi.com/2077-1312/12/3/412) is a contact-tolerant reactive alternative to costmap avoidance.
- [Project discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=)
- [Discord server](https://discord.gg/3y2JKz5T25)

# Request for Contribution - Instructions

- implement a *recovery ladder*
  - a configurable, ordered set of behaviors, e.g. *back up*, *wiggle / shake free*, *rotate in place*, *clear the costmap*, *nudge*, *try an alternate path*
  - pick the right recovery for the situation (wedged, bumper-jammed, no valid path, localization lost)
  - for *wedges*, prefer *contact-aware, open-loop* escapes (see *Design direction* above) over Nav2 `spin` / `backup`, which are collision-averse and stall in the wedge; start from the bumper-pattern escape heuristics and tune them in sim
  - post in [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=) to let everyone know you're working on it, and post your progress
- *escalation*
  - if behavior *N* fails, try *N+1*; track attempts per situation so the robot doesn't repeat a recovery forever
- *pause-and-alert*
  - when the whole ladder is exhausted, stop safely, publish a clear *error / status*, and wait for a human or a resume command — *never thrash*
- *safety sensors*
  - detect *cliffs*, *wheel drop*, *pickup / kidnap* (hand pickup detection to [nav-localize](../nav-localize)), and bumper jams; respond by stopping motors / entering a safe state
- *e-stop*
  - a software emergency stop that brings the robot to a safe state immediately
- *status & error reporting*
  - a structured robot-state / error topic, human-readable, suitable for Home Assistant, so a person knows *why* the robot paused
- test it well
  - induce wedged / stuck situations in sim: trap the robot, drop a dynamic obstacle onto it, place it at a cliff edge, lift it
  - verify the ladder runs, escalates, then pauses-and-reports; verify it never thrashes indefinitely
- regression tests (headless, CI-friendly)
  - recovery success rate across induced stuck scenarios
  - guaranteed termination — the robot always reaches *recovered* or *paused-and-reported*, never an infinite loop
  - safety responses fire on cliff / wheel-drop / pickup / e-stop
- submit a PR (pull request) to `contributions/recovery-safety/<your-github-username>/`
  - link to ROS2 package(s)
  - instructions, documentation - how to install, run, configure, troubleshoot, test results
  - videos of recovery, escalation, and pause-and-alert
  - announce your submission in [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=)
- iterate with review
- TBD, expect the RFC to evolve

## Acceptance criteria

Objective, measurable. Examples:
- A configurable *recovery ladder* runs the right behaviors for the situation and frees the robot in the large majority of induced stuck scenarios
- Recoveries *escalate* and are bounded — the robot never repeats a failed recovery forever
- When recovery is impossible, the robot *pauses safely and reports a clear error*, then resumes on command
- *Safety*: cliff, wheel-drop, pickup, and e-stop all bring the robot to a safe state
- *Status / error reporting* is structured and Home-Assistant-friendly
- *Regression tests* pass, including a guaranteed-termination check, runnable headless in CI
- Documented and reliably reproducible by someone else
- TBD, expect criteria to evolve

The maintainer selects among compliant candidates using these criteria. Multiple
attempts are welcome and useful even if not selected — modules are swappable, and
a non-selected design is still a valid learning exercise and a fallback.

---

## File: contributions/part-specs/OsakaTX/README.md

# Part Specs — Compiled Datasheets & Specifications

> **Contributor:** OsakaTX
> **Status:** Updated July 17, 2026 — consolidated batch covering the I/O board wheel connector, caster wheel, side brush motor and brushes, charging contacts, and wall/carpet sensors (new BOM items Jul 12-16). Encoder, gearbox, caster, and connector findings drawn from merged PRs, codetiger/VacuumTiger firmware analysis, and verified calibrations
> **Methodology:** Web research from manufacturer datasheets, public SDKs, open-source reverse-engineering projects (codetiger/VacuumTiger, codetiger/VacuumRobot), physical inspection data from merged contributor PRs (Scowt PR #13), Aliexpress listings, and VacuumTiger firmware empirical calibration
>
> 👉 **See the companion file for detailed methodology, derivation, and cross-referencing:**
> [`vacuumtiger-verified-specs.md`](vacuumtiger-verified-specs.md)

This document compiles the electrical and mechanical specifications found for key BOM
parts. Each section covers what was found and what is still missing.

---

## 1. Drive Wheel Assembly (Roborock S-family)

### Part Identification

The Roborock S4/S5/S5-Max/S6/S7-family drive wheel module is a complete unit:
gearmotor + incremental encoder + rubber tire + suspension + wheel-drop switch.
See BOM.md for the full Aliexpress search links.

### Motor Specs (Nidec 20N704RC70 — "Motor for Wheel")

Sourced from the [Nidec Robot Cleaner Motor catalog PDF](https://file.elecfans.com/web1/M00/CC/89/o4YBAF-ZOBKAQBvyADDMAglsvTw020.pdf):

| Parameter | Value |
|---|---|
| **Rated voltage** | 14.4 V |
| **No-load speed** | 16,800 rpm |
| **No-load current** | 0.4 A |
| **Rated speed** | 13,200 rpm |
| **Rated current** | 1.4 A |
| **Maximum power** | 13.8 W |
| **Noise (SPL)** | 45 dB(A) |
| **Status** | ⚠️ "In development" per Nidec catalog — model may have changed in production |

**Note:** The actual motor used in production Roborock wheels may differ from the
catalog entry above. The wheel module includes a **gearbox** (see estimated ratio below)
that reduces this to the final wheel RPM. The encoder is mounted after the gearbox.

### Wheel Dimensions (from PR #10 — alvarosamudio's URDF, merged)

| Dimension | Value |
|---|---|
| **Wheel diameter** | 0.065 m (65 mm) |
| **Robot body diameter** | 0.33 m (330 mm) |

### Encoder

- **Type:** **Single-channel Hall-effect sensor** (pulse output) — confirmed via physical inspection by Scowt PR #13 (merged). The wheel-side 7-pin JST connector has exactly **one** encoder signal wire (blue), plus 5V (orange) and GND (brown). This is NOT a quadrature A/B encoder.
- **GD32 decoding:** The GD32F103VCT6 hardware timer performs **4× edge counting** on the single pulse train (rising + falling edges × 2 timer channels), producing effective 4× resolution.
- **Effective ticks/rev (wheel shaft):** **~911 ticks/rev** — derived from the calibrated `ticks_per_meter = 4464.0` (VacuumTiger firmware, confirmed in `encoder_sim.rs` unit test) and wheel circumference = π × 0.065 m ≈ 0.2042 m. Calculation: 4464 ticks/m × 0.2042 m/rev ≈ 911 ticks/rev.
- **PPR:** **~228 PPR** (raw encoder pulses per wheel revolution) — 911 / 4 = ~228. Typical multi-pole magnetic ring encoders range from 8-32 poles, making a multi-pole ring a plausible mechanism but this pole count is speculative without physical inspection.
- **Directional ambiguity:** The single-channel encoder alone cannot determine rotation direction. Direction is resolved by the **IMU gyro** in the navigation pipeline (VacuumTiger dhruva-slam).
- **Data format:** u16 LE wrapping counter at offsets 0x10 (left wheel) / 0x18 (right wheel) in the 96-byte GD32 status packet, streamed at 110 Hz. Confirmed from codetiger/VacuumTiger `sangam-io/src/devices/crl200s/gd32/reader.rs` and `SENSORSTATUS.md`.
- **Calibrated odometry:** `ticks_per_meter = 4464.0`, `wheel_base = 0.233 m` — empirically calibrated on real hardware and confirmed in multiple VacuumTiger source files: `sangam-io/src/devices/mock/config.rs`, `dhruva-nav/dhruva.toml`, `dhruva-slam/src/sensors/odometry/wheel_odometry.rs`, and `dhruva-nav/src/odometry.rs`.

### Gearbox Ratio

- **Estimated ratio:** **~190:1** — derived from `VELOCITY_TO_DEVICE_UNITS = 523.0` (empirically calibrated in VacuumTiger `commands.rs`) and the verified max linear speed of 0.3 m/s. At 16,800 rpm no-load motor speed with 0.065 m wheels, a ~190:1 reduction produces a max wheel speed consistent with 0.3 m/s. Confirmed consistent with the calibrated ticks/m and 911 ticks/rev at the wheel shaft.
- **Physical verification:** ❌ **Not yet confirmed via tooth counting** — requires disassembly.

### Connectors

#### Wheel Module Connector (7-pin JST, cable side)

Confirmed by Scowt PR #13 physical inspection (merged):

| Pin | Wire Color | Function |
|-----|-----------|----------|
| 1 | Grey | Limit switch (wheel-drop, NC) |
| 2 | Grey | Limit switch (wheel-drop, common) |
| 3 | Orange | Encoder VCC (+5V) |
| 4 | Blue | Encoder signal (single-channel pulse) |
| 5 | Brown | Encoder GND |
| 6 | Black | Motor power (-) |
| 7 | Red | Motor power (+) |

**Cable length:** ~250 mm (per Scowt observation).

#### Mainboard Connectors (J25/J26 — 16-pin SHD 1.0mm)

Motor power is on **separate connectors** (J24 left / J27 right, 2-pin PH 2.0mm) — NOT carried by J25/J26.

**J25 (bottom, left wheel):** Left wheel encoder (3 wires) + Dustbox power + Left cliff sensor + Left bumper
**J26 (top, right wheel):** Right wheel encoder (3 wires) + Sweeper motor power + Right cliff sensor + Right bumper

Per codetiger/VacuumRobot reverse-engineering. **Full per-pin map** requires PCB continuity tracing (multimeter). The signal groups above are established; the exact pin-to-signal assignment within each group is hypothesis.

### Motor Driver IC

- **IC:** **TMI8870** (TOLL Microelectronic) — top mark "T8870", ESOP8 package
- **Specs:** Single H-bridge, 3.6A peak, 6.8-45V, PWM with integrated current regulation
- **Pin-compatible:** With TI DRV8870
- **Confirmed via:** TMI8870 datasheet (taoic.oss-cn-hangzhou.aliyuncs.com) matching the "8870" marking on U25 on the motherboard (photographed in codetiger/VacuumRobot Component_Diagram.md)
- **Dual ICs:** Since TMI8870 is a single-channel H-bridge and the robot has two wheels, a second matching IC should exist on the motherboard (near J27 for the right wheel)

### Wheel Module Weight

- **Pair:** ~463g (0.463 kg shipped pair — AliExpress listing for original Roborock S6 wheel module)
- **Single:** ~230g (estimated from pair weight)

### What We Still Need

| Item | Status |
|---|---|
| Exact gearbox ratio via tooth counting | ❌ Needs disassembly |
| Full J25/J26 per-pin map | ❌ Needs PCB continuity tracing |
| Wheel-drop sensor exact model | ❌ Needs physical inspection |
| Cable length (exact) | ❌ Needs physical measurement |
| Motor driver IC location for right wheel | ❌ Needs PCB inspection |

### References

- [Nidec Robot Cleaner Motor Product Introduction PDF](https://file.elecfans.com/web1/M00/CC/89/o4YBAF-ZOBKAQBvyADDMAglsvTw020.pdf)
- [codetiger/VacuumRobot — Mainboard Component Diagram](https://github.com/codetiger/VacuumRobot/blob/main/Research/Motherboard/Component_Diagram.md)
- [codetiger/VacuumRobot — Connection Evidence](https://github.com/codetiger/VacuumRobot/blob/main/Research/Motherboard/Connection_Evidence.md)
- [codetiger/VacuumTiger — Custom firmware with verified calibration](https://github.com/codetiger/VacuumTiger)
- [Scowt PR #13 — Wheel module 7-pin connector physical inspection](https://github.com/makerspet/oomwoo/pull/13)
- [TMI8870 Motor Driver Datasheet](https://www.taoic.com/product/28.html)
- Companion file: [`io-board-wheel-connector-and-caster.md`](io-board-wheel-connector-and-caster.md) — OOMWOO I/O board 5-pin ZH wheel connector (J12/J13) and on-board DRV8870 analysis

---

## 2. Suction Fan / Blower

### Nidec 20N-series Blower Units (full assemblies)

From the [Nidec catalog PDF](https://file.elecfans.com/web1/M00/CC/89/o4YBAF-ZOBKAQBvyADDMAglsvTw020.pdf):

#### Standard Blower Units

| Parameter | 20N183L010 A | 20N704R310 / R500 | 20N704S310 | 22N709Q140 |
|---|---|---|---|---|
| Rated voltage | 12 V | 14.4 V | 14.4 V | 14.4 V |
| Rated speed | 15,000 rpm | 17,200 rpm | 15,100 rpm | 22,500 rpm |
| Rated current | 1.7 A | 2.2 A | 1.6 A | 4.5 A |
| Input power | 20 W | 31.7 W | 23 W | 64.8 W |
| Max static pressure | 1.8 kPa | 2.6 kPa | 3.0 kPa | 4.0 kPa |
| Max air volume | 0.63 m³/min | 0.78 m³/min | 0.63 m³/min | 0.99 m³/min |
| Noise | 67 dB(A) | 70 dB(A) | 68 dB(A) | 75 dB(A) |
| Fixing device | With | With | Without | Without |

#### Nidec BLDC Bare Motors (for Blower — without impeller housing)

| Parameter | 20N704K500 B | 20N704P110 A | 22N709P230 D | 35N048P010 |
|---|---|---|---|---|
| Rated voltage | 12 V | 13 V | 14.4 V | 18 V |
| No-load speed | 16,500 rpm | 23,000 rpm | 29,000 rpm | 28,800 rpm |
| No-load current | 0.4 A | 0.6 A | 0.6 A | 0.8 A |
| Rated speed | 13,500 rpm | 16,700 rpm | 24,800 rpm | 25,900 rpm |
| Rated current | 1.4 A | 1.75 A | 2.8 A | 7.1 A |
| Max power | 15.9 W | 21.2 W | 58.4 W | 74.6 W |
| Noise | 40 dB(A) | 50 dB(A) | 65 dB(A) | 60 dB(A) |

### Fan Control Interface (BLDC bare motors)

From the catalog, the Nidec Smart_20N series BLDC motors support:

| Feature | Details |
|---|---|
| **Control** | PWM input for speed control |
| **Feedback** | FG (frequency generator / tachometer) output |
| **Special** | 22N709P230 D additionally has SS (soft-start) |
| **Drive** | 3-phase BLDC — requires external driver/controller |
| **Voltage range** | 5–24 V (Smart_20N series datasheet) |
| **Mass** | 25–30 g (bare motor) |

### Driving the Fans

- The open-source [ripinteer/fan_protector](https://github.com/ripinteer/fan_protector) project is a reference for driving/protecting these BLDC blowers
- The [jniebuhr/roborock-pcb](https://github.com/jniebuhr/roborock-pcb) project has a custom PCB for driving Roborock fans (Nidec-based), including connector pinout
- Connector on the Roborock CPAP board: JST XH 3-pin 2.54mm for motor, JST XH 2-pin 2.54mm for fan

### What We Still Need

| Item | Status |
|---|---|
| Full suction-assembly-level datasheet (including impeller housing) | ❌ Missing (we have bare motor specs) |
| Specific connector model + pinout for each assembly variant | ❌ Unknown |
| Cable length(s) | ❌ Unknown |
| Signal waveforms (PWM, FG) | ❌ Unknown |
| Weight of full assembly | ❌ Unknown |

### References

- [Nidec Robot Cleaner Motor PD](https://file.elecfans.com/web1/M00/CC/89/o4YBAF-ZOBKAQBvyADDMAglsvTw020.pdf)
- [Nidec Smart_20N series product page](https://www.nidec.com/en/product/search/category/B101/M102/S100/NCJ-20N-Type-3/)
- [jniebuhr/roborock-pcb — custom PCB with pinout](https://github.com/jniebuhr/roborock-pcb)
- [ripinteer/fan_protector — BLDC driver reference](https://github.com/ripinteer/fan_protector)

---

## 3. LiDAR — 3irobotix CRL-200S / Delta-2D

### Basic Specs

| Parameter | Value |
|---|---|
| **Model** | 3irobotix CRL-200S (same platform as Delta-2D) |
| **Type** | Laser triangulation (not ToF) |
| **Range** | 0.13–8 m @ 100% reflectivity |
| **Accuracy** | < 1% @ 5m |
| **Scan rate** | 4–10 Hz (configurable via motor voltage) |
| **Points/sec** | 2–5 KHz |
| **Wavelength** | 780 nm |
| **Laser class** | Class 1 |
| **Max ambient** | 1K lux |
| **Weight** | ~175 g |
| **Retail** | ~$28–40 |
| **Life** | Estimated 1,500+ hours |
| **Power** | 0.35A idle, 0.37A ranging @ 5V (~1.75 W) + motor ~0.1A |

### Interface

| Parameter | Value |
|---|---|
| **Connector** | JST PH 2.0mm 5-pin |
| **Data** | UART 115200 baud, 8N1 |
| **Protocol** | Proprietary 3irobotix — 8-byte header + variable payload |
| **Commands** | 0xAE (health), 0xAD (measurement data) |
| **Distance multiplier** | 0.25 mm per LSB |

#### Pinout (J17 on 3irobotix CRL-200S mainboard)

| Pin | Function | Direction | Notes |
|---|---|---|---|
| 1 | Motor+ | Power | 5V for rotation |
| 2 | Motor- | Power | GND |
| 3 | TX | Output | LiDAR → MCU (UART RX) |
| 4 | RX | Input | MCU → LiDAR (UART TX) |
| 5 | GND | Power | Ground |

### Motor Control

| Parameter | Value |
|---|---|
| **Motor voltage range** | 2.2–3.8 V (for valid ranging) |
| **RPM range** | ~240–420 RPM |
| **Motor drive** | Direct voltage control (external) or H-bridge / PWM |
| **Ranging fails below** | ~2.2 V motor (below 240 RPM) |
| **Ranging fails above** | ~4.0 V motor (above 460 RPM) |
| **Angular resolution** | 0.77° at 240 RPM → 1.41° at 420 RPM |

### Data Format (from notblackmagic.com reverse engineering)

Start byte `0xAA`, followed by:
- 2-byte packet type
- Data length
- Angle + distance pairs (each 2 bytes)
- Checksum byte

### References

- [kaiaai/awesome-2d-lidars — comparison table & pinout](https://github.com/kaiaai/awesome-2d-lidars)
- [kaiaai/LDS — Arduino LiDAR library supporting CRL-200S / Delta-2D](https://github.com/kaiaai/LDS)
- [notblackmagic.com — full reverse engineering of Delta-2G (same protocol)](https://notblackmagic.com/bitsnpieces/lidar-modules/)
- [codetiger/VacuumRobot — LiDAR protocol decoding](https://github.com/codetiger/VacuumRobot)
- [3irobotix Delta-2B SDK](https://github.com/CWRU-AutonomousVehiclesLab/Delta-2B-Lidar-SDK)
- Delta-1A protocol doc (shared with 2A/2B/2G/2D): see notblackmagic.com above

---

## 4. VL53L7CX — Multizone Time-of-Flight Sensor

### Basic Specs

| Parameter | Value |
|---|---|
| **Type** | Time-of-Flight, 8×8 multizone (64 zones) |
| **Field of View** | 90° diagonal (65° × 65° typical) |
| **Range** | Up to 350 cm (varies by target reflectivity and ambient) |
| **I²C address** | 0x52 (default) — configurable |
| **I²C speed** | Up to 1 MHz (fast mode+) |
| **Supply** | 2.8 V (AVDD), 1.8 V (IOVDD) or 2.8 V (optional) |
| **Package** | 6.4 × 3.0 × 1.6 mm (LGA-16) |

### Pinout

> **Correction:** earlier revisions of this section listed an 8-pin LGA-12
> (4.4 × 2.4 × 1.0 mm) pinout — those were VL53L5CX figures carried over in error.
> The VL53L7CX (and pin-to-pin-compatible VL53L7CH) is a **16-ball LGA,
> 6.4 × 3.0 × 1.6 mm**. Key signals below; full per-ball map in ST datasheet
> DS13865 §2.5.

| Ball | Name | Function |
|---|---|---|
| B1, B7 | AVDD | 2.8 / 3.3 V analog & VCSEL supply (both must be powered) |
| A4 | IOVDD | 1.8 / 2.8 / 3.3 V digital & I/O supply |
| C1, C7 | GND | Ground |
| C3 | SDA | I²C data (2.2 kΩ pull-up to IOVDD) |
| C4 | SCL | I²C clock (2.2 kΩ pull-up to IOVDD) |
| A3 | INT | Interrupt output (open-drain; 47 kΩ pull-up to IOVDD) |
| A5 | LPn | Comms enable (47 kΩ pull-up to IOVDD) |
| A1 | I2C_RST | I²C interface reset, active high (tie to GND via 47 kΩ) |
| B4 | THERMAL_PAD | Tie to ground plane |
| A2, A6, A7, C2, C5, C6 | Reserved | See DS13865 §2.5 for per-ball tie / no-connect |

### Interface

- **I²C** up to 1 MHz
- Requires external pull-up resistors on SCL/SDA
- Interrupt pin (GPIO1) for data-ready signaling
- LPn pin for low-power mode control

### References

- [ST VL53L7CX Datasheet](https://www.st.com/resource/en/datasheet/vl53l7cx.pdf)
- [STM32duino VL53L7CX Arduino library](https://github.com/stm32duino/VL53L7CX)
- [UM3038 — User guide for using VL53L7CX](https://www.pololu.com/file/0J1993/um3038-a-guide-to-using-the-vl53l7cx-timeofflight-multizone-ranging-sensor-with-90-fov-stmicroelectronics.pdf)

---

## 5. IMU — MPU-6050 (common choice, architecture TBD)

### Basic Specs

| Parameter | Value |
|---|---|
| **Type** | 6-axis (3-axis gyro + 3-axis accelerometer) |
| **Gyro range** | ±250, ±500, ±1000, ±2000 °/s (programmable) |
| **Accel range** | ±2g, ±4g, ±8g, ±16g (programmable) |
| **I²C address** | 0x68 (AD0 low) or 0x69 (AD0 high) |
| **I²C speed** | Up to 400 kHz |
| **Supply** | 2.375–3.46 V |
| **Package** | 4×4×0.9 mm QFN-24 |

### Pinout (QFN-24)

| Pin | Name | Function |
|---|---|---|
| 8 | SCL | I²C clock |
| 9 | SDA | I²C data |
| 12 | AD0 | I²C address select |
| 20 | INT | Interrupt output |
| 6 | VDD | Power (2.4–3.5 V) |
| 18 | VLOGIC | Logic reference (1.8V±5% or VDD) |
| 7, 10, 13, 19, 21 | GND | Ground |

### References

- [MPU-6000/6050 Register Map & Descriptions](https://cdn.sparkfun.com/datasheets/Sensors/Accelerometers/RM-MPU-6000A.pdf)
- [MPU-6050 Product Specification v3.4](https://www.cdiweb.com/datasheets/invensense/mpu-6050_datasheet_v3%204.pdf)

---

## 6. IR Cliff / Proximity Sensors — TCRT5000 (typical)

### Basic Specs

| Parameter | Value |
|---|---|
| **Type** | Reflective optical sensor (IR LED + phototransistor) |
| **Operating voltage** | LED: 1.2–1.5 V forward (typical), Phototransistor: up to 30 V |
| **Detection range** | 0.2–15 mm (for reliable cliff detection) |
| **Output** | Phototransistor collector (open collector — needs pull-up resistor) |
| **Package** | 4-pin DIP (standard leaded package) |

### Pinout

| Pin | Name | Function |
|---|---|---|
| 1 | A (Anode) | IR LED anode |
| 2 | C (Cathode) | IR LED cathode |
| 3 | C (Collector) | Phototransistor collector (output) |
| 4 | E (Emitter) | Phototransistor emitter (GND) |

### Typical Circuit

- IR LED: driven through a current-limiting resistor (e.g., 100–220 Ω at 3.3–5 V)
- Phototransistor: collector pulled up to MCU voltage (3.3 V) via a 10–47 kΩ resistor
- Output read as analog voltage (ADC) or digital threshold (comparator)

### References

- [TCRT5000 datasheet (components101)](https://components101.com/sensors/tcrt5000-ir-sensor-pinout-datasheet)
- [TCRT5000 guide (Utmel)](https://www.utmel.com/components/tcrt5000-ir-sensor-datasheet-pinout-and-circuit?id=697)

---

## 7. Caster Wheel

**⚠️ BOM specifies a Roomba-style caster** (iRobot part #4624869, $2.50-$5 push-in ball-type). The Roborock S-family caster (50mm wheel-type, OEM #9.01.1272/1273) is a different part — verify which is selected before designing the mount.

> **Note on Roborock caster SKUs:** replacement listings cite more than one OEM number for the S-family caster — `9.01.1272/1273` (~50mm wheel, ~45mm base) above, and `HA00021` (~46 × 52mm) in [`io-board-wheel-connector-and-caster.md`](io-board-wheel-connector-and-caster.md). These appear to be the same donor part under different reseller SKUs; the BOM's primary remains the Roomba-style iRobot 4624869. Confirm the exact SKU and dimensions against the selected part before designing the mount.

### Roomba Caster (BOM Source)

| Spec | Value |
|---|---|
| Part number | iRobot 4624869 / RM500CSA |
| Type | Snap-in ball-type caster |
| Wheel diameter | ~25mm |
| Weight | ~30g (plastic) to ~61g (metal+plastic variant) |
| Mounting | Push-in snap-fit |
| Sensor | None (passive) |
| Compatible with | Roomba i3/4/6/8/J7/e5/6/500-900 series |
| Price | $2.50–$12.95 |
| Availability | Abundant (iRobot official, Amazon, aftermarket) |

### Roborock S-family Caster (Alternative / Other BOM Variant)

| Spec | Value |
|---|---|
| OEM part numbers | 9.01.1272 (white) / 9.01.1273 (black) — confirmed via TechPunt.nl, Roborock India |
| Type | Wheel-type caster |
| Wheel height | ~50mm |
| Base diameter | ~45mm |
| Weight | ~45–70g |
| Mounting | Clip-in/pop-in (older models) or bolted-on screw bracket (S5 Max, S6 MaxV, E4+) |
| Sensor | None (passive) |
| Price | $4–17 |

### References

- [TechPunt.nl — OEM part listing](https://www.techpunt.nl/en/robotic-vacuums/robotic-vacuum-parts/20-c2109-roborock-original-spare-parts_141225)
- [Roborock India — OEM parts](https://www.roborockindia.in/)
- [iRobot official store — part 4624869](https://store.irobot.com/)
- [Thingiverse — Roomba caster 3D printable models](https://www.thingiverse.com/)
- [Printables — Roborock caster bracket model (Uko, Jan 2021)](https://www.printables.com/)

---

## 8. Side Brush Motor (Roborock S-family)

> **See detailed spec file:** [`side-brush-charging-contacts-specs.md`](side-brush-charging-contacts-specs.md)

### Motor Type — Verified by Physical Teardown

- **Brushed DC motor** (confirmed by Reddit teardown — carbon brushes, coal dust inside)
- Sintered bronze self-lubricating bushings (not ball bearings)
- 2-stage plastic gearbox with grease
- Contact pads TP1/TP2 on motor PCB (simple 2-wire DC connection)
- Weight: ~150g
- Robot detects stalls via current sensing and shuts off motor as safety precaution

### Firmware Control (VacuumTiger — verified from source code)

| Parameter | Value | Source |
|-----------|-------|--------|
| Command | `CMD_SIDE_BRUSH = 0x69` | `constants.rs` line 19 |
| Speed format | u8, 0-100% | `packet.rs` line 141-149 |
| Component ID | `"side_brush"` | `commands.rs` line 75 |
| Main brush command | `CMD_MAIN_BRUSH = 0x6A` (u8, 0-100%) | `constants.rs` line 20 |

### BOM Variants

| Type | Price | Notes |
|------|-------|-------|
| Fixed | $7-10 | Standard for S5/S6/S7 and many other models |
| Extendable (FlexiArm) | $18-35 | For Qrevo Master/Edge, S8 MaxV Ultra, G20S, V20 |

### Side Brushes

| Type | Price | Notes |
|------|-------|-------|
| 5-arm | $2-8 | S5, S50, S51, S55, S6, S60, S6 Pure |
| 3-arm | $3-9 | S8 and many other models |
| 2-arm curved | $3-7 | Saros |

Attachment: single Phillips #2 captive screw.

---

## 9. Charging Contacts

> **See detailed spec file:** [`side-brush-charging-contacts-specs.md`](side-brush-charging-contacts-specs.md)

### Robot Side

| Spec | Value |
|------|-------|
| Material | Nickel-plated steel strip |
| Dimensions | ~1mm wide, ~0.1mm thick, ~5cm long |
| Price | $1.50-2.50 (pair) |

### Dock Side

| Spec | Value |
|------|-------|
| Type | Gold-plated pogo pins |
| Current rating | 4A |
| Price | TODO (BOM not yet priced) |

### Charging System

| Parameter | Value | Source |
|-----------|-------|--------|
| Dock power supply | 20V DC, 1.2A (24W) | Amazon/AliExpress replacement adapter |
| Dock output at contacts | ~19.8-20V DC | Reddit user measurement |
| Robot battery | 13.5-15.5V (4S Li-ion, 14.4V nominal) | VacuumTiger `constants.rs` |
| Charging method | Contact-based (not inductive) | Standard Roborock |

### Firmware Charging Detection (VacuumTiger — verified from source code)

| Parameter | Value | Source |
|-----------|-------|--------|
| Charging flags offset | 0x07 (byte 7 in status packet) | `constants.rs` line 62 |
| Dock connected flag | bit 0 (0x01) | `constants.rs` line 101 |
| Charging flag | bit 1 (0x02) | `constants.rs` line 100 |
| Battery voltage offset | 0x08 (raw / 10 = volts) | `constants.rs` line 63 |
| Charger power command | `CMD_CHARGER_POWER = 0x9B` | `constants.rs` line 51 |

---

## 10. Wall & Carpet Sensors

> **See detailed spec file:** [`wall-carpet-sensor-specs.md`](wall-carpet-sensor-specs.md)

### Wall Sensor — TSOP38238 IR Receiver + 940nm IR LED

Active reflective proximity sensor: a 940nm IR LED emits 38kHz-modulated bursts and the TSOP38238 detects the reflection off a nearby wall.

| Parameter | Value | Source |
|-----------|-------|--------|
| IR receiver | Vishay TSOP38238 — 38kHz carrier, 2.0-5.5V, ~0.25mA typ, ±45° directivity, active-low output | Vishay datasheet (doc 82491) |
| AGC variant | AGC2 (long-burst; drive LED with ≥10 cycles/burst) | Vishay datasheet (doc 82491) |
| IR LED (representative) | Vishay TSAL6100 — 940nm, 170mW/sr, ±10° beam, 1.35V typ Vf, 100mA DC | Vishay datasheet (doc 81009); exact BOM part not specified |
| Output | Active-low digital pulse when a valid 38kHz reflection is detected | TSOP38238 datasheet |
| Premium alternative | VL53L7CX multizone ToF (true distance vs binary detection) | BOM note |

### Carpet Sensor — Ultrasonic 300kHz

| Parameter | Value | Source |
|-----------|-------|--------|
| Transducer | HTW HT-300PLTR1612-1 — 290±15kHz center, 16mm diameter, ~30mm target distance, ≤2mm precision, IP67 | HTW listing (Made-in-China) |
| OEM alternative | Roborock OEM carpet sensor module (~$6.93) | AliExpress listing |
| Principle | Echo-characteristic analysis to distinguish carpet from hard floor | — |

---

## Summary of Found vs Missing

| Part | Documentation Found | Critical Gaps |
|---|---|---|
| **Drive wheel** | Motor electrical specs (Nidec 20N704RC70), wheel diameter (65mm), encoder type (single-channel Hall, ~228 PPR), gearbox ratio (~190:1), ticks/meter (4464), wheel module connector (7-pin JST with wire colors), mainboard connectors (J25/J26 16-pin SHD signal groups, J24/J27 2-pin PH motor power), motor driver IC (TMI8870), module weight (~463g pair) | ❌ Exact gearbox ratio via tooth count, ❌ full J25/J26 per-pin map, ❌ wheel-drop sensor model |
| **Suction fan** | Complete Nidec catalog specs for many models, PWM/FG control, connector types (JST XH) | ❌ Impeller housing geometry, ❌ assembly weight, ❌ cable lengths |
| **LiDAR (CRL-200S)** | ✅ Full specs, pinout, protocol, RPM/voltage relationship, power consumption | Minor: exact motor model inside, formal manufacturer datasheet |
| **VL53L7CX ToF** | ✅ Full datasheet, I²C interface, pinout, FOV, range | None — standard ST component |
| **IMU (MPU-6050)** | ✅ Full datasheet, I²C interface, pinout | None — standard InvenSense component |
| **IR cliff sensor** | ✅ TCRT5000 specs, circuit, pinout | None — standard sensor, subject to final BOM choice |
| **Caster wheel** | ✅ Roomba 4624869 specs (25mm ball-type, push-in); Roborock 9.01.1272/1273 (50mm wheel-type); both passive | ❌ Exact dimensional drawing for chosen variant; ❌ which BOM variant is selected |
| **Side brush motor** | Brushed DC motor (teardown-verified), 2-stage plastic gearbox, TP1/TP2 contacts, ~150g, CMD 0x69 speed 0-100%, fixed ($7-10) and FlexiArm ($18-35) variants | ❌ Exact voltage, ❌ RPM, ❌ exact gearbox ratio, ❌ connector model |
| **Side brushes** | 5-arm ($2-8), 3-arm ($3-9), 2-arm curved ($3-7); single Phillips screw attachment | None — standard consumable part |
| **Charging contacts (robot)** | Nickel-plated steel strip, ~1mm×0.1mm×5cm, $1.50-2.50/pair | None — standard material |
| **Charging contacts (dock)** | Gold-plated pogo pins, 4A rating; dock outputs 20V 1.2A (24W) | ❌ Exact pogo pin model/part number, ❌ pricing |
| **Charging system** | CMD_CHARGER_POWER=0x9B, charging flags at offset 0x07 (dock=0x01, charging=0x02), battery voltage at 0x08 | None — firmware-level specs complete |
| **Wall sensor** | TSOP38238 IR receiver (38kHz, AGC2, ±45°, active-low) + 940nm IR LED (TSAL6100 representative); reflective proximity design | ❌ Exact IR LED part in BOM, ❌ detection-range tuning |
| **Carpet sensor** | HTW HT-300PLTR1612-1 ultrasonic (290±15kHz, IP67, ~30mm range); Roborock OEM module alternative | ❌ Manufacturer datasheet for exact BOM part, ❌ echo-analysis thresholds |

---

## File: contributions/part-specs/README.md

# Procure Part Specs & Datasheets for Sourced Components

We've already sourced candidate parts (see [BOM.md](../../BOM.md)). To design the I/O
board and firmware, *drive the motors and fans*, and build accurate mounts, we need each
part's detailed *electrical + mechanical specs* — pinouts, voltages, currents, encoder
PPR, torque, waveforms, etc. Vendors rarely publish these for vacuum sub-assemblies, so
contributors will *find datasheets, ask vendors, or safely reverse-engineer* them.

This is the *electrical/mechanical data* companion to
[source-3d-models](../source-3d-models) (which covers the *geometry*). No robotics
background needed — a multimeter, patience, and (for reverse-engineering) an oscilloscope go far.

Please check [BOM.md](../../BOM.md) and the [sourcing follow-along blog post](https://makerspet.com/blog/how-to-source-bom-for-oomwoo-open-source-vacuum-robot/)
for datasheets/specs already found.

## What we need, per part

### Drive wheel assembly (Roborock-family — see BOM)
- motor model; motor/assembly *datasheet* (if any)
- *encoder type + PPR* (pulses per revolution)
- *gearbox ratio*; *wheel diameter*
- rated + max motor *voltage*, *current* (no-load & stall), *torque*
- max / rated wheel *speed*
- *cable length(s)*; *connector models* (both ends); *full connector + motor pinouts*
- *wheel-drop sensor* model + pinout (these modules include one)
- signal *waveforms* (encoder channels, motor drive)
- assembly *weight*

### Suction fan / blower (several options sourced — see BOM)
- fan/motor model; *datasheet*
- rated *voltage, current, RPM*; *airflow* + *static pressure (Pa)*
- *how to drive it* — BLDC driver + control interface (PWM / tach / hall / 3-phase),
  soft-start / protection behaviour
- *connector model(s) + pinout*; cable length
- signal *waveforms*
- *weight*

### Caster / universal wheel assembly (Roomba-family — see BOM)
- model, dimensions, mounting, any embedded sensor, weight, datasheet

## Already found vs still missing 

*Found*
- *Fan datasheet (some options):* https://file.elecfans.com/web1/M00/CC/89/o4YBAF-ZOBKAQBvyADDMAglsvTw020.pdf
- *Nidec BLDC motors* (candidate fan motors — *bare motors, not the full suction assemblies*):
  [NCJ-20N Type-3](https://www.nidec.com/en/product/search/category/B101/M102/S100/NCJ-20N-Type-3/),
  [NCJ-20N Type-4](https://www.nidec.com/en/product/search/category/B101/M102/S100/NCJ-20N-Type-4/)
- *Driving the fans:* [ripinteer/fan_protector](https://github.com/ripinteer/fan_protector)
  — reference for driving / protecting the BLDC blower (from earlier project research)

*Still missing (help wanted)*
- Connector *pinouts* + cable lengths for wheels, fans, caster
- *Encoder type + PPR*, gearbox ratios, torque/current under load
- *Wheel-drop sensor* model + pinout
- Signal *waveforms* (encoder, motor/fan drive)
- Suction-*assembly*-level datasheets (we have some bare motors, not the assemblies)
- Weights; caster specs

## Reverse-engineering — only if specs can't be found, and SAFELY

If a spec isn't published, reverse-engineer it by opening an existing vacuum and probing.
*Safety first:*
- Don't do it unless you're qualified, experienced.
- Opening a vacuum usually *voids the warranty* and *can damage it* — accept that risk knowingly.
- *Secure / prop up the vacuum* so it can't scoot off the table or bench when the wheels or
  fan spin during testing (clamp it, or raise the wheels off the surface).
- Respect the *Li-ion battery* — don't short, pierce, or stress the pack; disconnect where sensible.
- Mind *pinch points and spinning parts* — keep fingers/hair clear of the impeller and brushes.
- Any *mains-connected* testing (e.g. a dock) — extra caution; isolate.
- Use a *multimeter + oscilloscope* to capture voltages, currents, and waveforms; trace and
  *label connector pinouts*; photograph everything.

*Legal:*
- By performing any work for this project including reverse engineering you agree to
  - wave liability, indemnify this project, the legal entity behind it (Remake AI Statutory Trust) and and its founder
  - contribute your work and results thereof, if any, as open-source, to be published under Apache 2.0 license

## Submit

A PR to `contributions/part-specs/<your-github-username>//`:
- a spec sheet (markdown table) with everything you found
- any datasheets (PDF) and source links
- photos of connectors + labelled pinouts
- waveform captures where reverse-engineered
- provenance — which vendor / model / revision the data is from
- announce it in [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=)

## Acceptance criteria

- Spec sheet for a part (or a clearly-scoped subset)
- Datasheets / links where found
  - pinouts + waveforms where reverse-engineered when appropriate
- Provenance stated (part / vendor / revision)
- Verifiable by someone else with the same part
- TBD, expect criteria to evolve

The maintainer selects among compliant candidates using these criteria. Multiple attempts
are welcome and useful even if not selected — a non-selected spec sheet is still a valid
reference and a fallback.

---

## File: contributions/obstacle-avoidance/README.md

# Near-Field Obstacle Avoidance: camera + ToF (ROS2 package)

Stop the robot getting stuck on the things a 2D LiDAR *cannot see*. The turret
LiDAR is blind below its ~10 cm scan plane — cables, socks, shoes, pet bowls,
thresholds — which is the *top real-world complaint* about robot vacuums. This
package adds *near-field forward perception* from a *front camera* and/or a
*multizone Time-of-Flight (ToF) sensor*, detects obstacles in front of the robot,
and publishes them so navigation and recovery can react. Because the physical
robot isn't built yet, the *software* is developed in a *Gazebo simulation*; it is
later re-validated on hardware in the [live-robot-bringup RFC](../live-robot-bringup).

> *Status — ready to start work (forward-looking / experimental).* This is the
> v2 "never gets stuck" direction, not a v1 promise — the v1 fallback for low
> obstacles is the *bumper* ([recovery-safety](../recovery-safety)). Develop the
> software in the Gazebo sim ([urdf-gazebo-sim](../urdf-gazebo-sim)); say so in the
> [discussions](https://github.com/makerspet/oomwoo/discussions) so we can coordinate.

> *Two tracks in this RFC.* (1) A *software* track — the sensor-agnostic ROS2
> package that detects and publishes obstacles (sim-first). (2) A *hardware* track —
> building and benchmarking the candidate front-depth sensors to decide which one
> the robot ships. They meet at a shared obstacle interface: the software must not
> care *which* sensor produced the depth/obstacle data.

# Important References

- [recovery-safety RFC](../recovery-safety) — the v1 bumper-based reaction this augments; a detected obstacle should feed the recovery/avoidance ladder.
- [clean-and-map RFC](../clean-and-map) / [cleaning-jobs RFC](../cleaning-jobs) — coverage/nav that consumes the obstacle signal to slow, steer, or replan.
- [floor-care RFC](../floor-care) — reactive wall/edge *following*: the near-field neighbor to obstacle *avoidance* (follow a boundary vs. steer around it), same reactive-sensing lineage.
- [urdf-gazebo-sim RFC](../urdf-gazebo-sim) — robot URDF; this package needs a *front camera* and a *front ToF* modeled.
- [ROS2 software interfaces](../../docs/SOFTWARE_INTERFACES.md) — shared topic/action/service contract for simulation-first modules.
- [OOMWOO ROS2 development](https://github.com/makerspet/oomwoo-install) — build OOMWOO ROS2 Docker image(s) with your packages.
- [Project discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=) · [Discord](https://discord.gg/3y2JKz5T25)

# Front sensor — three candidate approaches (under evaluation)

The 2D LiDAR handles 360° mapping/localization. This module fills the *below-LiDAR,
forward* gap, which needs a **wide horizontal FoV (target ≥120°)** so obstacles
across the robot's path — not just dead ahead — are seen. There are three
candidates; **which one (or combination) ships is exactly what the hardware track
below is meant to decide.**

### A. Stereo depth — 2× OV5647 + IR pattern illuminator

- Two rolling-shutter cameras with **wide lenses (target 120–160° horizontal)**,
  computing dense depth by disparity; doubles as the semantic camera.
- **Textureless surfaces** (blank walls/floors) give stereo no features → pair it
  with an **840 nm random-dot pattern illuminator** (eye-safe *LED* + engineered
  diffuser or a printed random-dot mask; **not** a laser projector — avoids the
  Class-1/DOE-interlock burden). Short range is fine; that's all we need.
- **Sync is mandatory and not automatic**: two *independent* OV5647 on the two CSI
  ports free-run and won't genlock. Drive frame-sync over a **shared I²C control
  line, broadcasting a single write** to both sensors (+ FREX/snapshot mode). Naive
  "2× cameras" stereo is a known trap.
- Cost: uses **both CM4/CM5 CSI lanes** and real depth-matching compute (competes
  with SLAM/NPU on the Pi). Widest FoV, richest data.

### B. Multizone ToF — 2× VL53L7CX/CH, angled ±30°

- Each VL53L7CX/CH is an 8×8 multizone ToF with **~60° horizontal FoV** (the "90°"
  in the datasheet is *diagonal*). Place **two side-by-side, one aimed 30° left, one
  30° right → ~120° horizontal total.**
- **I²C, not CSI** → frees both camera lanes, near-zero compute (128 range zones,
  not image frames). **Class-1 eye-safe** (VCSEL sealed in the module). **Works in
  total darkness and on textureless/blank surfaces** — no illuminator, no pattern.
- Sourcing: bare **VL53L7CX/CH ~$5.50 at DigiKey**; order **unpopulated carrier
  PCBs**, populate and **reflow at home**. ⚠️ It's an **optical LGA** — keep flux
  off the aperture and follow ST's reflow profile (this part has a cover-glass;
  no-clean, no post-wash near the window).
- Trade-off: coarse (8×8 per sensor) — great for *"something is there, this far,
  this tall"*, poor at *shape/contour* and no semantics.

### C. Both combined

- ToF for reliable, dark/blank-proof **geometry**; stereo (or a single wide camera)
  for **wide semantics + dense shape**. Fuse them. Most capable, most cost/compute.

> *Sim modeling.* Gazebo has no VL53L7CX model — approximate a multizone ToF with a
> *low-resolution depth camera* or a small *ray grid* over the same FoV/zones, and
> the camera(s) with standard Gazebo camera sensors. Add sensors *inside your
> submission* and propose folding the stable ones back into
> [urdf-gazebo-sim](../urdf-gazebo-sim) later — please don't rewrite that RFC.

# Hardware track — build & benchmark the candidates

**Goal:** determine, on real obstacles, which of **A / B / C** best detects the
near-field, below-LiDAR obstacles — and whether one suffices *alone* or they must
be *combined*. This produces a data-backed sensor decision for the BOM.

### Build notes

- **B (2× ToF)** is the cheapest, lowest-risk build (I²C, no CSI, no illuminator,
  eye-safe, dark/blank-proof) — a natural *first* thing to characterize. Mount two
  carriers at ±30°, verify the seam/overlap near boresight, and publish combined
  zones.
- **A (stereo)** needs the sync harness (shared-I²C broadcast + FREX) and the
  **840 nm illuminator**; validate *passive* stereo first (textured obstacles) then
  measure how much the pattern illuminator helps on blank floors/walls.
- Whichever is characterized, publish the **same obstacle interface** (below) so the
  software track and Nav2 don't care which sensor won.

### Test protocol — obstacles (primary)

Model the obstacle set on **[how Vacuum Wars tests obstacle avoidance](https://vacuumwars.com/how-vacuum-wars-tests-robot-vacuums/)**
so results are comparable to commercial robots:

- ~**24 objects** across categories, scored **1 point per object avoided** (their
  scale), run at max sensitivity. Categories include **cords/cables, socks, shoes,
  simulated pet waste**, plus a **"torture" pass** mixing everything — deliberately
  including **objects unlikely to be in any trained image library** (this is where
  ToF's geometry-only detection can *beat* a camera that relies on recognition).
- Include their **rug cases**: a *solid-color rug with same-color items* and a
  *patterned rug with a contrasting item* — both stress depth/segmentation.
- Add OOMWOO-specific cases the reviews under-cover: a **cable lying flat**, a **~2 cm
  threshold**, and a **cliff/edge** in-frame.
- **Run every case in two lighting conditions — normal light and full darkness** —
  and on **≥2 floor types**. Darkness and blank surfaces are the decisive
  differentiators: ToF is lighting-independent; stereo needs the IR illuminator and
  texture.

### Test protocol — object recognition (secondary)

Lower priority than raw obstacle avoidance. Where a camera is present, measure
**classification accuracy** for the categories above (feeds semantic behaviors like
"never smear pet waste — stop and alert"). A miss here should still *avoid* the
object as an unknown obstacle; recognition only changes the *reaction*, not the
*detection*.

### Metrics to report (per approach)

- **Detection / avoidance rate** per obstacle type (true-positive %), and the
  aggregate Vacuum-Wars-style score (/24).
- **False-positive rate** on clear floor (does normal cleaning get disrupted?).
- **Min detection distance & reaction distance**; blind spots / FoV coverage across
  the ≥120° target (map the seam between the two ToFs / stereo edges).
- **Darkness** and **blank-surface** performance deltas vs. normal.
- **Depth accuracy** vs. ground truth; **latency / frame rate**.
- **Pi compute load** (CPU/NPU %) — a real cost for stereo.
- **Integration cost**: CSI lanes used, power, BOM $, build/reflow difficulty.

### Decision framework

Pick the approach that clears the acceptance criteria at the **lowest cost +
compute + integration burden**. Expected shape of the answer: **B (2× ToF) as the
robust, cheap, dark/blank-proof geometry baseline**; add **A (camera/stereo)** if
semantic recognition or shape/contour proves necessary → i.e. **C** for the premium
tier. Let the data decide; publish it so the choice is reviewable.

# ROS2 obstacle interface (shared by both tracks)

Whichever sensor is used, publish obstacles in `base_link`: **distance + bearing /
coordinates**, plus `object_type` (`unknown` in Phase 1; typed in Phase 2), and/or
a `PointCloud2` / `Range`-grid the nav stack can consume as a costmap layer. Feed
[recovery-safety](../recovery-safety) / navigation — *coordinate the interface
rather than driving `cmd_vel` directly* where a nav layer owns it.

# Request for Contribution — Instructions

- *add the sensors to the sim robot*
  - front camera (image topic) and front ToF (zone/range topic), co-located and
    calibrated to a shared front frame
  - post in [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=) to let everyone know you're working on it, and post progress
- *Phase 1 — detect, don't classify*
  - from the ToF (primary) and camera, detect *any* obstacle in the forward
    near-field and publish it: *distance + bearing / coordinates in `base_link`*,
    with *object type = unknown*. Free-space vs blocked is enough to be useful.
  - the point is coverage of the *below-LiDAR* zone the 2D scan misses
- *Phase 2 — classify (later)*
  - run object detection on the camera, fuse with the ToF, and publish *typed*
    detections (cable, sock, pet-waste, threshold, furniture …) with confidence.
    This is the NPU-relevant workload; keep it optional/pluggable.
- *react*
  - feed detections to navigation / [recovery-safety](../recovery-safety): slow,
    steer around, or stop-and-alert (e.g. never smear pet waste). Coordinate the
    interface rather than driving `cmd_vel` directly from here where a nav layer owns it.
- *(hardware track)* build a candidate front sensor (A/B/C above), run the test
  protocol, and publish the metrics table so the sensor choice is data-backed.
  Coordinate in [Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=)
  before ordering parts so effort isn't duplicated.
- test it well
  - build worlds with *low obstacles the LiDAR can't see* (cable on the floor,
    sock, shoe, a 2 cm threshold) and verify they are detected and avoided
  - verify the robot still cleans normally when the near-field is clear (low false-positive rate)
- regression tests (headless, CI-friendly)
  - detection rate on a set of placed low obstacles (true-positive %)
  - false-positive rate on clear floor
  - "did not run over the flagged obstacle" success rate
- submit a PR (pull request) linking your ROS2 package(s) + sim sensor/world additions
  - instructions, documentation — install, run, configure, troubleshoot, test results
  - videos of low-obstacle detection and avoidance
  - announce your submission in [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=)
- iterate with review
- TBD, expect the RFC to evolve

## Acceptance criteria

Objective, measurable. Examples:
- *Below-LiDAR obstacles* (cable, sock, low threshold) are *detected* and
  published with distance/bearing, and the robot *avoids running them over*
- *Low false-positive rate* on clear floor — normal cleaning is not disrupted
- Phase 1 works *without* classification; Phase 2 classification is optional/pluggable
- *Regression tests* pass (detection %, false-positive %, avoidance success), runnable headless in CI
- Works in at least one world seeded with LiDAR-invisible obstacles
- *(hardware track)* a candidate is characterized against the Vacuum-Wars-style
  protocol *in light and darkness*, with the metrics table filled in — enough to
  compare it against the other approaches
- Documented and reliably reproducible by someone else
- TBD, expect criteria to evolve

The maintainer selects among compliant candidates using these criteria. Multiple
attempts are welcome and useful even if not selected — modules are swappable, and
a non-selected design is still a valid learning exercise and a fallback.

---

## File: contributions/nav-localize/README.md

# Localization & Navigation on a Known Map (ROS2 package)

Once the [first clean](../clean-and-map) has produced a map, the robot must be able
to *operate on that saved map*: localize itself, navigate to any goal with Nav2,
recover its pose when it gets *lost* or is picked up and moved (the *kidnapped
robot* problem), and *resume an unfinished map*. Because the physical robot isn't
built yet, this is a *Gazebo simulation*; it is later re-validated on hardware in
the [live-robot-bringup RFC](../live-robot-bringup).

> *Status — ready to start work.* No need to wait for OOMWOO hardware — develop it in the
> Gazebo sim ([urdf-gazebo-sim](../urdf-gazebo-sim)) or on the real
> [placeholder Proscenic M6 Pro](https://makerspet.com/blog/tutorial-connect-robot-vacuum-cleaner-to-ros-2-proscenic-m6-pro/).
> Say so in the [discussions](https://github.com/makerspet/oomwoo/discussions) so we can coordinate.

# Important References
- [clean-and-map RFC](../clean-and-map) — produces the saved/partial map this package consumes.
- [urdf-gazebo-sim RFC](../urdf-gazebo-sim) — robot URDF, Gazebo world(s), bumper.
- [ROS2 software interfaces](../../docs/SOFTWARE_INTERFACES.md) — shared topic/action/service contract for simulation-first modules.
- [Gazebo + Nav2 simulation tutorial](https://makerspet.com/blog/tutorial-map-navigate-ros2-robot-in-simulation/) — baseline diff-drive + LiDAR robot in a Gazebo world.
- [OOMWOO ROS2 development](https://github.com/makerspet/oomwoo-install) — build OOMWOO ROS2 Docker image(s) with your packages.
- Nav2 (navigation), AMCL (localization), and slam_toolbox (localization mode / serialized session continue) are the expected building blocks.
- [Project discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=)
- [Discord server](https://discord.gg/3y2JKz5T25)

# Request for Contribution - Instructions

- reproduce the baseline first
  - load a map saved by [clean-and-map](../clean-and-map) and bring up Nav2 + a localizer (AMCL or slam_toolbox localization mode) on the [urdf-gazebo-sim](../urdf-gazebo-sim) robot
  - post in [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=) to let everyone know you're working on it, and post your progress
- localization on a known map
  - *global initial localization at startup* without being given a pose (e.g. AMCL global init / scan-matching) — the robot figures out where it is on the saved map
  - track pose reliably during navigation; expose a localization-confidence signal (covariance / scan-match score)
- navigation
  - Nav2 navigate-to-pose and navigate-through-poses to arbitrary goals on the saved map
  - obey dynamic obstacles via the local costmap
- lost / kidnapped recovery
  - *detect* when localization confidence drops (low score, high covariance, or a detected pickup/kidnap)
  - *relocalize*: rotate in place and/or drive to gather scans until the pose re-converges
  - define a clear *"relocalized" success condition*, and what happens if it *fails* — hand off to the [dock-cycle](../dock-cycle) *find-the-dock-when-lost* fallback
- resume an unfinished map
  - load a *partial / serialized SLAM session* (e.g. slam_toolbox serialization) and *continue mapping where it left off*, merging newly seen areas into the existing map without corrupting it
- test it well
  - start from *many initial poses*, including a wrong or unknown initial pose
  - *kidnap* the robot mid-run (teleport it in sim) and verify it recovers
  - resume from several partial maps and verify the merged map is correct
- regression tests (headless, CI-friendly)
  - relocalization success rate from random poses
  - navigation success rate to random reachable goals
  - map-resume correctness (resumed + continued map matches a from-scratch map of the same world)
- submit a PR (pull request) to `contributions/nav-localize/<your-github-username>/`
  - link to ROS2 package(s)
  - instructions, documentation - how to install, run, configure, troubleshoot, test results
  - videos of relocalization-when-lost and map-resume runs
  - announce your submission in [Project Discussions](https://github.com/makerspet/oomwoo/discussions?discussions_q=)
- iterate with review
- TBD, expect the RFC to evolve

## Acceptance criteria

Objective, measurable. Examples:
- On a saved map, from an *unknown initial pose*, the robot performs global localization and converges to the correct pose
- Nav2 navigation reaches arbitrary reachable goals reliably, avoiding dynamic obstacles
- When *lost / kidnapped*, the robot detects it, relocalizes, and resumes — or cleanly hands off to the find-the-dock fallback when relocalization fails
- An *unfinished map* can be loaded and mapping continued, producing a complete, uncorrupted map
- *Regression tests* pass and verify relocalization, navigation, and map-resume, runnable headless in CI
- Documented and reliably reproducible by someone else
- TBD, expect criteria to evolve

The maintainer selects among compliant candidates using these criteria. Multiple
attempts are welcome and useful even if not selected — modules are swappable, and
a non-selected design is still a valid learning exercise and a fallback.

## 2. Official Technical Reference & Guides (makerspet/oomwoo-io-firmware)

MCU firmware for the OOMWOO [I/O board](https://github.com/makerspet/oomwoo-io-board),
targeting an **STM32G473VCT6**. Arduino (STM32duino) API on top, FreeRTOS for task
structure, and a HAL/timer-ISR real-time core underneath.

> **Status — RFC / not started.** This is a request for contribution: the plan
> below is the design intent, and the code doesn't exist yet. Say hi in
> [Discussions](https://github.com/makerspet/oomwoo/discussions) or on
> [Discord](https://discord.gg/3y2JKz5T25) if you want to build it.

OOMWOO splits compute across two processors ([ARCHITECTURE.md §5.4](https://github.com/makerspet/oomwoo/blob/main/docs/ARCHITECTURE.md)):
a **CPU** (CM4/CM5-class module) runs ROS 2, SLAM, Nav2, and behaviour; the **MCU**
on this I/O board owns *motors, encoders, sensors, battery charging, and hard
safety*. This repo is that MCU firmware. Its defining constraint: **safety must
never depend on Linux/ROS 2** — and, as you'll see below, it must never depend on
the friendly Arduino layer either.

## Why STM32G473VCT6

The board drives ~10 actuators and reads a dozen-plus analog channels, so the MCU
needs headroom the earlier STM32G0 (Cortex-M0+, no FPU) didn't comfortably have:

- **Cortex-M4F @ up to 170 MHz with an FPU** — real control math (PID, filters,
  odometry) in hardware float, with cycles to spare for FreeRTOS + the Arduino layer.
- **Many timers incl. HRTIM** — enough PWM channels for every motor, plus the
  high-resolution timer for clean BLDC/fan drive.
- **5× 12-bit ADCs** — the board has many simultaneous analog channels (per-motor
  current sense, `VBat`, source current, 4× cliff IR, 2× dock IR, 2× side IR); five
  ADCs let safety-critical currents be sampled fast and independently.
- **CORDIC + FMAC** math accelerators, 512 KB flash / 128 KB RAM, **LQFP100** (hand-
  solderable, JLCPCB-friendly).

STM32duino supports the G4 family (Nucleo-G474 is a good bring-up board), and
FreeRTOS is available via the `STM32FreeRTOS` library — so "Arduino API + FreeRTOS"
is a supported, real combination on this part.

## Architecture — three layers (the core design decision)

The usual "friendly Arduino **or** deterministic real-time safety" trade-off is a
false choice here. We get both by **layering**, so the layer a contributor touches
is not the layer that keeps the robot safe:

| Layer | What | Owned by | Determinism |
|---|---|---|---|
| **3 — Arduino (STM32duino) API** | New behaviours, features, and peripheral bring-up live here. This is the contributor-friendly surface. | community | best-effort |
| **2 — FreeRTOS tasks** (static allocation) | Task structure: CPU-serial comms, control loop, telemetry, charging supervisor, safety supervisor. Watchdog-fed, bounded reaction times. | maintainer + community | soft real-time |
| **1 — Real-time core** (HAL + timer ISRs) | Motor commutation/PWM, encoder capture, the hard-safety cutoffs, the CPU watchdog. Runs in hardware timers / ISRs. | maintainer, safety-reviewed | **hard real-time** |

**The rule that makes this safe:** contributor code lives at the Arduino level;
the motor-control and safety core is HAL/ISR and **structurally isolated** from it.
A bug or an infinite loop in someone's Arduino feature **cannot** defeat a cliff
stop, an overcurrent cutoff, or the CPU watchdog — those live in interrupts and a
hardware watchdog that the upper layers cannot starve. That's how you get the
community-attraction of Arduino *and* the real-time integrity a robot needs.

## Safety (non-negotiable, CE-oriented)

Hard safety runs on the MCU, independent of *both* Linux/ROS 2 and the Arduino
layer:

- **Bumper / cliff / wheel-drop → immediate motor stop**, at ISR level.
- **Per-motor overcurrent limiting** — a stuck brush, jammed wheel, or stall is
  current-limited or cut before thermal/mechanical damage.
- **CPU watchdog** — if the CPU's periodic health packets stop, the MCU stops the
  motors and can assert the CPU-reset line.
- **MCU independent watchdog (IWDG)**, static memory allocation, and *measured,
  documented* worst-case reaction times.

Per the project's safety-review gate, **safety-critical changes require maintainer
review before merge** and a short hazard note (over-current, thermal, short,
mechanical pinch).

## What the firmware owns

Source of truth is the board [SPEC.md](https://github.com/makerspet/oomwoo-io-board/blob/main/docs/SPEC.md)
(work in progress — treat it as authoritative over this summary, and note its open
TODOs, e.g. the GPIO 36/46 bumper-label question):

- **Actuators:** 2× drive wheels (H-bridge + hall encoders), suction fan (BLDC, PWM
  + FG feedback), main brush, side brush(es), 2D-LiDAR spin motor, water pump, mop
  motors, and the mop-lift / mop-arm / side-brush-arm servos — each drive path with
  current sense where the board provides it.
- **Sensors:** 4× cliff IR, 2× dock IR, 2× side-proximity IR (+ their IR-LED PWM),
  2× bumper switches, 2× wheel-drop switches, wheel encoders, IMU (SPI + interrupts
  + FSYNC), and the current-sense/`VBat`/source-current analog channels.
- **Power & charging:** supervise the power-path charger (0.5C charge cap, input
  DPM, USB-C PD *and* dock input, graceful "insufficient charger" handling), plus
  `motors power enable`, `vacuum power`, `CPU power on/off`, and `CPU reset`.
- **HMI:** power/home buttons and power/home LEDs.

## CPU ↔ MCU link

A **custom serial protocol over UART** — deliberately **not** micro-ROS, so the
safety core carries no heavyweight third-party dependency (and no risk of an
upstream library update slipping a bug into safety-critical firmware). The framing,
command set, telemetry, and health/watchdog handshake are being defined in the
[io-board-interface RFC](https://github.com/makerspet/oomwoo/tree/main/contributions/io-board-interface).
The MCU: accepts **bounded, expiring** commands (drive setpoints, motor/actuator
commands); publishes telemetry (encoders, battery/charge state, bumper/cliff/
wheel-drop, per-motor current); and enforces the health handshake that backs the
CPU watchdog. During development, the CPU side can be stood in for by the simulated
MCU serial tool in [oomwoo-install](https://github.com/makerspet/oomwoo-install).

## Toolchain

- **STM32duino** (the STM32 Arduino core) via the Arduino IDE **or** PlatformIO;
  G473 through the generic-G4 board definition (bring up on a Nucleo-G474 first).
- **FreeRTOS** via `STM32FreeRTOS`.
- **SWD** debug (ST-Link) on the board's `SWDIO`/`SWCLK` header; test/program pads.

## Request for contribution — bring-up milestones

Phased, each testable on the bench before the board even exists (start on a
Nucleo-G474, move to the real board when it's fabbed):

1. **Blink + SWD + serial echo** on a G473 dev board.
2. **CPU serial link** — implement the io-board-interface framing + health/watchdog
   handshake; loopback and echo tests green.
3. **One drive motor, closed loop** — H-bridge PWM + encoder capture + velocity PID
   in the real-time core. This is the pattern every other motor follows.
4. **All actuators** — fan (BLDC + FG), brushes, LiDAR spin, pump, mop motors/servos,
   each with current sense.
5. **All sensors** — cliff/dock/side IR (ADC), bumpers, wheel-drop, IMU (SPI),
   current channels.
6. **Safety layer** — ISR-level cliff/bumper/wheel-drop stop, overcurrent limiting,
   IWDG, CPU watchdog/reset; **measure and document** each cutoff's worst-case
   reaction time; hazard note.
7. **Charging supervisor** — power-path charger control, 0.5C cap, input DPM,
   insufficient-charger handling.
8. **Integration** — run end-to-end against the CPU (or the simulated MCU serial
   tool) driving the ROS 2 hardware bridge.

## Acceptance criteria

- **Deterministic real-time core** — measured, documented worst-case reaction time
  for each safety cutoff.
- **Safety is layer-independent** — a deliberately hung Arduino-level task must
  *not* defeat a cliff-stop, overcurrent cutoff, or the CPU watchdog. Demonstrate it.
- **Implements the io-board-interface serial contract** — loopback + integration
  tested.
- **Every actuator and sensor exercised on the bench**, documented, reproducible by
  someone else.
- **Charging behaves per spec** — 0.5C cap held; graceful degradation on a weak
  charger.
- **Safety-critical code passed maintainer safety review** with a hazard note.

## References

- Board: [oomwoo-io-board](https://github.com/makerspet/oomwoo-io-board) · [SPEC.md](https://github.com/makerspet/oomwoo-io-board/blob/main/docs/SPEC.md)
- CPU↔MCU contract: [io-board-interface RFC](https://github.com/makerspet/oomwoo/tree/main/contributions/io-board-interface)
- System architecture: [ARCHITECTURE.md §5.4](https://github.com/makerspet/oomwoo/blob/main/docs/ARCHITECTURE.md)
- [STM32duino](https://github.com/stm32duino/Arduino_Core_STM32) · [STM32FreeRTOS](https://github.com/stm32duino/STM32FreeRTOS)
- [Project Discussions](https://github.com/makerspet/oomwoo/discussions) · [Discord](https://discord.gg/3y2JKz5T25)

## License

[Apache License 2.0](LICENSE). Contributions are made on that basis; safety-critical
firmware additionally passes maintainer safety review before merge.