Skip to content

Add M5StickC device list UI, BLE GATT, and connection indicators#10

Open
dougborg wants to merge 21 commits intof1yaw4y:mainfrom
dougborg:pr/08-m5stick-ui-and-ble
Open

Add M5StickC device list UI, BLE GATT, and connection indicators#10
dougborg wants to merge 21 commits intof1yaw4y:mainfrom
dougborg:pr/08-m5stick-ui-and-ble

Conversation

@dougborg
Copy link
Copy Markdown
Contributor

@dougborg dougborg commented Feb 2, 2026

Stack order: 8/8 — merge after #9 (PR7: power-aware scanning)

Companion PR: deflock-app#37 — Add FlockSquawk scanner integration (USB + BLE) adds the app-side BLE client that connects to the GATT server introduced here.

Summary

  • Replace RSSI history graph with scrollable device list showing detected devices on M5StickC
  • Fix header flicker and remove heartbeat beep
  • Add BLE GATT server for DeFlock companion app communication
  • Smooth battery percentage display with rolling median filter
  • Add BLE/USB connection and power indicators to M5StickC header bar

Test plan

  • Run make test to verify battery smoothing and connection status tests pass
  • Compile and upload to M5StickC Plus2
  • Verify device list scrolls and updates as devices are detected
  • Verify BLE GATT server advertises and accepts connections from DeFlock app
  • Verify battery percentage is stable (no flickering between values)
  • Verify connection indicators show BLE/USB status correctly

🤖 Generated with Claude Code

dougborg and others added 21 commits February 2, 2026 00:48
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Provides per-variant build/upload/flash/monitor targets for all 6
hardware variants, plus LittleFS data upload for variants with audio
assets. Uses GNU Make define/eval/call to generate targets from a
single template.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests compile natively with clang++/g++ — no ESP32 hardware needed.
Covers detectors, device tracker state machine, and threat analyzer
scoring pipeline (37 cases, 126 assertions) targeting the M5Stick
variant's pure-logic headers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move DetectorTypes.h, Detectors.h, DeviceSignatures.h, EventBus.h,
ThreatAnalyzer.h, and TelemetryReporter.h from the M5Stick variant's
src/ into a new top-level common/ directory. Update Makefile to pass
-I common via build.extra_flags for all variants, update test includes,
and fix the M5Stick FQBN (m5stick_c_plus2 -> m5stack_stickc_plus2).

Merge AudioEvent (from M5Fire's EventBus.h) into the shared header so
other variants can adopt it when migrated.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Delete local copies of EventBus.h, DeviceSignatures.h, ThreatAnalyzer.h,
and TelemetryReporter.h from m5fire/src/. Replace legacy ThreatAnalyzer
(simple boolean matching) and TelemetryReporter (DynamicJsonDocument with
nested objects) implementations with the shared detector-based system.

Add ISR-safe deferred event processing with portMUX spinlocks for WiFi,
BLE, and threat events. Add ThreatAnalyzer::tick() heartbeat in loop()
and shouldAlert gate on triggerAlert().

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Delete local copies of EventBus.h, DeviceSignatures.h, ThreatAnalyzer.h,
and TelemetryReporter.h from mini12864/src/. Replace legacy ThreatAnalyzer
and TelemetryReporter implementations with shared detector-based system.

Add ISR-safe deferred event processing with portMUX spinlocks. Move
display notifications (Mini12864DisplayNotifyWifiFrame, ShowAlert) and
audio playback to the main loop's deferred handlers. Add tick() heartbeat
and shouldAlert gate.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Also fix Makefile build.extra_flags override that was clobbering
ESP32 core defines (-DESP32=ESP32 etc). Use build.defines instead,
which is included within build.extra_flags and starts empty.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move variant-specific src/ files into sketch directory to follow
Arduino convention. Keep Flipper's own TelemetryReporter (line-based
protocol for Flipper app). Fix radioType null-check for char array.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Dockerfile builds a debian:bookworm-slim image containing arduino-cli,
ESP32 core v3.0.7, all Arduino libraries (version-pinned), doctest.h,
and pre-warmed core caches for all 4 FQBNs. Source is bind-mounted at
runtime so the image is reusable across branches.

Also adds docker-compose.yml (build-all, test, shell, build-variant
services), entrypoint.sh (seeds doctest.h into bind-mount), .dockerignore,
and Makefile docker-* targets.

