diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e2213f --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Build output +/build/ + +# SDD scratch (ledger, briefs, reports, review packages) +/.superpowers/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..32a81ad --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.16) +project(bfebbx CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +add_compile_options(-Wall -Wextra) + +enable_testing() + +# --- library sources (grows as tasks are added) --- +add_library(bfebbx_lib + src/naming.cpp + src/betaflight.cpp + src/mounts.cpp + src/serial.cpp + src/eject.cpp + src/cli.cpp +) +target_include_directories(bfebbx_lib PUBLIC src) + +# --- platform-specific linking --- +if(APPLE) + target_link_libraries(bfebbx_lib PUBLIC + "-framework DiskArbitration" + "-framework CoreFoundation") +endif() + +# --- tests --- +add_executable(test_naming tests/test_naming.cpp) +target_link_libraries(test_naming PRIVATE bfebbx_lib) +add_test(NAME naming COMMAND test_naming) + +add_executable(test_betaflight_parse tests/test_betaflight_parse.cpp) +target_link_libraries(test_betaflight_parse PRIVATE bfebbx_lib) +add_test(NAME betaflight_parse COMMAND test_betaflight_parse) + +add_executable(test_mounts tests/test_mounts.cpp) +target_link_libraries(test_mounts PRIVATE bfebbx_lib) +add_test(NAME mounts COMMAND test_mounts) + +add_executable(test_serial tests/test_serial.cpp) +target_link_libraries(test_serial PRIVATE bfebbx_lib) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(test_serial PRIVATE util) # openpty +endif() +add_test(NAME serial COMMAND test_serial) + +add_executable(test_betaflight_proto tests/test_betaflight_proto.cpp) +target_link_libraries(test_betaflight_proto PRIVATE bfebbx_lib) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(test_betaflight_proto PRIVATE util) +endif() +add_test(NAME betaflight_proto COMMAND test_betaflight_proto) + +# --- main executable --- +add_executable(bfebbx src/main.cpp) +target_link_libraries(bfebbx PRIVATE bfebbx_lib) + +add_executable(test_cli tests/test_cli.cpp) +target_link_libraries(test_cli PRIVATE bfebbx_lib) +add_test(NAME cli COMMAND test_cli) diff --git a/README.md b/README.md new file mode 100644 index 0000000..c3d7a4b --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# bfebbx — Betaflight Extract Blackbox + +Extract a Betaflight flight controller's blackbox log to a timestamped, +craft-named file in a single command. macOS and Linux. + +## What it does + +1. Auto-detects the FC's serial port. +2. Reads `craft_name` over the Betaflight CLI. +3. Sends `msc` to reboot the FC into USB mass-storage mode. +4. Waits for the storage volume to mount, then copies `btfl_all.bbl` to + `__.bbl`. +5. Ejects the volume. + +## Build + +```bash +cmake -S . -B build +cmake --build build +ctest --test-dir build # run unit tests +``` + +The binary is `build/bfebbx`. + +Requirements: a C++17 compiler and CMake ≥ 3.16. No third-party libraries. +macOS links the system DiskArbitration/CoreFoundation frameworks. + +## Usage + +``` +bfebbx [--port PORT] [--output DIR] [--craft-name NAME] [--timeout SEC] [-v] +``` + +| Flag | Default | Meaning | +|------|---------|---------| +| `--port` | auto-detect | Serial device (e.g. `/dev/tty.usbmodem1101`). | +| `--output` | current dir | Directory for the extracted `.bbl`. | +| `--craft-name` | read from FC | Override / fallback craft name. | +| `--timeout` | 30 | Seconds to wait for the storage volume. | +| `-v` | off | Verbose progress on stderr. | + +### Exit codes + +| Code | Meaning | +|------|---------| +| 0 | Success | +| 2 | No candidate serial port | +| 3 | Multiple candidate ports (pass `--port`) | +| 4 | Serial open / handshake failure | +| 5 | Storage volume did not appear before timeout | +| 6 | `btfl_all.bbl` not found on the volume | +| 7 | Copy failed | +| 8 | Destination file already exists | +| 64 | Usage error | + +## Manual integration test (requires a real FC) + +The pure logic is unit-tested. The serial handshake and eject need hardware: + +1. Flash/confirm a Betaflight FC with blackbox logs on onboard flash. +2. Connect it via USB. Confirm the port appears: `ls /dev/tty.usbmodem*` + (macOS) or `ls /dev/ttyACM*` (Linux). +3. Run `./build/bfebbx -v` in an empty directory. +4. Verify: + - The craft name is read and printed. + - The FC re-enumerates as a USB drive. + - A file `__