Fesk TX acoustic transmission library and protocol #139
+5,841
−0
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Tldr; A new transmission library using the piezo buzzer.
Background
So this has been a fun and frustrating journey 🤓😅
When I first discovered the chirpy library in this project I was... I don't know what else to call it but "in love". What an amazing concept and idea ❤️
After hacking a bit on my own, plus trying to transmit from my own faces I observed that it was very picky (at least with my hardware) and I would very seldom be able to transmit anything longer than simple tests.
So I set out on my first attempt. Creating another receiver for chirpy. Well it turns out this stuff is actually really hard, and very hard to get stable and "not picky".
So I set out for my second attempt (and later my 3rd, 4th and ... I lost count there at some point). Different variations of frequency gaps. Frequencies to line up with clock rate. Reduce number of frequencies. Until I stumbled on another discord user who attempted something similar (spipm) where they did sound gaps (silence). Wow, of course that must be it! So this is the protocol. It's also reduced to only 2 frequencies (BFSK) to make it as stable and predictable as possible.
Technical details
The library is a binary FSK using the following tones:
0: D7# (~2489 Hz)1: G7 (~3136 Hz)Protocol Format:
The library includes the protocol specification which can be read there. I also included the very simplest demo face I could think of, to illustrate the usage. I intentionally wanted the API to be much less boilerplate than chirpy, so it can easily be added to faces as people would want to.
The demo face is literally these changes to a blank template:
^^ the session stored in our state.
^^ the place where I want to transmit the message
Optionally, but also best practice I would say, in the return of the loop handler:
edit: Interestingly enough. For some reason, when I wanted to squash this branch for the PR, I got some files messed up. Among them the demo watch face. The good news is that this means this commit now is a great representation of the changes needed to add FESK tx to a standard face: 782c861
API
The API is basically you supply callbacks to the config. My thought is that the defaults should be opinionated, but you can override them
For example. The demo face included sets no other values than the message to send (and its length). This means it uses defaults, which is 3 seconds countdown (same as chirpy demo), with the display updating the progress and showing a bell when making sound, and the string "TX" when transmitting. And finally "Done" when done.
This is fine for a demo, and for some other use cases. But usually you might want to control the display yourself, and maybe you even want to update the display on the countdown ticks yourself? Or heck, screw the countdown? Up to you
Demo
I have what at least on my hardware (both from sim and watch) decodes very consistently both on my laptop and phone. This demo is here: https://fesk-rt.vercel.app/
Also here is a video I posted in the discord
PXL_20251021_152148967.1.mp4
Potential improvements
One could say it would be nice to expose what frequencies are used. I don't think so personally, as this would make decoders incompatible with each other. The timings however would probably be very possible to expose, and the decoder in the link would totally decode it if you opted for a slower transmission (in exchange for improved stability).
Another improvement would be test coverage for
fesk_session. However, this would mean mocking more of the watch library, so I simply... did not do that.A third improvement would be #136 which would actually run the tests for this library on this and other pull requests (at least that was my intention).
One area of obvious improvement is the character set. As can be seen here this is limited to lowercase a-z, digits and a couple punctuation, quotes and white space characters. The main purpose of this was to fit within 6 bit characters, and with 2 frequencies, you don't have unlimited space. After I was able to speed up the build, this might have been too careful, but if there is a project where one can appreciate constraints, it must be this one, right? 🤓 Also. Should be possible to encode with base26 if some use case appeared for characters outside the character set.
A note on AI usage
I was assisted with AI coding tools for planning, coding and reviewing this code. That is not to say this entire thing was vibe coded. My oh my, did I have to help those robots. But I would certainly never had been able to iterate this fast, that's for sure