Fixes a portability bug in test/mocks/Arduino.h: adds <cstdio> for
snprintf, which macOS clang resolves transitively but Debian clang-14
does not.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Audit all dependency versions against latest available in the Arduino
library index, update where possible, and centralize pins in a single
versions.env file consumed by both Makefile (include) and Dockerfile
(--build-arg).

Version changes:
- Base image: debian:bookworm-slim → debian:trixie-slim (Debian 13)
- arduino-cli: unpinned → 1.4.1
- ArduinoJson: 7.3.0 → 7.4.2
- NimBLE-Arduino: 2.2.1 → 2.3.7
- M5Unified: 0.2.2 → 0.2.11
- Adafruit SSD1306: 2.5.13 → 2.5.16
- doctest: 2.4.11 → 2.4.12
- ESP32 core: 3.0.7 (unchanged — newer causes IRAM overflow)
- U8g2: 2.35.30 (unchanged — already latest in Arduino index)
- Adafruit GFX: 1.12.4 (unchanged — already latest)

Makefile install-deps now pins library versions from versions.env,
matching what the Dockerfile installs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move duplicated content (setup, architecture, telemetry, configuration,
troubleshooting, extending) from 6 variant READMEs into 8 shared docs/
files. Add new docs for build system and testing (previously undocumented).
Restructure CLAUDE.md as a scannable agent gateway with dispatch table.
Fix incorrect .ino filename reference in portable variant README.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Make RadioScanner timing values dynamic across all variants by replacing
static const members with static members initialized to battery-mode
defaults. Add setPerformanceMode() to switch between battery and
external-power scanning parameters.

M5Stick and M5Fire main loops now detect external power via
M5.Power.isCharging() and automatically switch to aggressive scanning
(200ms dwell, 3s BLE scan, 4s interval) and force the display awake.
Non-M5 variants get the API but remain at battery-mode defaults.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
NimBLE GATT server streams newline-delimited JSON telemetry to the
DeFlock app over BLE, enabling iOS support and wireless operation on
Android. When a BLE client connects, scan duty is reduced to share
radio time; when it disconnects (e.g. USB takeover), full scan duty
resumes.

- Add common/BleTransport.h: GATT server with notify characteristic,
  MTU negotiation up to 512, chunked notifications, client state callback
- Modify TelemetryReporter.h: dual output to Serial + BLE, with buffer
  overflow protection and truncation detection
- Modify RadioScanner.h: adaptive scan duty cycle based on BLE client
  state, deferred to main loop for thread safety
- Wire BleTransport into M5StickC setup, guard NimBLE double-init

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Battery readings from M5.Power.getBatteryLevel() jitter at charge
boundaries (e.g. 79%<->80%). Replace raw reads with a rolling median
of the last 8 samples (taken every 3s) to stabilize the display.

The filter is extracted to common/BatterySmoothing.h so the median
logic is host-testable with doctest.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a major refactoring to consolidate shared code, add a comprehensive test suite, and introduce a BLE GATT server for companion app communication. The changes include moving shared headers to common/, adding host-side unit tests, implementing battery smoothing, connection status indicators, and power-aware scanning.

Changes:

  • Consolidated shared code (EventBus, ThreatAnalyzer, Detectors, TelemetryReporter) into common/ directory
  • Added comprehensive unit test suite with doctest framework (device tracker, detectors, battery smoothing, connection status)
  • Introduced BLE GATT server for DeFlock companion app communication on M5StickC
  • Implemented battery percentage smoothing with rolling median filter
  • Added build system infrastructure (Makefile, Dockerfile, versions.env)
  • Added extensive documentation (architecture, testing, build system, configuration, troubleshooting)
  • Implemented thread-safe event processing across all variants using FreeRTOS critical sections
  • Added power-aware scanning with performance mode switching

Reviewed changes

Copilot reviewed 73 out of 76 changed files in this pull request and generated no comments.

Show a summary per file
File Description
versions.env Single source of truth for dependency versions
Makefile Complete build automation with variant targets and Docker support
Dockerfile Reproducible build environment with all dependencies
test/*.cpp Comprehensive unit test suite for core logic
common/*.h Shared headers consolidated from variant-specific copies
docs/*.md Complete documentation suite covering all aspects
variant .ino files Thread-safe event processing and common header includes
RadioScanner.h files Power-aware scanning parameters as mutable static members

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants