Skip to content

PRD revisions 3–5, copyleft relicense, BLE telemetry spec, and the PFD layout mockup - #1

Merged
allenmcghan merged 7 commits into
mainfrom
claude/levil-air-comparison-ho6xao
Aug 9, 2026
Merged

PRD revisions 3–5, copyleft relicense, BLE telemetry spec, and the PFD layout mockup#1
allenmcghan merged 7 commits into
mainfrom
claude/levil-air-comparison-ho6xao

Conversation

@allenmcghan

@allenmcghan allenmcghan commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Seven commits. Started as a comparison against the Levil iLevil / BOM line, ended up changing what the project is, and finishes with a working layout prototype and the repository's first CI.

Important

This relicenses the project. Code moves MIT → GPL-2.0-or-later, hardware moves CERN-OHL-P-2.0 → CERN-OHL-S-2.0. Specifications stay CC-BY. Read that section first — it is the least reversible thing here.


1. Relicensing to copyleft

Artifact Was Now
Firmware, app, tools MIT GPL-2.0-or-later
Board files, enclosure models CERN-OHL-P-2.0 CERN-OHL-S-2.0
Documentation and specifications CC-BY-4.0 unchanged

The purpose of the project is to put this capability into the world permanently, and a permissive license lets a better-funded fork take the work closed and outrun the original. Copyleft prevents that at no cost to any use the project cares about — building, selling, forking and competing all remain permitted.

GPL v2 or later specifically, because it matches MakerPlane. Verified from their source headers: version 2 of the License, or (at your option) any later version. That is GPL-2.0-or-later, not 2.0-only, so code moves in both directions without relicensing and GPLv3 stays reachable if ArduPilot code is ever wanted for the v2 bus.

Specifications stay permissive deliberately. A protocol nobody may adopt freely does not outlive its implementation. CAN-FIX is Creative Commons for the same reason.

LICENSE and LICENSE-HARDWARE are replaced with the canonical upstream texts, verbatim.

2. MakerPlane as a neighbour, not a competitor (new section 24)

Junco stays a separate project and contributes a plugin. FIX-Gateway is explicitly protocol-agnostic, so a Junco source is the shape it already expects, and that reaches pyEFIS as a panel display.

Six items leave Junco's scope because FIX-Gateway already has them: traffic (Stratux plugin), the second recording, annunciation, panel display, multi-source voting, and Pi baro/IMU.

What remains is the part nobody has built: a two-stroke with CDI ignition and no ECU. The existing open engine plugins are Grand Rapids EIS and MegaSquirt, and both assume an engine with a computer in it.

Section 20 stops being a business plan. The project ends at v2.

3. Traffic as a pluggable channel (new section 22)

The app sees targets, not a method, and the node is outside the path for every backend. Recorded as a display requirement rather than a footnote: a traffic screen must not imply completeness. Part 103 aircraft mostly carry no ADS-B Out, TIS-B only uplinks inside a service volume some other equipped aircraft triggers, and internet feeds have no coverage below roughly 1000–2000 ft AGL. The traffic most likely to conflict with a powered parachute at 800 feet is the traffic least likely to appear.

4. Compute platform opened to a Pi-class build (new section 23)

The 0–50°C rating is an enclosure and airflow requirement, not a disqualifier — the aircraft already routes a pitot line there, so ram air is available. Two distinctions survive the thermal work and become mandatory on that build: read-only root with the log on a separate card, and a supercapacitor sized for an orderly stop.

5. spec/ble-telemetry.md — the document that was blocking code

Four notify characteristics, one per rate class; a common header so an unrecognised characteristic can be discarded safely; value, source, status triples; monotonic node time with published timestamps explicitly not corrected after a clock write; and exactly two writable characteristics, neither able to influence a published value — design rule 1 expressed in the protocol rather than asserted alongside it.

The source tag enumeration is defined here and referenced from log-format.md rather than restated. Tag 0x41, mixed node-and-phone derivation, is load-bearing: such a value inherits the weaker assurance of its inputs, and vertical speed may never carry it.

