https://github.com/edycutjong/scarecrow
π Off-grid AI sentry: vision + rules + spoken alerts on a Raspberry Pi β€4GB
https://github.com/edycutjong/scarecrow
computer-vision dorahacks edge-ai offline-ai qvac raspberry-pi sentry tts
Last synced: 10 days ago
JSON representation
π Off-grid AI sentry: vision + rules + spoken alerts on a Raspberry Pi β€4GB
- Host: GitHub
- URL: https://github.com/edycutjong/scarecrow
- Owner: edycutjong
- License: mit
- Created: 2026-06-09T01:48:08.000Z (23 days ago)
- Default Branch: main
- Last Pushed: 2026-06-12T08:32:58.000Z (19 days ago)
- Last Synced: 2026-06-12T09:24:05.937Z (19 days ago)
- Topics: computer-vision, dorahacks, edge-ai, offline-ai, qvac, raspberry-pi, sentry, tts
- Language: TypeScript
- Size: 5.23 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
## π§ββοΈ For Judges
**The pitch (30s):** A $50 off-grid Raspberry Pi that *understands* a scene and *reasons* about a plain-English rule β "alert if a person is near the shed, ignore the dog" β then speaks the alert and pushes it to a phone. Fully offline, β€4GB RAM, solar-powered. Not motion detection β multimodal reasoning on the edge.
**Run it in 30 seconds (works on any laptop, no Pi needed):**
```bash
npm install && npm start # β open http://localhost:8080
```
The dashboard shows the live pipeline, camera HUD, rule editor, and event log. Add a rule or "teach" a known entity right from the UI.
**Where to look:**
| Judging signal | Where | Status |
|---|---|---|
| **Deep QVAC integration** (5 distinct APIs) | `completion` + multimodal, `ragSearch`, `textToSpeech`, `startQVACProvider` β see [Why ONLY QVAC](#-why-only-qvac) | β
real `@qvac/sdk` v0.10.2 |
| **Capability unlock** (RAG known-entity recall) | [`src/core/memory.ts`](src/core/memory.ts) β your car/pet won't trip it, a stranger will | β
|
| **β€4GB on retail hardware** | `/api/status` reports **real measured RSS**; single-model loadβinferβunload | β
measured, not faked |
| **True offline** | `python3 scripts/verify_offline.py` β static cloud-import/URL scan + live network probe | β
|
| **Production quality** | `npm test` β **128 unit tests**; `npm run ci` (lint + types + coverage) | β
|
| **Reproducible classification** | `python3 scripts/bench.py` β real precision/recall of the offline baseline over 233 labeled scenes | β
|
**Mandatory constraints β all met:** 100% on-device inference through `@qvac/sdk` (**zero cloud APIs** β the hard disqualifier; every interface declared in [`docs/REMOTE_APIS.md`](docs/REMOTE_APIS.md)), [MIT](LICENSE) licensed and fully public, BYOH consumer hardware (Pi 4/5), reproducible via [`scripts/setup-pi.sh`](scripts/setup-pi.sh).
**Evidence bundle:** remote-API declaration ([`docs/REMOTE_APIS.md`](docs/REMOTE_APIS.md) β zero cloud APIs) Β· structured inference audit log (`GET /api/audit` β model loads/unloads, TTFT, tokens/sec) Β· offline scan + live network probe (`verify_offline.py`) Β· real classification baseline over 233 labeled scenes (`bench.py`) Β· append-only event log (`data/events.jsonl`) Β· readiness gate (`check_submission_readiness.py`) Β· physical demo runbook ([`DEMO.md`](DEMO.md)). *Model latency, peak RAM, and mWh/event are captured **on-device** (see [Benchmarks](#-benchmarks)) β not simulated.*
**Radical honesty:** nothing fake is shown as real. The RAM gauge is measured process RSS (tagged `RSS`); battery/solar are *modelled* and clearly tagged **`SIM`** in the UI until an INA219 sensor is wired (`SCARECROW_BATTERY_PCT`/`SCARECROW_SOLAR_W`). Camera capture uses real `libcamera-still` on the Pi. See [Honest Limitations](#-honest-limitations).
---
Scarecrow π
$50 off-grid AI sentry on a Raspberry Pi β€4GB. Camera β multimodal scene understanding β natural-language rule matching β spoken TTS alerts. Solar/battery powered, fully offline.
[](https://dorahacks.io/hackathon/qvac-unleach-edge-ai-i/detail)
[-f59e0b?style=for-the-badge)](https://dorahacks.io/hackathon/qvac-unleach-edge-ai-i/tracks#tinkerer)




[](https://github.com/edycutjong/scarecrow/actions/workflows/ci.yml)
---
## π See It Work
πΊ **[Watch the full demo on YouTube](https://youtu.be/fB8f_LXUss4)**
**What the camera sees** *(test clip)*

A person in dark clothing approaching the shed at dusk.
**What Scarecrow does** *(real dashboard)*

π΄ Reasons about the scene β **THREAT DETECTED** β speaks the alert β logs it.

A dog trotting across the same yard.

β
Sees the dog (green box, `0.88`) β matches the *ignore* rule β **PERIMETER CLEAR**, no alert.
**Known-entity recall** *(on-device RAG)*

An **unfamiliar** dark vanβ¦

β¦vs. **your** silver truck (taught once).

The van trips a π΄ **ALERT**; the silver truck is **recognised** and the alert is **suppressed** β *"π§© Recognised known entity: My silver truck."* Teach it once; a stranger's vehicle still alerts.
> **This is reasoning, not motion detection.** Same yard, same camera β Scarecrow
> alerts on the person and stays silent for the dog because it *understands* the
> scene against your plain-English rules. The scene clips are stimulus (yours /
> licensed stock / AI-generated); the dashboard, spoken alert, and RAM gauge are
> the **real** system on-device. Open it yourself with `npm start`.
---
## π‘ The Problem & Solution
Traditional security cameras need WiFi, cloud subscriptions, and constant power. In farms, construction sites, and remote properties, none of these are available.
**Scarecrow** turns a $50 Raspberry Pi into an intelligent sentry that understands what it sees:
**Key Features:**
- πΈ **Vision AI** β QVAC-Vision-1B analyzes camera frames locally
- π **NL Rules** β Plain English rules: "alert if person near shed, ignore dogs"
- π§© **Known-Entity Recall** β on-device RAG remembers your car/pet/mail carrier and suppresses false alarms
- π **Spoken Alerts** β Piper TTS announces threats through a speaker
- π² **P2P Phone Push** β alerts a paired phone over the LAN, no cloud
- βοΈ **Solar Powered** β PIR-triggered wake with configurable sleep intervals
- π₯οΈ **Pi Dashboard** β live web UI + rule editor served from Pi's hotspot
## ποΈ Architecture & Tech Stack
```mermaid
flowchart TD
A["β‘ PIR Wake"] --> B["πΈ Camera"]
B --> C["π§ QVAC-Vision-1B"]
C --> D["π Scene Description"]
D --> E["π Llama 3.2 1B Rules"]
E --> F{"Alert?"}
F -- Yes --> K{"π§© Known entity? (RAG)"}
F -- No --> H["π€ Sleep"]
K -- Known --> H
K -- Unknown --> G["π Piper TTS + π² P2P Push"]
G --> I["π Event Log + Dashboard"]
I --> H
H --> A
```
| Layer | Technology |
|---|---|
| **Runtime** | Node.js on Raspberry Pi 4/5 |
| **AI Engine** | @qvac/sdk (completion, multimodal, RAG, TTS, P2P) |
| **Vision** | QVAC-Vision-1B (multimodal) |
| **Rules** | Llama 3.2 1B (NL evaluation) |
| **Known entities** | GTE-Large embeddings (on-device RAG) |
| **TTS** | Piper via @qvac/sdk |
| **Alerts** | Holepunch-backed P2P phone push |
| **Dashboard** | Vanilla HTML/JS (Pi hotspot) |
## π° Hardware BOM (~$50)
| Part | Cost |
|---|---|
| Raspberry Pi 4 (2GB/4GB) | ~$35 |
| Pi Camera Module v2 | ~$10 |
| USB Speaker | ~$5 |
| PIR Motion Sensor (HC-SR501) | ~$2 |
| Solar panel + battery (optional) | ~$15 |
### π Wiring
```
β Solar βββΆ [ Solar + UPS HAT ] ββββΆ π 18650 Li-ion
β 5V / 3A (USB-C)
βΌ
π· Camera ββCSI ribbonβββΆ ββββββββββββββββββ βββ USB ββ π Speaker
β Raspberry Pi β (spoken alerts)
π HC-SR501 PIR ββGPIO17βββΆβ 4 (β€4GB) β
ββββββββββββββββββ
(VCCβ5V Β· OUTβGPIO17 Β· GNDβGND β see table)
```
| Wire | Pi connection | Phys. pin | BCM |
|---|---|---|---|
| PIR `VCC` | 5V | 2 | β |
| PIR `OUT` | GPIO17 | 11 | **17** *(matches `gpio.ts`)* |
| PIR `GND` | GND | 6 | β |
| Camera | CSI camera port | ribbon | β |
| Speaker | USB-A | β | β |
| Power in | USB-C 5V/3A | β | β |
> The PIR is the **power-aware wake source** β the Pi sleeps until motion, then
> wakes β captures β infers β sleeps. Change the pin via
> `startPIRSentry({ pin })`. Solar/battery is optional; any 5V/3A USB-C supply
> works for a wall-powered build.
## π Why ONLY QVAC?
| QVAC SDK Method | Scarecrow Usage | Cloud Alternative You'd Need |
|---|---|---|
| `loadModel(QVAC_VISION_1B)` | Multimodal scene analysis from camera frames | Google Cloud Vision API |
| `completion()` + `images` | Describe what the camera sees in natural language | GPT-4 Vision |
| `completion()` (Llama 1B) | NL rule evaluation: "alert if person near shed" | OpenAI API |
| `ragIngest()` / `ragSearch()` | Recognize known-friendly entities, suppress false alarms | Pinecone + embeddings API |
| `textToSpeech()` (Piper) | Spoken alerts when rules match | Amazon Polly |
| `startQVACProvider()` (P2P) | Push alerts to a paired phone over the LAN | Firebase Cloud Messaging |
| `unloadModel()` | Critical β free RAM between model swaps on 4GB Pi | N/A |
**Take QVAC out and you'd need 5+ cloud services** (Google Vision + OpenAI + Pinecone + Amazon Polly + Firebase), a WiFi router, and a power outlet β destroying the entire $50 off-grid premise.
## π Getting Started
```bash
git clone https://github.com/edycutjong/scarecrow.git
cd scarecrow
npm install
python3 scripts/seed.py
# Start the sentry loop AND serve the live dashboard over the Pi hotspot
npm start # β http://192.168.4.1:8080 (PORT env to override)
# Sentry loop only, no dashboard (headless)
npm run sentry
```
The dashboard polls a tiny zero-dependency HTTP API (`node:http`) for live data:
| Endpoint | Returns |
|---|---|
| `GET /` | Live dashboard (camera HUD, pipeline, rules, events) |
| `GET /api/status` | Stage, RAM, battery, solar, frames, armed state |
| `GET /api/events` | Event log from the sentry loop |
| `GET /api/rules` Β· `POST` Β· `DELETE /api/rules/:id` | List / add / remove natural-language rules |
| `GET /api/entities` Β· `POST` Β· `DELETE /api/entities/:id` | List / teach / forget known-friendly entities |
| `GET /api/audit` | Structured inference audit log + summary (model loads/unloads, TTFT, tokens/sec) |
| `GET /api/health` | Liveness probe |
> Opened directly as a file (`open src/web/index.html`) the dashboard runs in a
> self-contained **simulation**; pointed at the API it switches to **LIVE** mode
> automatically β and the rule/entity editors become live. On an alert, a P2P
> phone push is sent via `@qvac/sdk` (`src/core/p2p.ts`).
### π§© Known-entity allow-list (on-device RAG)
Teach Scarecrow what to ignore β *"my silver truck," "the mail carrier," "my dog
Max."* Before raising an alert, `src/core/memory.ts` runs an on-device semantic
search (`ragIngest`/`ragSearch`, GTE-Large embeddings) against the scene; a match
above threshold **suppresses the alert**. This is reasoning, not a blocklist: the
owner's car pulling in won't trip it, but a stranger's will.
### π Power modes
| Command | Behaviour |
|---|---|
| `npm start` | Dashboard + fixed-interval sentry loop |
| `npm run sentry` | Headless fixed-interval loop |
| PIR mode (`startPIRSentry`, `src/core/gpio.ts`) | Sleep β **motion-triggered wake** β check β sleep (solar-optimal) |
Events are persisted append-only to `data/events.jsonl` (`src/core/storage.ts`) and
restored on boot β a zero-dependency, inspectable offline evidence trail.
**Honest telemetry.** `/api/status` reports **real measured process RSS** for the
RAM gauge (`process.memoryUsage().rss`, tagged `RSS` in the UI). Battery/solar are
**modelled** and tagged **`SIM`** on the dashboard until a real sensor is wired β
export `SCARECROW_BATTERY_PCT` / `SCARECROW_SOLAR_W` (e.g. from an INA219 daemon)
and the values go live with `powerSimulated: false`.
### π Provisioning a Pi
```bash
bash scripts/setup-pi.sh # camera, audio, Node, deps
bash scripts/setup-pi.sh --ap # also configure the Scarecrow-AP hotspot
```
## π Benchmarks
**Classification quality (real, reproducible anywhere β `python3 scripts/bench.py`).**
Precision/recall of the documented *offline keyword-fallback baseline* over the 233
labeled fixture scenes β the floor the Llama 3.2 1B engine improves on:
| Baseline metric | Value |
|---|---|
| Precision | 0.67 |
| Recall | 0.43 |
| F1 | 0.52 |
| Accuracy | 0.73 (169/233) |
> The modest recall is the point: a keyword baseline misses non-"person" alerts
> (vehicles, etc.), which is exactly why on-device LLM reasoning is used. The
> Llama model's real precision/recall is measured **on-device** (needs the model).
**Latency Β· RAM Β· power β measured on the Pi, not simulated.** These depend on the
QVAC models and physical rig, so they are captured on-device (the dashboard
`/api/status` reports **live RSS**; `htop`/`vcgencmd` for memory; INA219 for
mWh/event). Targets for Raspberry Pi 4 (4GB):
| Stage | Target |
|---|---|
| Vision analysis (1 frame) | < 4,000 ms |
| Rule evaluation | < 2,000 ms |
| TTS alert | < 2,000 ms |
| Full pipeline | < 8,000 ms |
| Peak RAM | < 3,800 MB (β€4GB constraint) |
> These are budgets, not measurements β fill them from a real Pi run for the
> submission. The benchmark script never fabricates device numbers.
## π§ͺ Testing & CI
**128 unit tests** (vision, rules + live editing + real fallback classification over the 233-scene fixture set, power state machine, telemetry, inference audit log, RAG known-entity recognition, P2P phone alerts, PIR wake, JSONL persistence, dashboard API) + offline verification scan + readiness suite. Run `npm test`. *(Tests mock only the `@qvac/sdk`/hardware boundary; every assertion exercises real project logic β no tautological label-fed tests.)*
**4-stage pipeline:** Quality β Security β Offline Verify β Deploy
```bash
python3 scripts/verify_offline.py
python3 scripts/bench.py
python3 scripts/check_submission_readiness.py
```
| Layer | Tool | Status |
|---|---|---|
| Code Quality | ESLint + TypeScript | β
|
| Security (SAST) | CodeQL | β
|
| Dependency Audit (SCA) | npm audit + Dependabot | β
|
| Secret Scanning | TruffleHog | β
|
| Offline Verification | verify_offline.py (cloud-import/URL scan + net probe) | β
|
## π Project Structure
```
scarecrow/
βββ docs/ # README assets
βββ data/fixtures/ # test_rules.json, test_scenes.json
βββ scripts/ # setup-pi, seed, bench, verify, readiness
βββ src/
β βββ core/
β β βββ qvac.ts # @qvac/sdk wrapper
β β βββ vision.ts # Camera + multimodal analysis
β β βββ rules.ts # NL rule engine + add/remove
β β βββ memory.ts # RAG known-entity allow-list (ragSearch)
β β βββ power.ts # Sentry loop + live telemetry (getSystemStatus)
β β βββ gpio.ts # PIR motion-wake source
β β βββ storage.ts # Append-only JSONL event persistence
β β βββ p2p.ts # P2P phone push alerts on match
β β βββ audit.ts # Structured inference audit log (loads/unloads, TTFT, tok/s)
β βββ web/
β βββ server.ts # Dashboard HTTP server + JSON API (node:http)
β βββ index.html # Live dashboard + rule/entity editor (Pi hotspot)
βββ .github/ # CI/CD + CodeQL + Dependabot
βββ README.md
```
## β οΈ Honest Limitations
1. Camera capture simulated in dev (real Pi Camera required)
2. PIR wake uses the `onoff` GPIO binding on the Pi; dev/CI run a simulated motion source
3. Battery/solar are modelled (tagged `SIM` in the UI) until an INA219 sensor feeds `SCARECROW_BATTERY_PCT`/`SCARECROW_SOLAR_W`; the RAM gauge is real measured RSS
4. Event log persists to append-only JSONL; a queryable SQLite store is future work
5. Single-model-at-a-time due to 4GB constraint
6. English rules only
## π License
[MIT](LICENSE) Β© 2026 Edy Cu
## π Acknowledgments
Built for **QVAC Hackathon I β Unleash Edge AI** (DoraHacks). Proving that useful AI doesn't need the cloud β just a $50 Pi and QVAC.