An open API service indexing awesome lists of open source software.

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

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 Logo

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.


Scarecrow Hero


[![Built for QVAC Hackathon](https://img.shields.io/badge/DoraHacks-QVAC%20Edge%20AI-8b5cf6?style=for-the-badge)](https://dorahacks.io/hackathon/qvac-unleach-edge-ai-i/detail)
[![Track](https://img.shields.io/badge/Track-Tinkerer%20(Pi%20≀4GB)-f59e0b?style=for-the-badge)](https://dorahacks.io/hackathon/qvac-unleach-edge-ai-i/tracks#tinkerer)


![Node.js](https://img.shields.io/badge/Node.js-339933?style=flat&logo=node.js&logoColor=white)
![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat&logo=typescript&logoColor=white)
![Raspberry Pi](https://img.shields.io/badge/Raspberry_Pi-A22846?style=flat&logo=raspberrypi&logoColor=white)
![QVAC](https://img.shields.io/badge/@qvac/sdk-06b6d4?style=flat)
[![CI](https://github.com/edycutjong/scarecrow/actions/workflows/ci.yml/badge.svg)](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)*

Person approaching the shed at dusk

A person in dark clothing approaching the shed at dusk.

**What Scarecrow does** *(real dashboard)*

Scarecrow dashboard raising a real alert

πŸ”΄ Reasons about the scene β†’ **THREAT DETECTED** β†’ speaks the alert β†’ logs it.

A dog crossing the yard past the shed

A dog trotting across the same yard.

Scarecrow seeing the dog and staying silent

βœ… 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 in the driveway

An **unfamiliar** dark van…

The owner's silver truck

…vs. **your** silver truck (taught once).

Scarecrow alerting on the van but recognizing the known truck

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.