-
Notifications
You must be signed in to change notification settings - Fork 7
Add build system, tests, and project documentation #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d9825be
cfa53a3
519a3da
b6a0f24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| .build/ | ||
| test/doctest.h |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## Project Overview | ||
|
|
||
| FlockSquawk is an ESP32 Arduino project for passive RF awareness. It sniffs WiFi management frames and BLE advertisements, matches them against known surveillance device signatures, and outputs alerts via displays, audio, and JSON telemetry over Serial. | ||
|
|
||
| ## Build System | ||
|
|
||
| This is a pure **Arduino IDE** project (no CMake, PlatformIO, or Makefile). Each hardware variant is a self-contained Arduino sketch that opens directly in the IDE. | ||
|
|
||
| **Critical constraint:** ESP32 board package must be **version 3.0.7 or older** (newer versions cause IRAM overflow). | ||
|
|
||
| ### Building with arduino-cli | ||
|
|
||
| ```bash | ||
| # Install ESP32 core | ||
| arduino-cli core install esp32:esp32@3.0.7 | ||
|
|
||
| # Install required libraries | ||
| arduino-cli lib install ArduinoJson NimBLE-Arduino M5Unified | ||
|
|
||
| # Compile a variant (example: M5StickC Plus2) | ||
| arduino-cli compile --fqbn esp32:esp32:m5stick_c_plus2 m5stack/flocksquawk_m5stick/ | ||
|
|
||
| # Upload | ||
| arduino-cli upload --fqbn esp32:esp32:m5stick_c_plus2 --port /dev/ttyUSB0 m5stack/flocksquawk_m5stick/ | ||
|
|
||
| # Serial monitor (115200 baud for JSON telemetry) | ||
| arduino-cli monitor --port /dev/ttyUSB0 --config baudrate=115200 | ||
| ``` | ||
|
|
||
| Board FQBNs vary per variant β check each variant's README for exact board settings. | ||
|
|
||
| ## Hardware Variants | ||
|
|
||
| Six self-contained variants, each in its own directory with a dedicated README: | ||
|
|
||
| | Variant | Path | Display | Audio | | ||
| |---|---|---|---| | ||
| | M5StickC Plus2 | `m5stack/flocksquawk_m5stick/` | Built-in TFT 135x240 | Buzzer tones | | ||
| | M5Stack FIRE | `m5stack/flocksquawk_m5fire/` | Built-in TFT 320x240 | Built-in speaker | | ||
| | Mini12864 | `Mini12864/flocksquawk_mini12864/` | ST7567 LCD 128x64 | I2S (MAX98357A) | | ||
| | 128x32 OLED | `128x32_OLED/flocksquawk_128x32/` | SSD1306/SH1106 I2C | I2S (MAX98357A) | | ||
| | 128x32 Portable | `128x32_OLED/flocksquawk_128x32_portable/` | SSD1306/SH1106 I2C | GPIO buzzer | | ||
| | Flipper Zero | `flipper-zero/dev-board-firmware/` | None (UART only) | None | | ||
|
|
||
| Variants do **not** share source files β each has its own copy of the core headers in `src/`. Changes to shared logic (EventBus, ThreatAnalyzer, etc.) must be applied to each variant individually. | ||
|
|
||
| ## Architecture | ||
|
|
||
| Data flows through a publish-subscribe pipeline: | ||
|
|
||
| ``` | ||
| RadioScannerManager (WiFi promiscuous + BLE scan) | ||
| β EventBus (WiFiFrameEvent / BluetoothDeviceEvent) | ||
| β ThreatAnalyzer (signature matching β ThreatEvent) | ||
| β TelemetryReporter (JSON over Serial) | ||
| β Display (variant-specific UI) | ||
| β SoundEngine (alerts) | ||
| ``` | ||
|
|
||
| ### Core subsystems (in each variant's `src/`) | ||
|
|
||
| - **EventBus.h** β Header-only static publish/subscribe. Event types: `WifiFrameCaptured`, `BluetoothDeviceFound`, `ThreatIdentified`, `SystemReady`. | ||
| - **RadioScanner.h** β WiFi promiscuous mode (channels 1-13, 1s hop interval) and BLE scanning via NimBLE (5s interval, 1s duration). Uses FreeRTOS portMUX spinlocks for thread safety between ISR callbacks and main loop. | ||
| - **ThreatAnalyzer.h** β Compares observations against `DeviceSignatures.h`. Tracks up to 32 devices with LRU eviction (states: NEW_DETECT β IN_RANGE β DEPARTED). Alert threshold: 65% certainty. | ||
| - **DeviceSignatures.h** β Static arrays of known SSIDs, MAC OUI prefixes, BLE device names, and service UUIDs. | ||
| - **TelemetryReporter.h** β Serializes detections as JSON via ArduinoJson (StaticJsonDocument<512>). | ||
| - **SoundEngine.h** β I2S WAV playback from LittleFS (Mini12864/128x32) or M5.Speaker tone generation (M5Stack variants). | ||
|
|
||
| ### Pluggable detector system (M5Stick variant only β newest architecture) | ||
|
|
||
| The M5Stick variant introduces a function-pointer-based detector registry: | ||
|
|
||
| - **DetectorTypes.h** β Defines `DetectorResult` (matched/weight/name) and `WiFiDetectorEntry`/`BLEDetectorEntry` structs. | ||
| - **Detectors.h** β Registers detector functions. WiFi: `detectSsidFormat` (weight 75), `detectSsidKeyword` (45), `detectWifiMacOui` (20). BLE: `detectBleName` (55), `detectRavenCustomUuid` (80), `detectRavenStdUuid` (10), `detectBleMacOui` (20). | ||
| - Weighted scoring with subsumption (higher-confidence detectors override lower ones) and RSSI-based modifiers. | ||
|
|
||
| This detector pattern is the intended direction for all variants. | ||
|
|
||
| ## Thread Safety Pattern | ||
|
|
||
| WiFi promiscuous callbacks and BLE scan callbacks run on different cores/tasks than `loop()`. All variants use the same pattern: | ||
|
|
||
| - `portMUX_TYPE` spinlocks guard shared volatile state in ISR callbacks | ||
| - `taskENTER_CRITICAL` / `taskEXIT_CRITICAL` for atomic reads/writes | ||
| - Main loop copies event data under lock, then processes outside the critical section | ||
|
|
||
| ## Key Constants | ||
|
|
||
| - WiFi channel hop: 1 second | ||
| - BLE scan interval: 5 seconds, 1 second duration | ||
| - Device tracking slots: 32 (LRU eviction) | ||
| - Device departure timeout: 60 seconds | ||
| - Heartbeat re-alert interval: 10 seconds | ||
| - Alert certainty threshold: 65% | ||
| - Serial baud rate: 115200 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| # FlockSquawk β arduino-cli build system | ||
| # Usage: make help | ||
|
|
||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # User-configurable variables | ||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| PORT ?= | ||
| BAUD ?= 115200 | ||
| VARIANT ?= m5stick | ||
| CORE_VERSION ?= 3.0.7 | ||
|
|
||
| BUILD_DIR := $(CURDIR)/.build | ||
|
|
||
| # Arduino toolchain paths (Linux default; macOS overrides below) | ||
| ARDUINO_DATA ?= $(HOME)/.arduino15 | ||
| ifeq ($(shell uname),Darwin) | ||
| ARDUINO_DATA := $(HOME)/Library/Arduino15 | ||
| endif | ||
|
|
||
| ESPTOOL = $(firstword $(wildcard $(ARDUINO_DATA)/packages/esp32/tools/esptool_py/*/esptool.py) \ | ||
| $(wildcard $(ARDUINO_DATA)/packages/esp32/tools/esptool_py/*/esptool)) | ||
| MKLITTLEFS = $(firstword $(wildcard $(ARDUINO_DATA)/packages/esp32/tools/mklittlefs/*/mklittlefs)) | ||
|
|
||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # Variant definitions: NAME FQBN SKETCH_DIR HAS_DATA | ||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| VARIANTS := m5stick m5fire mini12864 oled portable flipper | ||
|
|
||
| m5stick_FQBN := esp32:esp32:m5stick_c_plus2 | ||
| m5stick_SKETCH := m5stack/flocksquawk_m5stick | ||
| m5stick_DATA := | ||
|
|
||
| m5fire_FQBN := esp32:esp32:m5stack_fire | ||
| m5fire_SKETCH := m5stack/flocksquawk_m5fire | ||
| m5fire_DATA := 1 | ||
|
|
||
| mini12864_FQBN := esp32:esp32:esp32 | ||
| mini12864_SKETCH := Mini12864/flocksquawk_mini12864 | ||
| mini12864_DATA := 1 | ||
|
|
||
| oled_FQBN := esp32:esp32:esp32 | ||
| oled_SKETCH := 128x32_OLED/flocksquawk_128x32 | ||
| oled_DATA := 1 | ||
|
|
||
| portable_FQBN := esp32:esp32:esp32 | ||
| portable_SKETCH := 128x32_OLED/flocksquawk_128x32_portable | ||
| portable_DATA := | ||
|
|
||
| flipper_FQBN := esp32:esp32:esp32s2 | ||
| flipper_SKETCH := flipper-zero/dev-board-firmware/flocksquawk-flipper | ||
| flipper_DATA := | ||
|
|
||
| # LittleFS defaults (4 MB flash, default partition table) | ||
| LITTLEFS_OFFSET ?= 0x290000 | ||
| LITTLEFS_SIZE ?= 0x160000 | ||
| LITTLEFS_PAGE ?= 256 | ||
| LITTLEFS_BLOCK ?= 4096 | ||
|
|
||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # Auto-detect serial port when PORT is empty | ||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| define detect_port | ||
| $(or $(PORT),$(firstword $(wildcard /dev/ttyUSB* /dev/ttyACM* /dev/cu.usbserial* /dev/cu.usbmodem* /dev/cu.wchusbserial*))) | ||
| endef | ||
|
|
||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # Per-variant target template | ||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| define VARIANT_TARGETS | ||
|
|
||
| .PHONY: build-$(1) upload-$(1) flash-$(1) monitor-$(1) | ||
|
|
||
| build-$(1): | ||
| arduino-cli compile \ | ||
| --fqbn $($(1)_FQBN) \ | ||
| --output-dir $(BUILD_DIR)/$(1) \ | ||
| $($(1)_SKETCH) | ||
|
|
||
| upload-$(1): | ||
| $$(if $$(call detect_port),, $$(error PORT not set and no device auto-detected)) | ||
| arduino-cli upload \ | ||
| --fqbn $($(1)_FQBN) \ | ||
| --port $$(call detect_port) \ | ||
| --input-dir $(BUILD_DIR)/$(1) \ | ||
| $($(1)_SKETCH) | ||
|
|
||
| flash-$(1): build-$(1) upload-$(1) | ||
|
|
||
| monitor-$(1): | ||
| $$(if $$(call detect_port),, $$(error PORT not set and no device auto-detected)) | ||
| arduino-cli monitor \ | ||
| --port $$(call detect_port) \ | ||
| --config baudrate=$(BAUD) | ||
|
|
||
| # Only generate upload-data target for variants with a data/ directory | ||
| ifneq ($($(1)_DATA),) | ||
| .PHONY: upload-data-$(1) | ||
| upload-data-$(1): | ||
| $$(if $$(call detect_port),, $$(error PORT not set and no device auto-detected)) | ||
| $$(if $(MKLITTLEFS),, $$(error mklittlefs not found β install ESP32 core first)) | ||
| $$(if $(ESPTOOL),, $$(error esptool not found β install ESP32 core first)) | ||
| @echo "Building LittleFS image from $($(1)_SKETCH)/data β¦" | ||
| $(MKLITTLEFS) -c $($(1)_SKETCH)/data \ | ||
| -p $(LITTLEFS_PAGE) -b $(LITTLEFS_BLOCK) \ | ||
| -s $(LITTLEFS_SIZE) \ | ||
| $(BUILD_DIR)/$(1)/littlefs.bin | ||
| @echo "Flashing LittleFS image to $$(call detect_port) β¦" | ||
| $(ESPTOOL) --chip esp32 --port $$(call detect_port) --baud 921600 \ | ||
| write_flash $(LITTLEFS_OFFSET) $(BUILD_DIR)/$(1)/littlefs.bin | ||
| endif | ||
|
|
||
| endef | ||
|
|
||
| # Expand targets for every variant | ||
| $(foreach v,$(VARIANTS),$(eval $(call VARIANT_TARGETS,$(v)))) | ||
|
|
||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # Shorthand targets (use VARIANT variable) | ||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| .PHONY: build upload flash monitor | ||
|
|
||
| build: build-$(VARIANT) | ||
| upload: upload-$(VARIANT) | ||
| flash: flash-$(VARIANT) | ||
| monitor: monitor-$(VARIANT) | ||
|
|
||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # Host-side unit tests (no ESP32 needed) | ||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| TEST_CXX ?= clang++ | ||
| TEST_CXXFLAGS := -std=c++17 -Wall -Wextra -g -O0 | ||
| TEST_INCLUDES := -isystem test/mocks -I m5stack/flocksquawk_m5stick/src -I test | ||
| TEST_SRCS := test/test_main.cpp test/eventbus_impl.cpp \ | ||
| test/test_detectors.cpp test/test_device_tracker.cpp \ | ||
| test/test_threat_analyzer.cpp | ||
| TEST_BIN := $(BUILD_DIR)/test_runner | ||
| DOCTEST_URL := https://raw.githubusercontent.com/doctest/doctest/v2.4.11/doctest/doctest.h | ||
|
|
||
| .PHONY: test test-verbose fetch-doctest | ||
|
|
||
| fetch-doctest: test/doctest.h | ||
|
|
||
| test/doctest.h: | ||
| @echo "Fetching doctest.h β¦" | ||
| curl -sL -o $@ $(DOCTEST_URL) | ||
|
Comment on lines
+137
to
+145
|
||
|
|
||
| test: fetch-doctest | ||
| @mkdir -p $(BUILD_DIR) | ||
| $(TEST_CXX) $(TEST_CXXFLAGS) $(TEST_INCLUDES) $(TEST_SRCS) -o $(TEST_BIN) | ||
| $(TEST_BIN) | ||
|
|
||
| test-verbose: fetch-doctest | ||
| @mkdir -p $(BUILD_DIR) | ||
| $(TEST_CXX) $(TEST_CXXFLAGS) $(TEST_INCLUDES) $(TEST_SRCS) -o $(TEST_BIN) | ||
| $(TEST_BIN) --success | ||
|
|
||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # Global targets | ||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| .PHONY: all clean install-deps help | ||
|
|
||
| all: $(foreach v,$(VARIANTS),build-$(v)) | ||
|
|
||
| clean: | ||
| rm -rf $(BUILD_DIR) | ||
|
|
||
| install-deps: | ||
| arduino-cli core update-index | ||
| arduino-cli core install esp32:esp32@$(CORE_VERSION) | ||
| arduino-cli lib install \ | ||
| ArduinoJson \ | ||
| "NimBLE-Arduino" \ | ||
| M5Unified \ | ||
| U8g2 \ | ||
| "Adafruit GFX Library" \ | ||
| "Adafruit SSD1306" | ||
|
|
||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| # Help (default target) | ||
| # ββββββββββββββββββββββββββββββββββββββββββββββ | ||
| .DEFAULT_GOAL := help | ||
|
|
||
| help: | ||
| @echo "FlockSquawk Build System" | ||
| @echo "========================" | ||
| @echo "" | ||
| @echo "Variants: $(VARIANTS)" | ||
| @echo "" | ||
| @echo "Global targets:" | ||
| @echo " make help Show this message" | ||
| @echo " make all Compile all variants" | ||
| @echo " make test Run host-side unit tests" | ||
| @echo " make test-verbose Run tests with per-assertion output" | ||
| @echo " make clean Remove build output (.build/)" | ||
| @echo " make install-deps Install ESP32 core and Arduino libraries" | ||
| @echo "" | ||
| @echo "Per-variant targets (replace <v> with a variant name above):" | ||
| @echo " make build-<v> Compile sketch" | ||
| @echo " make upload-<v> Upload firmware to device" | ||
| @echo " make flash-<v> Compile + upload" | ||
| @echo " make monitor-<v> Open serial monitor" | ||
| @echo " make upload-data-<v> Flash LittleFS data (m5fire, mini12864, oled only)" | ||
| @echo "" | ||
| @echo "Shorthand (uses VARIANT, default: $(VARIANT)):" | ||
| @echo " make build => build-\$$(VARIANT)" | ||
| @echo " make upload => upload-\$$(VARIANT)" | ||
| @echo " make flash => flash-\$$(VARIANT)" | ||
| @echo " make monitor => monitor-\$$(VARIANT)" | ||
| @echo "" | ||
| @echo "Variables:" | ||
| @echo " PORT=<path> Serial port (auto-detected if unset)" | ||
| @echo " BAUD=<rate> Monitor baud rate (default: 115200)" | ||
| @echo " VARIANT=<name> Default variant (default: m5stick)" | ||
| @echo " CORE_VERSION=<ver> ESP32 core version (default: 3.0.7)" | ||
| @echo "" | ||
| @echo "Examples:" | ||
| @echo " make build-m5stick" | ||
| @echo " make flash-oled PORT=/dev/cu.usbserial-0001" | ||
| @echo " make upload-data-mini12864 PORT=/dev/ttyUSB0" | ||
| @echo " make all" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation states "This is a pure Arduino IDE project (no CMake, PlatformIO, or Makefile)" but this PR adds a Makefile. This is contradictory. The statement should be updated to acknowledge that a Makefile wrapper around arduino-cli is now available, or the phrasing should be clarified to indicate the project doesn't require these tools but now supports optional Makefile-based workflows.