6. Revision 5 — closing the open questions

Fifteen questions were decidable at a desk and blocking firmware. The three irreversible ones first:

Decision Why
BLE UUID base frozen at 1761601a-XXXX-4e69-b9a1-b45cf63c7638 Future versions may allocate new slots, never redefine one
Log is a preallocated file on FAT32, not a raw partition Design rule 4 — the owner reads the card on any laptop
Profile hash over the bytes actually stored Otherwise changing the serializer invalidates every cached copy

Two came out better than proposed. Integer SI encoding made the NaN convention redundant, so it was deleted — the status bit is now the only validity signal. And log payloads are byte-identical to BLE payloads, so the node serializes once and the link and the card cannot disagree about what a flight contained.

Design rule 9 added — advisory-only data never raises an alert. New docs/open-questions.md indexes what remains. Spec open items drop 19 → 10.

7. Doc resync

Seven files still described the revision 1 product. Two were live ambiguities rather than stale text: log-format.md never required source tags on records, so a compliant logger could have shipped with none; and §22 didn't say where traffic backend selection lives (resolved app-side — a receiver is in the flight bag, not on the airframe).

8. PFD layout mockup, an APK, and the first CI

app/mockup/ is a primary flight display in the standard glass-panel layout — attitude centre, speed tape left, altitude tape right, VSI, HSI below, engine strip down the edge. It is not the client and not on the path to being it; it exists so layout questions get answered before anyone writes an APK.

It already answered one. Design rule 8 as twenty small source tags reads as noise. The answer it proposes is the one real avionics already use — annunciate the source on the instrument: PHONE AHRS · ADVISORY on the attitude indicator, cyan for node-plumbed, magenta for GPS-derived. Same rule, native idiom, almost no visual cost.

It half-answered another. The attitude needed low-pass filtering to be watchable sitting still on a desk. §15 asks whether phone attitude survives a vibrating airframe; that is an early data point and not an encouraging one.

Packaged as a PWA (offline via service worker, installs fullscreen and landscape-locked) and wrapped in an Android APK. The wrapper exists for exactly one reason: sensors require a secure context, so a WebView on file:// would silently kill them. It serves bundled assets over an https:// origin and intercepts locally — nothing leaves the device.

The APK is not committed. .gitignore already excluded *.apk and that decision stands. .github/workflows/mockup-apk.yml is where it comes from: artifact on every run, release asset on a mockup-v* tag. The workflow also checks the APK contains its web assets, since a wrapper shipped without its payload would still build and install cleanly. First run passed in 33 s.

Two build notes recorded in the script for whoever hits them: build-tools 34.0.0's d8 fails on JDK 21 on every class with an opaque String.length() NPE, and passing file lists to d8 via @argfile produces the identical error for a different reason (trailing newline → empty argument).

§19's PWA row is sharpened rather than reversed — Web Bluetooth reaches the node on Android, so the transport objection now applies to iOS alone; what still decides it is duration, since a web page has no background operation and drops the link when suspended.


Section numbering runs 1–25 contiguous after two renumberings. Design rules match at nine across README.md and docs/prd.md.

Needs the maintainer: ESP32-S31 appears three times and refers to no Espressif part that exists.

Phase 0 was never blocked — an ESP32, one baro, a breadboard, ground runs. Adding one thermocouple and a logging laptop closes four of the twelve open measurements at once, including both thermal rows that decide the Pi-class build.

claude added 3 commits August 8, 2026 19:36
Traffic becomes a channel with pluggable backends, following the pattern
section 9 already uses for fuel. The node is explicitly outside the path
for every backend, so design rule 1 holds without needing to be defended.

The section 4 non-goal is re-scoped from "ADS-B receive" to "a Junco-built
ADS-B receiver." Consuming GDL90 costs the node nothing, because the
receiver belongs to the pilot and the listener is a socket in the app.
That is a different feature from emitting it, which stays in phase 2.

