docs(readme): add quick-start, hardware sources, power draw + latency notes
Adds a sourced parts table (M5 TimerCamera-F, USB cable, 5V adapter), the ~750 mW measured power draw, the 3-5s detection latency caveat, and a six-step Quick Start aimed at semi-technical operators deploying their own device. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
123
README.md
123
README.md
@@ -2,13 +2,95 @@
|
||||
|
||||
Retail door traffic counter using M5Stack TimerCamera-F (ESP32 + OV3660). Counts walker traversals via overhead camera CV, passively scans BLE foot traffic, and reports hourly to `logs.research.bike`.
|
||||
|
||||
> **Known limitation — directional accuracy.** This firmware reports counts as `{entries, exits}` for API compatibility, but **per-walk direction labelling is not reliable at the current mount (7' overhead, straight down).** In bench testing, event detection was 100% (8/8 walks detected) while per-walk direction matched the physical walk only ~50% of the time — the centroid trajectories produced by entries and exits were nearly indistinguishable. **The number to trust is gross traffic: `entries + exits` ≈ total walkers through the doorway.** The directional split is an unreliable best-effort heuristic. See [Directional counting](#directional-counting) for why.
|
||||
> **Known limitations.**
|
||||
> - **Directional accuracy.** Counts are reported as `{entries, exits}` for API compatibility, but **per-walk direction labelling is not reliable at the current mount (7' overhead, straight down).** Bench testing: event detection 100% (8/8), per-walk direction ~50% (coin flip). **Trust gross traffic: `entries + exits` ≈ total walkers.** See [Directional counting](#directional-counting).
|
||||
> - **Detection latency.** A walker takes **3–5 seconds** from entering the FOV to being registered as a count — the state machine waits for the walker to clear the frame (or a 5s timeout) before finalizing. Counts are not instantaneous; hourly aggregation is the intended consumption mode.
|
||||
|
||||
## Hardware
|
||||
|
||||
- **Device**: M5Stack TimerCamera-F (ESP32-S, OV3660, PSRAM, WiFi/BLE)
|
||||
- **Mount**: Overhead, camera pointing straight down, centered above doorway
|
||||
- **Power**: USB (any phone charger)
|
||||
| Component | Source | Notes |
|
||||
|-----------|--------|-------|
|
||||
| **Camera** | [M5Stack TimerCamera-F (OV3660 fisheye, PSRAM)](https://shop.m5stack.com/products/esp32-psram-timer-camera-fisheye-ov3660) | ESP32 + WiFi/BLE on board |
|
||||
| **USB cable** | [USB-A → USB-C, right-angle](https://www.amazon.com/dp/B0DWMPVP4F) | Right-angle plug helps with overhead mounts |
|
||||
| **Power supply** | [5V USB wall adapter](https://www.amazon.com/dp/B0B2WLSY9D) | Any 5V/1A+ USB charger works |
|
||||
|
||||
- **Mount**: Overhead, camera pointing straight down, centered above doorway (~7' / 2.1m height)
|
||||
- **Power draw**: **~750 mW measured at the wall** (camera + WiFi + BLE all active). Runs cool — fanless, can be sealed in a small enclosure. Annual energy cost at US residential rates is well under $1.
|
||||
|
||||
## Quick Start (semi-technical)
|
||||
|
||||
The fastest path from "box arrived" to "counts in the dashboard." Comfortable with a terminal but not necessarily an embedded developer? Start here.
|
||||
|
||||
**You will need**: the camera + cable + power supply listed above, a Linux/macOS computer with USB, and ~20 minutes.
|
||||
|
||||
### 1. Install the toolchain (one-time)
|
||||
|
||||
```bash
|
||||
# Python 3.10+ and pip
|
||||
pip install --user platformio esptool esp-idf-nvs-partition-gen
|
||||
```
|
||||
|
||||
PlatformIO installs the ESP32 compiler on first build — expect a few minutes the first time.
|
||||
|
||||
### 2. Clone this repo
|
||||
|
||||
```bash
|
||||
git clone https://github.com/<your-org>/DoorCounter.git
|
||||
cd DoorCounter
|
||||
```
|
||||
|
||||
### 3. Plug the camera in
|
||||
|
||||
Connect the USB-C cable to the TimerCamera and the other end to your computer. On Linux it appears as `/dev/ttyUSB0`; on macOS as `/dev/tty.usbserial-*`. If you don't see it, install [CP210x USB drivers](https://www.silabs.com/developer-tools/usb-to-uart-bridge-vcp-drivers).
|
||||
|
||||
### 4. Flash the firmware
|
||||
|
||||
```bash
|
||||
cd firmware
|
||||
pio run -t upload --upload-port /dev/ttyUSB0
|
||||
```
|
||||
|
||||
### 5. Provision the device with its credentials
|
||||
|
||||
Pick a unique device ID (e.g. `dc-0001`), a location ID, and generate a 32-byte HMAC secret. The server admin must record this same secret — counts won't be accepted without it.
|
||||
|
||||
```bash
|
||||
# Generate a fresh secret
|
||||
openssl rand -hex 32 > my-device-secret.txt
|
||||
|
||||
# Provision
|
||||
python tools/flash_device.py \
|
||||
--port /dev/ttyUSB0 \
|
||||
--device-id dc-0001 \
|
||||
--location-id my-store \
|
||||
--hmac-secret "$(cat my-device-secret.txt)" \
|
||||
--wifi-ssid "MyStoreWiFi" \
|
||||
--wifi-password "wifi-password-here"
|
||||
```
|
||||
|
||||
> If you skip `--wifi-ssid`/`--wifi-password`, the device opens a `DoorCounter-Setup` WiFi access point on boot. Connect a phone to it and enter the credentials in the captive portal.
|
||||
|
||||
### 6. Mount the device
|
||||
|
||||
1. Position above the doorway, camera lens pointing straight down (~7' / 2.1m up).
|
||||
2. Plug into the wall adapter — that's it. The LED turns red while joining WiFi, then off once it's counting.
|
||||
3. First heartbeat lands at the server within ~60 seconds; first hourly count batch arrives at the top of the next hour.
|
||||
|
||||
### What "working" looks like
|
||||
|
||||
- LED behavior: **off** = counting normally · **red** = no WiFi · **yellow** = uploading · **brief flash** when a walker is registered (1 flash = entry, 2 flashes = exit).
|
||||
- A walker takes 3–5 seconds from entering the FOV to triggering the LED flash — this is normal.
|
||||
- Hourly uploads to `logs.research.bike` (or your configured server) include the entry/exit counts since the last report.
|
||||
|
||||
### If something is off
|
||||
|
||||
| Symptom | Try |
|
||||
|---------|-----|
|
||||
| Red LED stays on | Wrong WiFi password — re-run step 5, or use the `DoorCounter-Setup` captive portal. |
|
||||
| LED blinks ~1 Hz forever (or device reboots in a loop) | NVS got wiped — re-run step 5 with the same credentials. |
|
||||
| No counts appearing on server | Run `python tools/serial_monitor.py --port /dev/ttyUSB0 --reset --timestamp --seconds 30` and watch for `[CV] entry/exit` lines as you walk under it. |
|
||||
|
||||
For deeper troubleshooting see [Troubleshooting](#troubleshooting) and [Operator Setup](#operator-setup).
|
||||
|
||||
## Firmware
|
||||
|
||||
@@ -123,10 +205,29 @@ python tools/flash_device.py \
|
||||
|
||||
WiFi credentials are optional — if omitted, device starts captive portal on boot.
|
||||
|
||||
**Known-good command for dc-0002** (dev device at research.bike):
|
||||
|
||||
```bash
|
||||
python tools/flash_device.py \
|
||||
--port /dev/ttyUSB0 \
|
||||
--device-id dc-0002 \
|
||||
--location-id retailer-123 \
|
||||
--hmac-secret "$(cat .agent/dc-0002-secret)" \
|
||||
--wifi-ssid Elly-Fi \
|
||||
--wifi-password <ask> \
|
||||
--line-offset 50
|
||||
```
|
||||
|
||||
Secret is stored in `.agent/dc-0002-secret` (gitignored). Server must already
|
||||
know this secret — do not rotate without updating the server side.
|
||||
|
||||
> **Re-provision after firmware uploads.** Flashing firmware via
|
||||
> `pio run -t upload` may clear the NVS partition on this board. If the device
|
||||
> boots into a ~1 Hz LED blink (the "not provisioned" fatal state) after a
|
||||
> firmware update, re-run `flash_device.py` with the same credentials. See
|
||||
> `pio run -t upload` may clear the NVS partition on this board.
|
||||
> - **FW 1.0**: device boots into a ~1 Hz LED blink (hang in "not provisioned" fatal).
|
||||
> - **FW 1.1+**: device reboot-loops with `FATAL: device_id/location_id/hmac_secret not provisioned`
|
||||
> followed by `rst:0xc (SW_CPU_RESET)` (FATAL paths now reboot instead of hang).
|
||||
>
|
||||
> Either way, re-run `flash_device.py` with the same credentials. See
|
||||
> [Troubleshooting](#troubleshooting).
|
||||
|
||||
### 3. OTA updates
|
||||
@@ -188,7 +289,7 @@ DoorCounter/
|
||||
|
||||
| Symptom | Likely cause | Remedy |
|
||||
|---------|--------------|--------|
|
||||
| ~1 Hz LED blink after boot, no serial beyond `esp_core_dump_flash: No core dump partition found!` | NVS missing `device_id` / `location_id` / `hmac_secret`. Commonly triggered by a firmware upload wiping NVS. | Re-run `flash_device.py` with the device's known credentials. |
|
||||
| ~1 Hz LED blink after boot (FW 1.0), OR reboot loop with `FATAL: device_id/location_id/hmac_secret not provisioned` → `rst:0xc (SW_CPU_RESET)` (FW 1.1+) | NVS missing `device_id` / `location_id` / `hmac_secret`. Commonly triggered by a firmware upload wiping NVS. FW 1.1+ reboots on FATAL instead of hanging. | Re-run `flash_device.py` with the device's known credentials (see section 2 for dc-0002). |
|
||||
| Device stays on `DoorCounter-Setup` AP instead of joining customer WiFi | SSID/password in NVS wrong, or network out of range. | Connect phone to `DoorCounter-Setup` → captive portal → re-enter WiFi. Or reflash NVS with correct `--wifi-ssid` / `--wifi-password`. |
|
||||
| No entries/exits counted for a known-walking doorway | WiFi captive portal still up (camera task starts only after connect); or camera blocked/unfocused. | Check LED: solid on = booting/uploading, off = counting. Run `serial_monitor.py` to see `[CV] entry/exit` log lines. |
|
||||
|
||||
@@ -228,6 +329,12 @@ flash any device.
|
||||
cd firmware && pio run -e timercam -t upload
|
||||
```
|
||||
|
||||
> **If the device reboot-loops after flashing** with `FATAL:
|
||||
> device_id/location_id/hmac_secret not provisioned`, NVS was wiped. Re-run
|
||||
> `flash_device.py` (see [section 2](#2-provision-device-identity)). FW 1.1
|
||||
> turned the old FW 1.0 LED-blink hang into an explicit reboot loop; same
|
||||
> root cause, same fix.
|
||||
|
||||
### Expected first boot
|
||||
|
||||
On the serial log (115200 baud), the device prints the boot banner, then
|
||||
|
||||
Reference in New Issue
Block a user