Skip to content

Conversation

@eiriksm
Copy link
Contributor

@eiriksm eiriksm commented Nov 3, 2025

Tldr; A new transmission library using the piezo buzzer with support for BFSK (super stable) and 4FSK (faster, but still very stable).

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.

update: I did some experimenting, and it's still very stable at 4 frequencies (4FSK) so it now defaults to that with the option to choose BFSK.

Technical details

The 4 frequency protocol uses the following tones:

  • 00: D7 (~2349 Hz)
  • 01: E7 (~2637 Hz)
  • 10: F7# (~2960 Hz)
  • 11: G7# (~3322 Hz)

The binary FSK using the following tones:

  • Binary 0: D7# (~2489 Hz)
  • Binary 1: G7 (~3136 Hz)

Timing: 1 tick per bit tone, 2 ticks silence between bits

Protocol Format:

[START(6bit)] [PAYLOAD(N×6bit)] [CRC8(8bit)] [END(6bit)]

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:

typedef struct {
   fesk_session_t session;
} fesk_demo_lite_state_t;

^^ the session stored in our state.

       case EVENT_ALARM_BUTTON_UP:
           {
               fesk_session_config_t config = fesk_session_config_defaults();
               config.static_message = "test";
               fesk_session_init(&state->session, &config);
               fesk_session_start(&state->session);
           }
           break;

^^ the place where I want to transmit the message

Optionally, but also best practice I would say, in the return of the loop handler:

   return fesk_session_is_idle(&state->session);

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.eiriksm.dev/

Also here is a video I posted in the discord (this is a video of BFSK, and a slight earlier version of the receiver UI)

PXL_20251021_152148967.1.mp4

Update: And here is a video of the 4FSK being decoded in the hybrid mode decoder as well

PXL_20260106_180944747.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

@eiriksm eiriksm changed the title Fesk TX Fesk TX acoustic transmission library and protocol Nov 4, 2025
@eiriksm eiriksm marked this pull request as draft November 29, 2025 08:06
@eiriksm
Copy link
Contributor Author

eiriksm commented Nov 29, 2025

I'm converting this to draft as I am seeing some very promising results with using 4 tones, and would hate for such a change to be merged separately 😅

@eiriksm
Copy link
Contributor Author

eiriksm commented Jan 3, 2026

Marking back to "ready" after making an option for 4 tones, and making it the default.

I also refactored it to use the "new" buzzer API from the merge of the counter32 stuff

@eiriksm eiriksm marked this pull request as ready for review January 3, 2026 18:33
@eiriksm eiriksm changed the title Fesk TX acoustic transmission library and protocol FESK TX acoustic transmission library and protocol Jan 6, 2026
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.

1 participant