A Linux single-board variant is admitted as a supported second build.
The 0 to 50C commercial rating is recorded as an enclosure and airflow
requirement rather than a disqualifier: the aircraft already routes a
pitot line to the node's location, so ram air is available there. The
distinctions that survive the thermal work, the boot medium and the
unclean-stop risk, become mandatory requirements on that build instead.

New sections 22 and 23. Revision history renumbered to 24.

Also recorded:
- Why the ESP32-S3 cannot decode ADS-B, as bandwidth arithmetic
- What ADS-B In does not show a Part 103 aircraft, as a display requirement
- Internet-sourced traffic may never alert
- Four new open items in section 15, instrumented during phase 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8P8RZQELMR3NQ7Zacx79v
The README described the revision 1 product: GDL90 out as a headline
feature, a $250 parts target, and seven design rules. Revision 2 demoted
GDL90 to phase 2, cut the target to $180, and added the source-tagging
rule. Fixed all three.

app/README.md claimed iOS users get GDL90 into ForeFlight, which revision 2
gave up, and led with Wi-Fi socket binding as the reason the client is
native. BLE reachability is the first reason now; the binding still matters
for the on-demand AP but that AP is never used in flight.

firmware/README.md had phase 0 watching for GNSS loss on a node that no
longer has GNSS. Now watches BLE dropouts and logs enclosure temperature,
since that measurement decides the Pi-class question and the hardware is
already running for the EMI work.

Spec gaps found while checking consistency:

- log-format.md required design rule 7 header content but never required
  design rule 8 source tags on records, and said nothing about the clock
  offset that makes the node and phone logs merge
- aircraft-profile.md did not say what stays out of the profile, which
  revision 3 made load-bearing: traffic backend selection is app-side
  because a receiver lives in the flight bag, not on the airframe
- spec/README.md listed ble-telemetry.md as merely not started rather than
  as the document blocking implementation

Also reconciled the PRD firmware license to MIT, matching LICENSE, the
README, and MAINTAINERS.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8P8RZQELMR3NQ7Zacx79v
Relicense code from MIT to GPL-2.0-or-later and hardware from
CERN-OHL-P-2.0 to CERN-OHL-S-2.0. Specifications stay CC-BY.

The purpose of the project is to put this capability into the world
permanently, and a permissive license lets a better funded fork take the
work closed and outrun the original. Copyleft prevents that at no cost to
any use the project cares about: building, selling, forking and competing
all remain permitted.

GPL v2 or later specifically, because it matches MakerPlane. Their source
headers read "version 2 of the License, or (at your option) any later
version," so code moves in both directions between this project and
FIX-Gateway or pyEFIS without relicensing, and GPLv3 stays reachable if
ArduPilot code is ever wanted for the v2 bus.

Specifications stay permissive on purpose. They are meant to be
implemented by anyone in anything, and a protocol nobody may adopt freely
does not outlive its implementation. CAN-FIX is Creative Commons for the
same reason.

New section 24 positions Junco as the engine and air data front end for
the MakerPlane stack rather than a parallel instrument system. Junco stays
a separate project and contributes a plugin: FIX-Gateway is explicitly
protocol-agnostic, so a Junco source is the shape it already expects, and
that reaches pyEFIS as a panel display.

Six items leave Junco's scope because FIX-Gateway already has them, among
them the traffic work from revision 3, the annunciator, and the second
recording. What remains is the part nobody has built: a two-stroke with
CDI ignition and no ECU. The existing open engine plugins are Grand Rapids
EIS and MegaSquirt, both of which assume an engine with a computer in it.

Section 20 stops being a business plan. The project ends at v2; a boxed
product and an autopilot are described so the architecture does not
foreclose them, and are explicitly left to anyone who wants them.

Section 19 gains rows for AP_Periph, which defeats half the original
ArduPilot rejection by being genuinely publish-only but fails v1 on BLE,
STM32-only, and thermocouples; and the DroneCAN versus CAN-FIX question,
recorded and deliberately not decided since v1 has no bus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8P8RZQELMR3NQ7Zacx79v
@allenmcghan allenmcghan changed the title PRD revision 3: traffic as a pluggable channel, second compute path PRD revisions 3 and 4: pluggable traffic, second compute path, copyleft relicense Aug 9, 2026
This is the document that was blocking firmware and app work. Every value
the pilot sees crosses this link, and PRD success criterion 4 requires a
stranger to write a second client from it without reading the Android
source.

