diff --git a/docs/BEE-CAMERA.md b/docs/BEE-CAMERA.md new file mode 100644 index 0000000..da5318c --- /dev/null +++ b/docs/BEE-CAMERA.md @@ -0,0 +1,810 @@ +# Bee Camera System — Full Technical Report + +*Generated: 2026-03-13* + +--- + +## Executive Summary + +The Hivemapper Bee dashcam uses an Intel Keem Bay SoC with an integrated Myriad X VPU for camera capture and ML inference. The camera pipeline flows from a Sony IMX378-equivalent sensor through MIPI CSI-2 to the VPU, where DepthAI firmware handles image processing and neural network inference. Frames are written to disk and exposed through multiple odc-api REST endpoints. + +**Key Findings:** +- Camera controlled via `depthai_gate.service` (Python/Flask on port 11492) +- ML inference handled by `map-ai.service` using the VPU's Neural Compute Engine +- Live frames stored in `/tmp/recording/pics/` +- Landmark observation images stored in `/data/recording/cached_observations/` +- Preview mode restarts the camera-bridge service with different configuration +- No direct V4L2 access — all camera access goes through DepthAI pipeline + +--- + +## 1. Hardware + +### 1.1 System-on-Chip: Intel Keem Bay + +| Component | Specification | +|-----------|---------------| +| **SoC** | Intel Keem Bay (RVC2 / Robotics Vision Core 2) | +| **CPU** | 4× ARM Cortex-A53 @ 1.5GHz | +| **VPU** | Intel Movidius Myriad X (16 SHAVE cores) | +| **NPU** | Integrated Neural Compute Engine (hardware inference) | +| **RAM** | 4GB LPDDR4 (~3.5GB usable) | +| **ISP** | Integrated Image Signal Processor on VPU | +| **Process** | 10nm (Intel) | + +**Memory Configuration:** +``` +MemTotal: 3,584,000 kB (~3.5GB) +SwapTotal: 2,097,148 kB (~2GB) +CmaTotal: 1,408,000 kB (~1.34GB reserved for VPU/camera DMA) +``` + +### 1.2 Camera Sensor + +| Specification | Value | +|---------------|-------| +| **Sensor** | Sony IMX378 (or equivalent 12MP) | +| **Resolution** | 4056 × 3040 native, downscaled to 2028 × 1024 | +| **Interface** | MIPI CSI-2 | +| **Frame Rate** | Variable, typically 30 FPS | +| **ISP** | On-VPU processing via DepthAI | + +The Bee uses a Luxonis OAK-1 compatible camera module integrated with the Keem Bay SoC. The camera sensor connects directly to the SoC's MIPI CSI-2 interface, which is managed entirely by the DepthAI/Luxonis firmware running on the Myriad X VPU. + +### 1.3 Bus Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Intel Keem Bay SoC │ +├─────────────────────────────────────────────────────────────────┤ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ +│ │ ARM Cortex │ │ Myriad X VPU │ │ Neural Compute │ │ +│ │ A53 (4-core) │ │ (16 SHAVE) │ │ Engine (NCE) │ │ +│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │ +│ │ │ │ │ +│ └────────┬────────┴──────────────────────┘ │ +│ │ │ +│ ┌────────┴────────┐ │ +│ │ Internal Bus │ │ +│ │ (AXI/NoC) │ │ +│ └────────┬────────┘ │ +│ │ │ +│ ┌─────────────┼─────────────────────────────────────┐ │ +│ ┌─┴──┐ ┌────┴────┐ ┌────────┐ ┌───────────┐ │ │ +│ │PCIe│ │ USB │ │ SDIO │ │ MIPI CSI │ │ │ +│ └──┬─┘ └────┬────┘ └────┬───┘ └─────┬─────┘ │ │ +└─────┼───────────┼──────────────┼──────────────┼────────┘ │ + │ │ │ │ │ +┌─────┴─────┐ ┌───┴───────┐ ┌──┴──┐ ┌────┴─────┐ │ +│ Marvell │ │ Telit │ │eMMC │ │ Camera │ │ +│ 88W8997 │ │ LE910C4 │ │Flash│ │ Module │ │ +│ WiFi/BT │ │ LTE Modem │ │ │ │ (IMX378) │ │ +└───────────┘ └───────────┘ └─────┘ └──────────┘ │ +``` + +--- + +## 2. Kernel / V4L2 + +### 2.1 Kernel Modules + +The Bee runs a custom Yocto-based Linux with Intel-specific VPU drivers: + +| Module | Purpose | Status | +|--------|---------|--------| +| **kmb_cam** (if present) | Keem Bay camera driver | Likely used internally | +| **kmb_imx412** (if present) | Sony IMX412 sensor driver | May be loaded for sensor | +| **videodev** | V4L2 subsystem | Core video framework | +| **v4l2_fwnode** | V4L2 firmware node parsing | Device tree integration | + +**Note:** Standard V4L2 device access (`/dev/video*`) is **not used** for normal operation. The camera is accessed exclusively through the DepthAI XLink protocol running on the VPU. The VPU owns the camera hardware completely. + +### 2.2 VPU Sysfs Interface + +The VPU is controlled via sysfs: +``` +/sys/class/vpu/ +``` + +**Firmware Loading:** +- `luxonis_vpu.bin` — DepthAI firmware (Luxonis/OAK) +- `vpu_nvr_b0.bin` — Intel HDDL firmware (NOT used, conflicts) + +The VPU firmware is written to a sysfs attribute (`fwname`) to trigger loading. The DepthAI firmware must load first, otherwise the Intel HDDL service (`deviceservice`) grabs the VPU and causes conflicts. + +### 2.3 No Direct V4L2 Access + +**Important:** You cannot access the camera via `/dev/video*` while `depthai_gate` is running. The DepthAI pipeline has exclusive ownership of the camera hardware. To get frames, you must: +1. Use the existing depthai_gate/odc-api stack, OR +2. Stop depthai_gate and implement your own DepthAI pipeline, OR +3. Reverse-engineer XLink and write custom firmware + +--- + +## 3. DepthAI Gate + +### 3.1 Service Configuration + +```ini +# depthai_gate.service (inferred from analysis) +[Unit] +Description=DepthAI Camera Gate +After=network.target + +[Service] +Type=simple +User=root +ExecStart=/opt/depthai_gate/run.py +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +### 3.2 Technical Details + +| Property | Value | +|----------|-------| +| **Language** | Python 3 + Flask | +| **Port** | 11492 (localhost) | +| **Tasks** | ~158 threads observed | +| **Memory** | ~200MB RSS | +| **Location** | `/opt/depthai_gate/` (estimated) | + +### 3.3 Responsibilities + +1. **VPU Firmware Loading** — Writes `luxonis_vpu.bin` to VPU sysfs +2. **XLink Connection** — Establishes PCIe XLink to Myriad X VPU +3. **DepthAI Pipeline** — Configures camera capture and ISP settings +4. **Frame Capture** — Captures frames at configured resolution/framerate +5. **Frame Output** — Writes frames to `/tmp/recording/pics/` + +### 3.4 XLink Protocol + +XLink is Luxonis's proprietary protocol for host-to-VPU communication: +- **Transport:** PCIe (on Keem Bay) or USB (on desktop OAK devices) +- **Channels:** Bidirectional data streams for frames, tensors, and control +- **Status Values:** + - `0` = Disconnected + - `1` = Connecting / Error + - `2` = Connected (good) + +**Status Check (from logs):** +``` +xlink_device_status=2 # Healthy +vpu_firmware=luxonis_vpu.bin +``` + +### 3.5 Pipeline Configuration + +The DepthAI pipeline likely includes: +- **ColorCamera node** — IMX378 capture at 4K, downscaled to 2028×1024 +- **ImageManipNode** — Resize, crop, color conversion +- **XLinkOut node** — Send frames to host for storage +- **NeuralNetwork node** (optional) — On-VPU inference + +Pipeline configs may exist at: +- `/opt/depthai_gate/pipeline.json` +- `/data/camera_config.json` +- Hardcoded in Python + +### 3.6 VPU Conflict Bug + +**Root Cause (identified and fixed):** + +`deviceservice.service` (Intel HDDL / OpenVINO) was racing with `depthai_gate.service`: + +1. HDDL starts at boot, loads `vpu_nvr_b0.bin` +2. depthai_gate starts, overwrites with `luxonis_vpu.bin` +3. HDDL locked out, retries XLink every 2 seconds forever +4. On depthai_gate restart, HDDL grabs VPU first → camera dead +5. Watchdog (`secure-wdtclient`) crash loops → memory pressure → OOM + +**Fix:** +```bash +systemctl disable --now deviceservice +systemctl mask deviceservice # Survives OTA better +``` + +--- + +## 4. map-ai Pipeline + +### 4.1 Service Configuration + +```ini +# map-ai.service (inferred) +[Unit] +Description=Map AI Processing +After=depthai_gate.service + +[Service] +Type=simple +User=root +ExecStart=/opt/map-ai/run.py +Restart=always + +[Install] +WantedBy=multi-user.target +``` + +### 4.2 Technical Details + +| Property | Value | +|----------|-------| +| **Language** | Python 3 | +| **Model Format** | ONNX (via OpenVINO or DepthAI NCE) | +| **Input** | Frames from depthai_gate | +| **Output** | Detections to Redis, blurred frames to disk | + +### 4.3 Processing Pipeline + +``` +Frame from depthai → map-ai.py + │ + ▼ +┌───────────────────────────────────────┐ +│ ML INFERENCE (on VPU) │ +│ - Road sign classifier │ +│ - Face detector (privacy) │ +│ - License plate detector (privacy) │ +└───────────────────────┬───────────────┘ + │ + ▼ +┌───────────────────────────────────────┐ +│ PRIVACY PROCESSING │ +│ - PrivacyBlurNode │ +│ - Gaussian blur on faces/plates │ +│ - cv2.imwrite blurred frames │ +└───────────────────────┬───────────────┘ + │ + ┌───────────────┴───────────────┐ + ▼ ▼ +Redis ZSET (detections) Disk (blurred frames) +``` + +### 4.4 AI Models + +| Model | Location | Purpose | +|-------|----------|---------| +| Road Signs | `/opt/object-detection/model.blob` or `/data/models/` | Sign classification | +| Privacy | `/opt/odc-api/python/` or `/data/models/` | Face/plate detection | +| PVC | `/data/recording/models/pvc.onnx` | Unknown (227 bytes — likely index) | + +**Privacy Model Hash:** Stored in FrameKm metadata for verification. + +### 4.5 Redis Integration + +map-ai writes to Redis status keys: +``` +GET MAP_AI_READY → "True" +GET EXTERNAL_MODEL_CLASSIFIER_READY → "True" +``` + +Detection results stored in SQLite, not Redis ZSETs. + +--- + +## 5. Frame Storage + +### 5.1 Storage Locations + +| Path | Type | Purpose | Persistence | +|------|------|---------|-------------| +| `/tmp/recording/pics/` | tmpfs | Live camera frames | Ephemeral | +| `/tmp/recording/preview/` | tmpfs | Preview mode frames | Ephemeral | +| `/data/recording/cached_observations/` | ext4 | Landmark observation images | Persistent | +| `/data/recording/framekm/` | ext4 | FrameKm upload bundles | Persistent | +| `/tmp/rgb/` | tmpfs | Frame list files | Ephemeral | + +### 5.2 Frame Format + +| Property | Value | +|----------|-------| +| **Format** | JPEG | +| **Resolution** | 2028 × 1024 | +| **Quality** | ~85% (estimated ~150-200KB/frame) | +| **Color** | RGB | + +### 5.3 Naming Convention + +**Live frames** (`/tmp/recording/pics/`): +``` +{system_time_ms}_{frame_id}_{sequence}.jpg +Example: 1709920000123_0001_0042.jpg +``` + +**Cached observations** (`/data/recording/cached_observations/`): +``` +{timestamp}_{subsecond}_{frame_number}.jpg +Example: 1746377552_043000_2945056.jpg +``` + +### 5.4 Frame Purger + +The `folder_purger` service manages disk space: +```bash +folder-purger /tmp/recording/pic 400000000 /mnt/data/gps 2000000000 ... +``` + +When `/tmp/recording/pics/` exceeds 400MB, older frames are deleted. + +### 5.5 Database Schema + +Frames are tracked in SQLite (`/data/recording/odc-api.db` or `data-logger.v2.0.0.db`): + +```sql +-- frames table +CREATE TABLE frames ( + system_time INTEGER PRIMARY KEY, + image_name TEXT +); +``` + +Landmark observations reference frames: +```sql +-- observations table (simplified) +CREATE TABLE observations ( + id INTEGER PRIMARY KEY, + landmark_id INTEGER, + image_name TEXT, + x1 REAL, y1 REAL, x2 REAL, y2 REAL, -- bounding box + ts INTEGER, + ... +); +``` + +--- + +## 6. video-processor + +### 6.1 Service Details + +The `video-processor` service is not explicitly documented in the analyzed firmware, but based on naming patterns, it likely handles: + +1. **FrameKm Bundling** — Package frames + metadata for upload +2. **Video Encoding** — H.264/H.265 encoding for preview/streaming +3. **Frame Sequencing** — Order frames for FrameKm creation + +### 6.2 FrameKm Format + +**Purpose:** Bundle ~1km of driving data for upload to Hivemapper/HERE. + +**Path:** `/data/recording/framekm/` + +**Contents:** +``` +framekm-2024-03-08-12-34-56-abc123.tar +├── manifest.json +├── frame_0001.jpg +├── frame_0002.jpg +├── ... +├── gnss_auth_buffer.bin +└── gnss_auth_signature.bin +``` + +**Manifest Fields:** +```json +{ + "name": "framekm-2024-03-08-12-34-56-abc123", + "numFrames": 150, + "deviceId": "fvhL2I-iCT", + "firmwareVersion": "0.0.1", + "privacyModelHash": "sha256:abc123...", + "gnssAuthBuffer": "base64...", + "gnssAuthSignature": "base64...", + "gnssAuthPublicKey": "base64...", + "createdAt": 1709920000000 +} +``` + +### 6.3 Relationship to Camera Frames + +The video-processor does NOT produce the frames we care about for camera access. It only packages existing blurred frames for upload. For raw frame access, focus on `depthai_gate` and the preview system. + +--- + +## 7. odc-api Camera Endpoints + +### 7.1 Base URL + +``` +http://192.168.0.10:5000/api/1/ +``` + +Binds to AP interface (`wlp1s0f0`) only — not accessible from home LAN directly. + +### 7.2 Preview Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/preview/start` | GET | Start preview mode (120s timeout) | +| `/preview/stop` | GET | Stop preview mode | +| `/preview/status` | GET | Check if preview is active | +| `/preview/metadata` | GET | Get latest frame metadata | + +**Preview Implementation (`util/preview.ts`):** + +```typescript +export const startPreview = async () => { + // Create preview directory + await execSync('mkdir /tmp/recording/preview'); + + // Write preview config + writeFileSync(IMAGER_CONFIG_PATH, JSON.stringify(getPreviewConfig())); + + // Restart camera-bridge with new config + await execSync(CMD.STOP_CAMERA); // systemctl stop camera-bridge + await sleep(1000); + await execSync(CMD.START_CAMERA); // systemctl start camera-bridge +}; +``` + +**Preview Timeout:** 120 seconds (auto-stops to preserve 4K quality recording) + +### 7.3 Landmark Image Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/landmarks/images/:id` | GET | Get image paths for landmark | +| `/landmarks/:id/chips` | GET | Get chip endpoints for landmark | +| `/landmarks/:id/chips/:chip_id` | GET | Get cropped observation image (JPEG) | +| `/landmarks/boundingBox/:id` | GET | Get bounding box coordinates | +| `/landmarks/upload` | PUT | Upload landmark image to external URL | + +**Image Retrieval Flow:** + +``` +GET /landmarks/images/123 + ↓ +Returns: ["/data/recording/cached_observations/1746377552_043000_2945056.jpg"] + ↓ +GET /landmarks/123/chips/456 + ↓ +Returns: Cropped JPEG (bounding box region) +``` + +### 7.4 Camera Configuration + +**Config Path:** `/opt/camera-bridge/config.json` + +**Commands (from `bee.ts`):** +```typescript +export const CMD = { + RESTART_CAMERA: 'systemctl restart camera-bridge', + START_CAMERA: 'systemctl start camera-bridge', + STOP_CAMERA: 'systemctl stop camera-bridge', + START_PREVIEW: 'systemctl start camera-preview', + STOP_PREVIEW: 'systemctl stop camera-preview', + // ... +}; +``` + +### 7.5 Frame Retrieval + +There is **no direct `/camera/frame` endpoint** in the current odc-api. To get a camera frame: + +1. **Via Preview Mode:** + - Call `/preview/start` + - Read frames from `/tmp/recording/preview/` + - Call `/preview/stop` when done + +2. **Via Landmark Images:** + - Call `/landmarks/last/N` to get recent detections + - Call `/landmarks/images/:id` to get observation image paths + - Call `/landmarks/:id/chips/:chip_id` to get cropped JPEG + +3. **Direct File Access (SSH):** + - Read from `/tmp/recording/pics/` for latest frames + - Read from `/data/recording/cached_observations/` for landmark images + +--- + +## 8. Full Data Flow + +### 8.1 Complete Pipeline + +``` +┌──────────────────────────────────────────────────────────────────────────────┐ +│ CAMERA CAPTURE │ +│ IMX378 Sensor → MIPI CSI-2 → VPU ISP → DepthAI Pipeline │ +└───────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ DEPTHAI_GATE (port 11492) │ +│ - XLink communication with Myriad X VPU │ +│ - Frame capture from DepthAI pipeline │ +│ - Writes frames to /tmp/recording/pics/ │ +└───────────────────────────────────┬──────────────────────────────────────────┘ + │ + ┌───────────────┴───────────────┐ + ▼ ▼ +┌───────────────────────────────┐ ┌──────────────────────────────────────────┐ +│ RAW FRAME STORAGE │ │ MAP-AI INFERENCE │ +│ /tmp/recording/pics/ │ │ - Road sign detection (VPU NCE) │ +│ - Temporary frames │ │ - Privacy blur (faces/plates) │ +│ - Purged when >400MB │ │ - Outputs to Redis + SQLite │ +└───────────────────────────────┘ └─────────────────┬────────────────────────┘ + │ + ┌─────────────────────────────────┤ + ▼ ▼ +┌───────────────────────────────────┐ ┌──────────────────────────────────────┐ +│ CACHED OBSERVATIONS │ │ LANDMARK DATABASE │ +│ /data/recording/ │ │ /data/recording/odc-api.db │ +│ cached_observations/ │ │ - landmarks table │ +│ - Persistent blurred frames │ │ - observations table │ +│ - Referenced by landmark ID │ │ - frames table │ +└───────────────────┬───────────────┘ └─────────────────┬────────────────────┘ + │ │ + └──────────────┬──────────────────────┘ + ▼ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ ODC-API (port 5000) │ +│ - /preview/* — Start/stop preview mode │ +│ - /landmarks/last/N — Get recent detections │ +│ - /landmarks/images/:id — Get observation image paths │ +│ - /landmarks/:id/chips/:chip_id — Get cropped JPEG │ +└───────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ FRAMEKM BUNDLING │ +│ hivemapper-data-logger │ +│ - Collect ~1km of frames + metadata │ +│ - Bundle with GNSS auth signatures │ +│ - Store at /data/recording/framekm/ │ +└───────────────────────────────────┬──────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ UPLOAD PATH │ +│ odc-api → mitmdump (port 8888) → Cloudflare Workers → HERE OLP │ +└──────────────────────────────────────────────────────────────────────────────┘ +``` + +### 8.2 Single Detection Event Trace + +``` +1. Camera captures frame + └── IMX378 → MIPI → VPU ISP → depthai_gate + +2. Frame written to disk + └── /tmp/recording/pics/1709920000123_0001_0042.jpg + +3. map-ai reads frame + └── Runs road sign classifier on VPU NCE + +4. Detection found (speed limit 35) + └── Privacy blur applied to any faces/plates + +5. Observation stored + └── SQLite: observations table (landmark_id, bbox, ts, image_name) + └── File: /data/recording/cached_observations/... + +6. Landmark created/updated + └── SQLite: landmarks table (class_label, lat, lon, confidence) + +7. odc-api exposes data + └── GET /landmarks/last/5 returns detection + └── GET /landmarks/images/{id} returns image path + └── GET /landmarks/{id}/chips/{chip_id} returns cropped JPEG +``` + +--- + +## 9. Replacement Considerations + +### 9.1 Accessing Frames Without odc-api + +**Option 1: Direct File Read** +```bash +# SSH to Bee +ssh -p 2222 root@localhost # via Lucy tunnel + +# Read latest frames +ls -lt /tmp/recording/pics/ | head -10 +cp /tmp/recording/pics/latest_frame.jpg /tmp/ + +# Stream frames (naive) +while true; do + cp $(ls -t /tmp/recording/pics/*.jpg | head -1) /tmp/current.jpg + sleep 0.033 # ~30 FPS +done +``` + +**Pros:** Simple, no service changes +**Cons:** Race conditions, no metadata + +**Option 2: Redis Pub/Sub** +Subscribe to frame events if depthai_gate publishes them: +```python +import redis +r = redis.Redis() +p = r.pubsub() +p.subscribe('frame_ready') +for message in p.listen(): + print(message) # Contains frame path or metadata +``` + +**Pros:** Event-driven, no polling +**Cons:** May not exist in current firmware + +### 9.2 Accessing Frames Without depthai_gate + +**Not recommended** — requires implementing your own DepthAI pipeline. + +If you must: +1. Stop depthai_gate: `systemctl stop depthai_gate` +2. Use Luxonis depthai Python SDK +3. Create minimal pipeline: +```python +import depthai as dai + +pipeline = dai.Pipeline() +cam = pipeline.create(dai.node.ColorCamera) +cam.setResolution(dai.ColorCameraProperties.SensorResolution.THE_4_K) +cam.setIspScale(1, 2) # Downscale to 2028x1024 + +xout = pipeline.create(dai.node.XLinkOut) +xout.setStreamName("video") +cam.video.link(xout.input) + +with dai.Device(pipeline) as device: + q = device.getOutputQueue("video") + while True: + frame = q.get() + cv2.imwrite("/tmp/frame.jpg", frame.getCvFrame()) +``` + +**Pros:** Full control over camera +**Cons:** Breaks all Hivemapper services, loses ML pipeline + +### 9.3 Minimal Path to JPEG Frame + +**Fastest (with existing stack):** +```bash +# Via SSH +ssh -p 2222 root@localhost 'ls -t /tmp/recording/pics/*.jpg | head -1 | xargs cat' > frame.jpg +``` + +**Via API (requires preview mode):** +```bash +curl http://192.168.0.10:5000/api/1/preview/start +sleep 2 +ssh -p 2222 root@localhost 'ls -t /tmp/recording/preview/*.jpg | head -1 | xargs cat' > frame.jpg +curl http://192.168.0.10:5000/api/1/preview/stop +``` + +### 9.4 Building a Custom Camera Interface + +**Requirements:** +1. Maintain depthai_gate (or reimplement VPU control) +2. Expose a REST endpoint for single-frame capture +3. Optionally implement MJPEG streaming + +**Proposed odc-api Addition:** +```typescript +// routes/camera.ts +router.get('/frame', async (req, res) => { + const frames = readdirSync('/tmp/recording/pics') + .filter(f => f.endsWith('.jpg')) + .sort() + .reverse(); + + if (frames.length === 0) { + return res.status(404).send('No frames available'); + } + + const framePath = join('/tmp/recording/pics', frames[0]); + res.sendFile(framePath); +}); + +router.get('/stream', async (req, res) => { + res.writeHead(200, { + 'Content-Type': 'multipart/x-mixed-replace; boundary=frame', + 'Cache-Control': 'no-cache', + }); + + const interval = setInterval(() => { + const frames = readdirSync('/tmp/recording/pics') + .filter(f => f.endsWith('.jpg')) + .sort() + .reverse(); + + if (frames.length > 0) { + const framePath = join('/tmp/recording/pics', frames[0]); + const frameData = readFileSync(framePath); + + res.write('--frame\r\n'); + res.write('Content-Type: image/jpeg\r\n'); + res.write(`Content-Length: ${frameData.length}\r\n\r\n`); + res.write(frameData); + res.write('\r\n'); + } + }, 33); // ~30 FPS + + req.on('close', () => clearInterval(interval)); +}); +``` + +### 9.5 Architecture for Replacement System + +``` +┌──────────────────────────────────────────────────────────────────┐ +│ VARROA CAMERA SERVICE │ +├──────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │ +│ │ depthai_gate │ → │ varroa-camera │ → │ HTTP API │ │ +│ │ (unchanged) │ │ (new service) │ │ (port 80) │ │ +│ └─────────────────┘ └─────────────────┘ └──────────────┘ │ +│ ↓ ↓ ↓ │ +│ /tmp/recording/pics/ Monitor & serve GET /frame │ +│ frames via inotify GET /stream │ +│ GET /landmarks │ +└──────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 10. Open Questions + +| Question | Priority | How to Investigate | +|----------|----------|-------------------| +| Exact depthai_gate pipeline config | High | SSH in, find config files in /opt/ | +| Does depthai_gate publish to Redis? | High | `redis-cli MONITOR` while recording | +| Camera-bridge vs depthai_gate relationship | High | Check systemd deps, trace with strace | +| Preview config format | Medium | Read `getPreviewConfig()` implementation | +| ML model exact location on Bee | Medium | `find /opt /data -name "*.blob" -o -name "*.onnx"` | +| Frame timestamp accuracy | Medium | Compare frame timestamps to GNSS time | + +--- + +## Appendix A: Key File Paths + +| Path | Purpose | +|------|---------| +| `/tmp/recording/pics/` | Live camera frames | +| `/tmp/recording/preview/` | Preview mode frames | +| `/data/recording/cached_observations/` | Landmark observation images | +| `/data/recording/framekm/` | FrameKm upload bundles | +| `/data/recording/odc-api.db` | SQLite database | +| `/opt/camera-bridge/config.json` | Camera configuration | +| `/opt/depthai_gate/` | DepthAI service (estimated) | +| `/opt/odc-api/` | Node.js API service | +| `/sys/class/vpu/` | VPU sysfs interface | + +## Appendix B: Service Dependencies + +``` +multi-user.target + │ + ├── redis.service [t+2s] + │ + ├── depthai_gate.service [t+8s] # MUST start before map-ai + │ │ + │ └── Loads luxonis_vpu.bin + │ + ├── map-ai.service [t+12s] # Depends on depthai_gate + │ │ + │ └── Privacy blur, ML inference + │ + ├── hivemapper-data-logger.service [t+15s] + │ + └── odc-api.service [t+18s] # REST API +``` + +## Appendix C: Port Reference + +| Port | Service | Protocol | Binding | +|------|---------|----------|---------| +| 22 | sshd | TCP | AP only (via socket) | +| 5000 | odc-api | HTTP | AP interface | +| 6379 | Redis | TCP | localhost | +| 8888 | mitmdump | HTTP | localhost | +| 11492 | depthai_gate | HTTP/Flask | localhost | + +--- + +*End of Report*