Skip to content

Add BengleShotSample (0xA013), the Bengle high-resolution shot sample - #1

Open
ChampionDesigns wants to merge 1 commit into
ben/detect-protocolfrom
ben/bengle-shot-sample
Open

ChampionDesigns wants to merge 1 commit into
ben/detect-protocolfrom
ben/bengle-shot-sample

Conversation

@ChampionDesigns

Copy link
Copy Markdown
Owner

Filed inside the fork so the diff shows only this PR's own changes.
It will be re-filed against decentespresso/de1app once its base has merged there.

merges. 242 insertions, 8 files.

Summary

A Bengle exposes an extra BLE characteristic carrying a superset of ShotSample at higher
precision, plus a milk probe and integrated-scale fields the stock packet has no room for.
28 bytes, big-endian, matching the firmware's T_BengleShotSample and
decodeBengleShotSample in decentespresso/decaid.

ShotSample (0xA00D) keeps its stock v1 layout on every machine, a Bengle included, so a
stock build talking to a Bengle still works and this app talking to a DE1 still works, with
no negotiation on the shared packet.

Wire format

field type scale
SampleTime U16 half-cycles
GroupPressure, SetGroupPressure U16 ×0.01 bar
GroupFlow, SetGroupFlow U16 ×0.01 mL/s
GFlow U16 ×0.01 g/s, gravimetric
MixTemp, HeadTemp, SetMixTemp, SetHeadTemp U16 ×0.01 °C
Weight S16 ×0.0625 g, signed
FrameNumber U8
SteamTemp, MilkTemp U16 ×0.01 °C, MilkTemp 0 = no probe
Flags U8 bit 0 = firmware has tared

Weight is signed on purpose: it is net of tare, so an unloaded platform after a tare
reads negative.

A note on the spec table. The signed column must be left empty, not set to signed.
fields::2form only strips the s modifier for type I, so {signed} on a Short
emits Ss — two binary specifiers — which shifts FrameNumber, SteamTemp, MilkTemp
and Flags off their bytes. Tcl's S is already signed; Su is the unsigned one. This
was caught by running fields::2form on the real spec, not by reading it.

Changes

machine.tcl — cuuid_13, the command name, and defaults for steam_stop_mode and
target_milk_temp.

de1_de1.tcl — bengleshotsample_parse, and from_shotvalue split into
from_shotvalue / from_bengleshotvalue / a shared _apply_shotvalue. The stale-frame
guard added by decentespresso#343 moves into the shared body, so it protects both paths.

bluetooth.tcl — dispatch for 0xA013. The 0xA00D dispatch is gated on
use_ble_v2, because a Bengle streams both and charting both would double-sample with
broken intersample timing. Receiving 0xA013 pins the protocol to v2 immediately, closing
the window before the model MMR read returns.

de1_comms.tcl — the enable, gated on the characteristic being present in
::cinstance. That gate is load-bearing: on a DE1 the enable would throw on the unset
instance, and the vital-retry path re-runs a failed vital command every 500 ms without
advancing the FIFO
, stalling the whole BLE queue. Also set_target_milk_temp, gated and
clamped to 0–85 °C to match BengleSteamMmr.targetMilkTemp in decaid.

gui.tcl — sample deltas are normalised to their 5 Hz-equivalent magnitude using the
measured intersample time. The chart math was implicitly tuned for 5 Hz; without this the
delta trace and the diff_flow_rate_text readout scale with the notify rate. Falls back to
the raw delta when the event carries no intersample time.

vars.tcl, utils.tcl, shot.tcl — the milk probe: ::de1(milk_temperature), a
steam_milk_temperature chart vector, and milktemp / milktemp_text.

Known issues, not fixed here

Raised by a review of the series, recorded rather than fixed. Neither has been reproduced
on hardware.

  • steam_milk_temperature is not truncated in reset_gui_starting_steam, which zeroes
    its five sibling vectors. It may grow unbounded across steam sessions and desynchronise
    from steam_elapsed.
  • The milk auto-stop write is currently unreachable. ::settings(steam_stop_mode) has
    no UI in this series, so it never equals "temp". Both settings now have defaults and
    the read is guarded, so nothing throws, but the feature needs a control before it does
    anything.

Impact on a DE1

The enable returns early because 0xA013 is not in ::cinstance. Nothing subscribes or
dispatches, and 0xA00D charts as before. The gui.tcl delta change is the one path a DE1
does reach — see the test plan.

Test plan

  • Pull a shot on a DE1. Compare the flow-delta trace and flow-rate readout against a
    build without this PR.
  • Confirm the DE1 BLE queue is not stalled at connect.
  • TODO (Ben): pull a shot on a Bengle. Confirm one chart trace, not two.
  • TODO (Ben): confirm a negative weight after a tare charts as negative, and that
    FrameNumber, SteamTemp and Flags decode correctly.
  • TODO (Ben): steam with the milk probe attached, then without.

Testing

Manual verification, plus a tclsh run of fields::2form over the sample spec to confirm
the byte layout decodes correctly. There is no automated test coverage for these Tcl paths.

A Bengle exposes an extra BLE characteristic carrying a superset of
ShotSample at higher precision, plus a milk probe and integrated-scale
fields the stock packet has no room for. 28 bytes, big-endian, matching
the firmware's T_BengleShotSample and decodeBengleShotSample in
decentespresso/decaid.

ShotSample (0xA00D) keeps its stock v1 layout on every machine, a Bengle
included, so a stock build talking to a Bengle still works and this app
talking to a DE1 still works, with no negotiation on the shared packet.

Weight is SIGNED (S16P4). It is net of tare, so an unloaded platform after
a tare reads negative; clamping at zero would hide a real reading.

from_shotvalue splits into from_shotvalue / from_bengleshotvalue and a
shared _apply_shotvalue. The stale-frame guard added by decentespresso#343 moves into
the shared body, so it now protects the 0xA013 path too. The
superset-only fields are picked up with [info exists].

On a Bengle the 0xA00D dispatch is gated off: it still streams, but
charting both would double-sample with broken intersample timing.
Receiving 0xA013 pins the protocol to v2 immediately, closing the window
between connect and the model MMR read.

The enable is gated on the characteristic being present in ::cinstance.
That gate is load-bearing: on a DE1 the enable would throw on the unset
instance, and the vital-retry path re-runs a failed vital command every
500 ms WITHOUT advancing the FIFO, stalling the whole BLE queue.

gui.tcl normalises sample-to-sample deltas to their 5 Hz-equivalent
magnitude using the measured intersample time. The chart math was
implicitly tuned for 5 Hz; without this the delta trace and the
diff_flow_rate_text readout scale with the notify rate. Falls back to the
raw delta when the event carries no intersample time.

The milk probe lands here too: ::de1(milk_temperature), a
steam_milk_temperature chart vector, and milktemp / milktemp_text.
0 means no probe. set_target_milk_temp is gated and clamped to 0-85 C,
matching BengleSteamMmr.targetMilkTemp in decaid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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