Revision 4 made that requirement easier to hold honestly: there are now
two expected reference clients, the Android app and a FIX-Gateway plugin
in Python. A rule only one of them can satisfy is the wrong rule.

Settled here:

- Four notify characteristics, one per rate class, taken from the channel
  table in PRD section 8 rather than one characteristic per channel
- A common header on every notification so an unrecognised characteristic
  can be discarded safely
- Per-channel value, source, status triples, with the status bit
  authoritative for validity rather than the value
- Connection interval, MTU negotiation, and notifications over indications
- Monotonic node time with the client writing wall clock on connect, and
  published timestamps explicitly not corrected afterward
- Exactly two writable characteristics, neither able to influence a
  published value, which is design rule 1 expressed in the protocol

The source tag enumeration is defined here and referenced from
log-format.md rather than restated, because two copies of an enumeration
diverge. It allocates phone-side sources too, which never cross this link
but do appear in the log. The mixed-derivation tag is load-bearing: a
value combining node and phone channels inherits the weaker assurance of
the two, and vertical speed may never carry it.

Eight items are recorded as not yet specified, including the UUID base,
which must be generated once and frozen before any unit ships, and the
two-clients-at-once case that section 24 turned from hypothetical into
real.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8P8RZQELMR3NQ7Zacx79v
@allenmcghan allenmcghan changed the title PRD revisions 3 and 4: pluggable traffic, second compute path, copyleft relicense PRD revisions 3 and 4, copyleft relicense, and the BLE telemetry spec Aug 9, 2026
Fifteen questions sitting in "not yet specified" lists were decidable at a
desk and were blocking firmware. A question that can be answered without
data, and is instead left open, becomes a decision someone makes
accidentally while implementing.

The three irreversible ones first, since each costs field hardware to
change later:

- BLE UUID base generated and frozen, with the allocation slot scheme
  recorded so future versions can add characteristics but never redefine
  an existing one
- Log is a preallocated file on FAT32, not a raw partition. Design rule 4
  says the owner owns the data, and a file means they pull the card and
  read it on any laptop. Raw-sector recovery already handles the
  interrupted-write case that usually argues for a partition
- Profile hash is SHA-256 over the bytes actually stored, not over a
  re-serialization, so changing the serializer cannot silently invalidate
  every cached copy and log header referencing an unmodified profile

Also closed: integer SI channel encoding with no floats and no display
units on the wire, which drops the redundant NaN convention since the
status bit is now the only validity signal; whole-profile atomic
configuration writes; "in flight" defined as the log file being open,
which is one condition the node already tracks; node-side transition
counts, because a client that connected late would count wrong; bonding
required to write but not to subscribe, putting the security boundary on
the design rule 1 line; two concurrent clients with first-clock-write-
wins; TOML source plus a compiled binary form, which resolves the tension
between hand-editability and not parsing text in flight firmware; schema
versioning that refuses an unknown version rather than guessing;
validation rules; record type allocation; ArduPilot-style descriptors;
and seizure precursors publishing a raw rate rather than a judgment.

Log record payloads are now byte-identical to BLE characteristic
payloads, with the log adding only magic, type and CRC. One serializer
instead of two removes the category of defect where the link and the card
disagree about what a flight contained.

Design rule 9 added: advisory-only data never raises an alert. This was
local to section 22 in revision 3 and generalises past traffic, and a rule
living inside one section gets forgotten by the next section that needs
it.

New docs/open-questions.md indexes what remains, separated into what needs
a measurement, what is blocked on a prior decision, and what is merely not
urgent. Spec open items drop from 19 to 10.

Also fixes the status line, which was left at revision 3 when revision 4
landed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W8P8RZQELMR3NQ7Zacx79v
@allenmcghan allenmcghan changed the title PRD revisions 3 and 4, copyleft relicense, and the BLE telemetry spec PRD revisions 3–5: copyleft relicense, BLE telemetry spec, and closing the open questions Aug 9, 2026
claude added 2 commits August 9, 2026 03:31
A primary flight display laid out the way pilots already read one:
attitude centre, airspeed tape left, altitude tape right, VSI, HSI below,
engine strip down the edge. It exists to settle layout questions before
anyone writes an APK, because a tape range or a band order is cheap to
change here and expensive once there is an app.

It answers one question already. Design rule 8 as twenty small source tags
reads as noise on a real layout. The answer this proposes is the one real
avionics already use: annunciate the source on the instrument. PHONE AHRS
ADVISORY sits on the attitude indicator, cyan marks node-plumbed channels,
and magenta marks GPS-derived data, which is a convention every pilot
already reads without thinking. Same rule, native idiom, almost no visual
cost.

It half-answers a second. The attitude is low-pass filtered because raw
phone fusion is too jittery to watch sitting still on a desk. Section 15
asks whether phone attitude survives a vibrating airframe; this is an
early data point and not an encouraging one.

Runs with no sensors at all: a scripted circuit on load, and drag anywhere
to fly it by hand. That is enough to judge layout and arguably better than
real sensors, since it holds sustained bank angles nobody produces waving
a phone around. Real attitude needs a top-level HTTPS document, because
device orientation is gated by Permissions Policy and never fires in a
cross-origin iframe.

Packaged as a PWA with a service worker, so once installed to the home
screen it runs fullscreen, landscape-locked, screen awake, and offline. A
flight instrument that stops working when the cell signal drops is not an
instrument, and at 800 feet over a field the signal drops routinely.

Section 19's PWA row is sharpened rather than reversed. Web Bluetooth
reaches the node on Android and would genuinely work, so the transport
objection now applies to iOS alone, and the socket binding objection
scopes to the on-demand Wi-Fi AP since revision 2 moved the in-flight link
to BLE. What still decides it is duration: no background operation, a
gesture per connection, and the link drops when the page suspends. A
second row records that a PWA is adopted as a prototyping vehicle, which
is a different question from the shipping client.
The wrapper exists for one reason. Device orientation and geolocation are
only available to a secure context, so a WebView loading file:// would
silently kill exactly the sensors the app is for. It serves the bundled
assets over an https:// origin and intercepts those requests locally.
Nothing leaves the device and no network is used.

Built with the raw SDK tools rather than Gradle. One activity, no
dependencies, nothing to resolve, so the Android Gradle Plugin would add a
large download and a version matrix to a build that is otherwise six
commands. A real client would use Gradle; this is not a real client.

Two things worth recording for whoever builds this next:

- build-tools 34.0.0 does not work on JDK 21. Its d8 fails on every class
  with "Cannot invoke String.length() because <parameter1> is null" while
  writing the dex. build-tools 35 is fine. The script now selects 35 or
  newer and says so if it cannot find one.
- Do not pass file lists to d8 via @argfile. A trailing newline arrives as
  an empty argument and produces the same opaque NPE for a different
  reason.

The APK is deliberately not committed. .gitignore already excluded *.apk
and that decision stands, so the workflow is where the APK comes from:
every run uploads it as an artifact, and a mockup-v* tag attaches it to a
release. The workflow also checks the APK is signed, declares the right
package, and actually contains the web assets, since a wrapper that ships
without its payload would still build and install cleanly.

This is the repository's first CI.
@allenmcghan allenmcghan changed the title PRD revisions 3–5: copyleft relicense, BLE telemetry spec, and closing the open questions PRD revisions 3–5, copyleft relicense, BLE telemetry spec, and the PFD layout mockup Aug 9, 2026
@allenmcghan
allenmcghan marked this pull request as ready for review August 9, 2026 13:58
@allenmcghan
allenmcghan merged commit 14f7955 into main Aug 9, 2026
1 check passed
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