From a808444b340974112c4e27dda07b2f6a9a31abb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 06:45:09 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=E2=80=94=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=203=EB=8B=A8=EA=B3=84=20PR=208=20=E2=80=94=20?= =?UTF-8?q?=ED=8E=8C=EC=9B=A8=EC=96=B4=EA=B0=80=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=EC=99=80=20=EA=B0=99=EC=9D=B4=20=EC=93=B0=EB=8A=94=20?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20=ED=95=9C=20=ED=8C=8C=EC=9D=BC=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9D=BD=EB=8A=94=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019raaKug4zzwsoT9eFKgJ78 From 926bde0b67a40e232bd34b69315da8864a242852 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 06:54:51 +0000 Subject: [PATCH 2/3] firmware: assert each shared literal equals its core_shared.h value The first of two commits: CI builds all five compositions with these asserts, and the next commit swaps the literals for the macros. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019raaKug4zzwsoT9eFKgJ78 --- firmware/patternflow/config.h | 14 ++++++++++++++ firmware/patternflow/net_config.h | 5 +++++ firmware/patternflow/patternflow.ino | 1 + firmware/patternflow/src/core_bus.h | 3 +++ firmware/patternflow/src/core_encoders.h | 4 ++++ firmware/toolchain/check_midi.py | 3 ++- 6 files changed, 29 insertions(+), 1 deletion(-) diff --git a/firmware/patternflow/config.h b/firmware/patternflow/config.h index c396dce97..06bbe40eb 100644 --- a/firmware/patternflow/config.h +++ b/firmware/patternflow/config.h @@ -78,9 +78,14 @@ constexpr float EULER = 2.71828182845904523536f; // pattern with PFCanvas::setFrame(). See README.md. // Running a non-stock panel? Please report how it went, working or not: // https://github.com/engmung/Patternflow/issues/224 +#include "src/core_shared.h" #define PANEL_GEOM_128x64_SINGLE 1 // one 128×64 module (stock) #define PANEL_GEOM_64x64_SINGLE 2 // one 64×64 module #define PANEL_GEOM_64x64_CHAIN2 3 // two 64×64 modules daisy-chained → 128×64 +static_assert(PANEL_GEOM_128x64_SINGLE == PF_SHARED_PANEL_GEOM_128x64_SINGLE, "same value as shared/constants.json"); +static_assert(PANEL_GEOM_64x64_SINGLE == PF_SHARED_PANEL_GEOM_64x64_SINGLE, "same value as shared/constants.json"); +static_assert(PANEL_GEOM_64x64_CHAIN2 == PF_SHARED_PANEL_GEOM_64x64_CHAIN2, "same value as shared/constants.json"); +static_assert(PANEL_GEOM_128x64_SINGLE == PF_SHARED_PANEL_GEOM_DEFAULT, "same value as shared/constants.json"); // #define PANEL_GEOMETRY PANEL_GEOM_64x64_CHAIN2 #ifndef PANEL_GEOMETRY @@ -93,18 +98,27 @@ constexpr float EULER = 2.71828182845904523536f; #define PANEL_CHAIN 1 #define PANEL_RES_W 64 #define PANEL_RES_H 64 + static_assert(PANEL_PHYS_W == PF_SHARED_PANEL_GEOM_64x64_SINGLE_PHYS_W && PANEL_PHYS_H == PF_SHARED_PANEL_GEOM_64x64_SINGLE_PHYS_H && + PANEL_CHAIN == PF_SHARED_PANEL_GEOM_64x64_SINGLE_CHAIN && PANEL_RES_W == PF_SHARED_PANEL_GEOM_64x64_SINGLE_RES_W && + PANEL_RES_H == PF_SHARED_PANEL_GEOM_64x64_SINGLE_RES_H, "same value as shared/constants.json"); #elif PANEL_GEOMETRY == PANEL_GEOM_64x64_CHAIN2 #define PANEL_PHYS_W 64 #define PANEL_PHYS_H 64 #define PANEL_CHAIN 2 #define PANEL_RES_W 128 #define PANEL_RES_H 64 + static_assert(PANEL_PHYS_W == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_PHYS_W && PANEL_PHYS_H == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_PHYS_H && + PANEL_CHAIN == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_CHAIN && PANEL_RES_W == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_RES_W && + PANEL_RES_H == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_RES_H, "same value as shared/constants.json"); #else #define PANEL_PHYS_W 128 #define PANEL_PHYS_H 64 #define PANEL_CHAIN 1 #define PANEL_RES_W 128 #define PANEL_RES_H 64 + static_assert(PANEL_PHYS_W == PF_SHARED_PANEL_GEOM_128x64_SINGLE_PHYS_W && PANEL_PHYS_H == PF_SHARED_PANEL_GEOM_128x64_SINGLE_PHYS_H && + PANEL_CHAIN == PF_SHARED_PANEL_GEOM_128x64_SINGLE_CHAIN && PANEL_RES_W == PF_SHARED_PANEL_GEOM_128x64_SINGLE_RES_W && + PANEL_RES_H == PF_SHARED_PANEL_GEOM_128x64_SINGLE_RES_H, "same value as shared/constants.json"); #endif #if PANEL_RES_W != (PANEL_PHYS_W * PANEL_CHAIN) diff --git a/firmware/patternflow/net_config.h b/firmware/patternflow/net_config.h index b5134678f..0e0338f7c 100644 --- a/firmware/patternflow/net_config.h +++ b/firmware/patternflow/net_config.h @@ -32,6 +32,8 @@ #include "patternflow_secrets.h" #endif +#include "src/core_shared.h" + // ── Wi-Fi ──────────────────────────────────────────────────────────── // Whether the panel has a radio at all. 0 makes a USB-only instrument: // everything network-side (console, OTA, every feature that dials out) @@ -200,5 +202,8 @@ #define PF_LANE_MOTION_SCALE PF_AUDIO_VIRTUAL_KNOB_SCALE #else #define PF_LANE_MOTION_SCALE 48.0f +static_assert(PF_LANE_MOTION_SCALE == + (float)(PF_SHARED_DETENTS_PER_TURN * PF_SHARED_TURNS_PER_FULL_RANGE), + "same value as shared/constants.json"); #endif #endif diff --git a/firmware/patternflow/patternflow.ino b/firmware/patternflow/patternflow.ino index cb4094307..66b730a67 100644 --- a/firmware/patternflow/patternflow.ino +++ b/firmware/patternflow/patternflow.ino @@ -271,6 +271,7 @@ bool updateDirty = false; volatile int uploadPctShown = -1; const uint32_t MODE_HOLD_MS = 1000; +static_assert(MODE_HOLD_MS == PF_SHARED_LONG_PRESS_MS, "same value as shared/constants.json"); const uint32_t BRIGHTNESS_IDLE_MS = 5000; // How long the value has to be still before it is written to NVS - the // console's slider sends a step for every pixel of a drag. diff --git a/firmware/patternflow/src/core_bus.h b/firmware/patternflow/src/core_bus.h index 29dfedcfc..ce1bce891 100644 --- a/firmware/patternflow/src/core_bus.h +++ b/firmware/patternflow/src/core_bus.h @@ -23,6 +23,7 @@ #include #include "core_encoders.h" // InputFrame +#include "core_shared.h" // The wire scale of an absolute channel: 0..PF_BUS_MAX, integer. Everything // that writes the bus — MQTT param/1..4, POST /api/params, a .pfs cue, a MIDI @@ -30,6 +31,7 @@ // (BUS_WIRE_MAX in web/src/lib/pattern/controls.ts). abi/pf_params.h clamps // to the same literal and cannot be changed to this name: the ABI is frozen. #define PF_BUS_MAX 1000 +static_assert(PF_BUS_MAX == PF_SHARED_BUS_MAX, "same value as shared/constants.json"); namespace PatternflowBus { @@ -87,6 +89,7 @@ inline void applyRemoteParam(int index, long value) { if (diff != 0) { const int32_t acc = __atomic_add_fetch(&clickResidual[index], (int32_t)diff, __ATOMIC_RELAXED); + static_assert(10 == PF_SHARED_BUS_UNITS_PER_CLICK, "same value as shared/constants.json"); const int32_t d = acc / 10; // truncates toward zero, both signs if (d != 0) { __atomic_fetch_sub(&clickResidual[index], d * 10, __ATOMIC_RELAXED); diff --git a/firmware/patternflow/src/core_encoders.h b/firmware/patternflow/src/core_encoders.h index 84cd93e81..c057a3119 100644 --- a/firmware/patternflow/src/core_encoders.h +++ b/firmware/patternflow/src/core_encoders.h @@ -2,6 +2,7 @@ #include #include #include "config.h" +#include "core_shared.h" volatile long encPos[4] = {0, 0, 0, 0}; // sub-steps; 4 per detent volatile uint8_t encState[4] = {0, 0, 0, 0}; // last ACCEPTED (A<<1)|B @@ -74,6 +75,7 @@ void IRAM_ATTR isr4() { handleEncoder(3, ENC4_A, ENC4_B); } inline bool knobInvert[4] = {INVERT_ENCODER != 0, INVERT_ENCODER != 0, INVERT_ENCODER != 0, INVERT_ENCODER != 0}; inline int knobSubSteps[4] = {4, 4, 4, 4}; // edges per click: 4, 2 or 1 +static_assert(4 == PF_SHARED_EDGES_PER_DETENT, "same value as shared/constants.json"); // A settings change must not move a knob. The click count carries on from // where it was, counting the edges since the change in the new direction // and at the new size, so the frame's delta stays zero across a save. @@ -140,6 +142,7 @@ struct Button { bool pressed() { bool cur = digitalRead(pin); uint32_t now = millis(); + static_assert(50 == PF_SHARED_BUTTON_DEBOUNCE_MS, "same value as shared/constants.json"); if (cur != lastState && (now - lastChangeMs) > 50) { lastState = cur; lastChangeMs = now; @@ -166,6 +169,7 @@ struct Button { return c; } + static_assert(1000 == PF_SHARED_LONG_PRESS_MS, "same value as shared/constants.json"); bool longPressed(uint32_t threshold = 1000) { if (!isDown()) return false; uint32_t now = millis(); diff --git a/firmware/toolchain/check_midi.py b/firmware/toolchain/check_midi.py index d654331ee..63185e122 100644 --- a/firmware/toolchain/check_midi.py +++ b/firmware/toolchain/check_midi.py @@ -13,7 +13,8 @@ def main(): compiler, env = compiler_environment() with tempfile.TemporaryDirectory(prefix='patternflow-midi-') as directory: work = Path(directory) - for name in ('features/midi/core_midi.h', 'features/midi/midi_queue.h', 'src/core_bus.h'): + for name in ('features/midi/core_midi.h', 'features/midi/midi_queue.h', 'src/core_bus.h', + 'src/core_shared.h'): dest = work / name dest.parent.mkdir(parents=True, exist_ok=True) dest.write_bytes((ROOT / 'firmware/patternflow' / name).read_bytes()) From b5556da519277f900360b0113eafc75b394ef495 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 06:58:16 +0000 Subject: [PATCH 3/3] firmware: read the shared literals from core_shared.h The second of two commits: each literal the previous commit asserted is now the macro it was checked against, and the asserts go. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019raaKug4zzwsoT9eFKgJ78 --- firmware/patternflow/config.h | 54 ++++++++++-------------- firmware/patternflow/net_config.h | 8 ++-- firmware/patternflow/patternflow.ino | 3 +- firmware/patternflow/src/core_bus.h | 10 ++--- firmware/patternflow/src/core_encoders.h | 15 ++++--- 5 files changed, 39 insertions(+), 51 deletions(-) diff --git a/firmware/patternflow/config.h b/firmware/patternflow/config.h index 06bbe40eb..af335c10c 100644 --- a/firmware/patternflow/config.h +++ b/firmware/patternflow/config.h @@ -78,47 +78,37 @@ constexpr float EULER = 2.71828182845904523536f; // pattern with PFCanvas::setFrame(). See README.md. // Running a non-stock panel? Please report how it went, working or not: // https://github.com/engmung/Patternflow/issues/224 +// +// The ids and the sizes come from shared/constants.json, which the site +// reads too. #include "src/core_shared.h" -#define PANEL_GEOM_128x64_SINGLE 1 // one 128×64 module (stock) -#define PANEL_GEOM_64x64_SINGLE 2 // one 64×64 module -#define PANEL_GEOM_64x64_CHAIN2 3 // two 64×64 modules daisy-chained → 128×64 -static_assert(PANEL_GEOM_128x64_SINGLE == PF_SHARED_PANEL_GEOM_128x64_SINGLE, "same value as shared/constants.json"); -static_assert(PANEL_GEOM_64x64_SINGLE == PF_SHARED_PANEL_GEOM_64x64_SINGLE, "same value as shared/constants.json"); -static_assert(PANEL_GEOM_64x64_CHAIN2 == PF_SHARED_PANEL_GEOM_64x64_CHAIN2, "same value as shared/constants.json"); -static_assert(PANEL_GEOM_128x64_SINGLE == PF_SHARED_PANEL_GEOM_DEFAULT, "same value as shared/constants.json"); +#define PANEL_GEOM_128x64_SINGLE PF_SHARED_PANEL_GEOM_128x64_SINGLE // one 128×64 module (stock) +#define PANEL_GEOM_64x64_SINGLE PF_SHARED_PANEL_GEOM_64x64_SINGLE // one 64×64 module +#define PANEL_GEOM_64x64_CHAIN2 PF_SHARED_PANEL_GEOM_64x64_CHAIN2 // two 64×64 modules daisy-chained → 128×64 // #define PANEL_GEOMETRY PANEL_GEOM_64x64_CHAIN2 #ifndef PANEL_GEOMETRY -#define PANEL_GEOMETRY PANEL_GEOM_128x64_SINGLE +#define PANEL_GEOMETRY PF_SHARED_PANEL_GEOM_DEFAULT #endif #if PANEL_GEOMETRY == PANEL_GEOM_64x64_SINGLE - #define PANEL_PHYS_W 64 - #define PANEL_PHYS_H 64 - #define PANEL_CHAIN 1 - #define PANEL_RES_W 64 - #define PANEL_RES_H 64 - static_assert(PANEL_PHYS_W == PF_SHARED_PANEL_GEOM_64x64_SINGLE_PHYS_W && PANEL_PHYS_H == PF_SHARED_PANEL_GEOM_64x64_SINGLE_PHYS_H && - PANEL_CHAIN == PF_SHARED_PANEL_GEOM_64x64_SINGLE_CHAIN && PANEL_RES_W == PF_SHARED_PANEL_GEOM_64x64_SINGLE_RES_W && - PANEL_RES_H == PF_SHARED_PANEL_GEOM_64x64_SINGLE_RES_H, "same value as shared/constants.json"); + #define PANEL_PHYS_W PF_SHARED_PANEL_GEOM_64x64_SINGLE_PHYS_W + #define PANEL_PHYS_H PF_SHARED_PANEL_GEOM_64x64_SINGLE_PHYS_H + #define PANEL_CHAIN PF_SHARED_PANEL_GEOM_64x64_SINGLE_CHAIN + #define PANEL_RES_W PF_SHARED_PANEL_GEOM_64x64_SINGLE_RES_W + #define PANEL_RES_H PF_SHARED_PANEL_GEOM_64x64_SINGLE_RES_H #elif PANEL_GEOMETRY == PANEL_GEOM_64x64_CHAIN2 - #define PANEL_PHYS_W 64 - #define PANEL_PHYS_H 64 - #define PANEL_CHAIN 2 - #define PANEL_RES_W 128 - #define PANEL_RES_H 64 - static_assert(PANEL_PHYS_W == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_PHYS_W && PANEL_PHYS_H == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_PHYS_H && - PANEL_CHAIN == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_CHAIN && PANEL_RES_W == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_RES_W && - PANEL_RES_H == PF_SHARED_PANEL_GEOM_64x64_CHAIN2_RES_H, "same value as shared/constants.json"); + #define PANEL_PHYS_W PF_SHARED_PANEL_GEOM_64x64_CHAIN2_PHYS_W + #define PANEL_PHYS_H PF_SHARED_PANEL_GEOM_64x64_CHAIN2_PHYS_H + #define PANEL_CHAIN PF_SHARED_PANEL_GEOM_64x64_CHAIN2_CHAIN + #define PANEL_RES_W PF_SHARED_PANEL_GEOM_64x64_CHAIN2_RES_W + #define PANEL_RES_H PF_SHARED_PANEL_GEOM_64x64_CHAIN2_RES_H #else - #define PANEL_PHYS_W 128 - #define PANEL_PHYS_H 64 - #define PANEL_CHAIN 1 - #define PANEL_RES_W 128 - #define PANEL_RES_H 64 - static_assert(PANEL_PHYS_W == PF_SHARED_PANEL_GEOM_128x64_SINGLE_PHYS_W && PANEL_PHYS_H == PF_SHARED_PANEL_GEOM_128x64_SINGLE_PHYS_H && - PANEL_CHAIN == PF_SHARED_PANEL_GEOM_128x64_SINGLE_CHAIN && PANEL_RES_W == PF_SHARED_PANEL_GEOM_128x64_SINGLE_RES_W && - PANEL_RES_H == PF_SHARED_PANEL_GEOM_128x64_SINGLE_RES_H, "same value as shared/constants.json"); + #define PANEL_PHYS_W PF_SHARED_PANEL_GEOM_128x64_SINGLE_PHYS_W + #define PANEL_PHYS_H PF_SHARED_PANEL_GEOM_128x64_SINGLE_PHYS_H + #define PANEL_CHAIN PF_SHARED_PANEL_GEOM_128x64_SINGLE_CHAIN + #define PANEL_RES_W PF_SHARED_PANEL_GEOM_128x64_SINGLE_RES_W + #define PANEL_RES_H PF_SHARED_PANEL_GEOM_128x64_SINGLE_RES_H #endif #if PANEL_RES_W != (PANEL_PHYS_W * PANEL_CHAIN) diff --git a/firmware/patternflow/net_config.h b/firmware/patternflow/net_config.h index 0e0338f7c..2ebb142d8 100644 --- a/firmware/patternflow/net_config.h +++ b/firmware/patternflow/net_config.h @@ -192,7 +192,8 @@ // it into virtual knob deltas (applyLaneMotion), so EVERY encoder-driven // pattern reacts with no per-pattern code. This is how many knob clicks a // full 0..1 swing maps to. Higher = stronger response. No per-frame clamp, -// so the value tracks without lag. +// so the value tracks without lag. The default is a parameter's whole range, +// two turns of 24 detents (src/core_shared.h). // // The old name is honoured below because a firmware built elsewhere may // already set it in its overrides.h, and a rename that silently ignores @@ -201,9 +202,6 @@ #ifdef PF_AUDIO_VIRTUAL_KNOB_SCALE #define PF_LANE_MOTION_SCALE PF_AUDIO_VIRTUAL_KNOB_SCALE #else -#define PF_LANE_MOTION_SCALE 48.0f -static_assert(PF_LANE_MOTION_SCALE == - (float)(PF_SHARED_DETENTS_PER_TURN * PF_SHARED_TURNS_PER_FULL_RANGE), - "same value as shared/constants.json"); +#define PF_LANE_MOTION_SCALE ((float)(PF_SHARED_DETENTS_PER_TURN * PF_SHARED_TURNS_PER_FULL_RANGE)) #endif #endif diff --git a/firmware/patternflow/patternflow.ino b/firmware/patternflow/patternflow.ino index 66b730a67..f451f8ebf 100644 --- a/firmware/patternflow/patternflow.ino +++ b/firmware/patternflow/patternflow.ino @@ -270,8 +270,7 @@ bool updateDirty = false; // loop() draws it. -1 when nothing is being received. volatile int uploadPctShown = -1; -const uint32_t MODE_HOLD_MS = 1000; -static_assert(MODE_HOLD_MS == PF_SHARED_LONG_PRESS_MS, "same value as shared/constants.json"); +const uint32_t MODE_HOLD_MS = PF_SHARED_LONG_PRESS_MS; const uint32_t BRIGHTNESS_IDLE_MS = 5000; // How long the value has to be still before it is written to NVS - the // console's slider sends a step for every pixel of a drag. diff --git a/firmware/patternflow/src/core_bus.h b/firmware/patternflow/src/core_bus.h index ce1bce891..d423228e1 100644 --- a/firmware/patternflow/src/core_bus.h +++ b/firmware/patternflow/src/core_bus.h @@ -30,8 +30,9 @@ // CC — speaks this scale, and the site's Director bakes to it // (BUS_WIRE_MAX in web/src/lib/pattern/controls.ts). abi/pf_params.h clamps // to the same literal and cannot be changed to this name: the ABI is frozen. -#define PF_BUS_MAX 1000 -static_assert(PF_BUS_MAX == PF_SHARED_BUS_MAX, "same value as shared/constants.json"); +// The number is BUS_MAX in shared/constants.json, and check:shared holds the +// ABI's literal to it. +#define PF_BUS_MAX PF_SHARED_BUS_MAX namespace PatternflowBus { @@ -89,10 +90,9 @@ inline void applyRemoteParam(int index, long value) { if (diff != 0) { const int32_t acc = __atomic_add_fetch(&clickResidual[index], (int32_t)diff, __ATOMIC_RELAXED); - static_assert(10 == PF_SHARED_BUS_UNITS_PER_CLICK, "same value as shared/constants.json"); - const int32_t d = acc / 10; // truncates toward zero, both signs + const int32_t d = acc / PF_SHARED_BUS_UNITS_PER_CLICK; // truncates toward zero, both signs if (d != 0) { - __atomic_fetch_sub(&clickResidual[index], d * 10, __ATOMIC_RELAXED); + __atomic_fetch_sub(&clickResidual[index], d * PF_SHARED_BUS_UNITS_PER_CLICK, __ATOMIC_RELAXED); __atomic_fetch_add(&pendingDelta[index], d, __ATOMIC_RELAXED); } } diff --git a/firmware/patternflow/src/core_encoders.h b/firmware/patternflow/src/core_encoders.h index c057a3119..b0d61299a 100644 --- a/firmware/patternflow/src/core_encoders.h +++ b/firmware/patternflow/src/core_encoders.h @@ -74,8 +74,9 @@ void IRAM_ATTR isr4() { handleEncoder(3, ENC4_A, ENC4_B); } // (namespace "pf_knobs"), set from the /knobs page (core_knobs_http.h). inline bool knobInvert[4] = {INVERT_ENCODER != 0, INVERT_ENCODER != 0, INVERT_ENCODER != 0, INVERT_ENCODER != 0}; -inline int knobSubSteps[4] = {4, 4, 4, 4}; // edges per click: 4, 2 or 1 -static_assert(4 == PF_SHARED_EDGES_PER_DETENT, "same value as shared/constants.json"); +// Edges per click: 4, 2 or 1. +inline int knobSubSteps[4] = {PF_SHARED_EDGES_PER_DETENT, PF_SHARED_EDGES_PER_DETENT, + PF_SHARED_EDGES_PER_DETENT, PF_SHARED_EDGES_PER_DETENT}; // A settings change must not move a knob. The click count carries on from // where it was, counting the edges since the change in the new direction // and at the new size, so the frame's delta stays zero across a save. @@ -88,7 +89,9 @@ inline long getClicks(int idx) { return knobClickBase[idx] + rel / knobSubSteps[idx]; } -inline int clampSubSteps(int v) { return (v == 1 || v == 2 || v == 4) ? v : 4; } +inline int clampSubSteps(int v) { + return (v == 1 || v == 2 || v == 4) ? v : PF_SHARED_EDGES_PER_DETENT; +} // Call on the frame task (the frame reads the same counters). inline void setKnobSettings(int idx, bool invert, int subSteps) { @@ -142,8 +145,7 @@ struct Button { bool pressed() { bool cur = digitalRead(pin); uint32_t now = millis(); - static_assert(50 == PF_SHARED_BUTTON_DEBOUNCE_MS, "same value as shared/constants.json"); - if (cur != lastState && (now - lastChangeMs) > 50) { + if (cur != lastState && (now - lastChangeMs) > PF_SHARED_BUTTON_DEBOUNCE_MS) { lastState = cur; lastChangeMs = now; if (cur == LOW) { @@ -169,8 +171,7 @@ struct Button { return c; } - static_assert(1000 == PF_SHARED_LONG_PRESS_MS, "same value as shared/constants.json"); - bool longPressed(uint32_t threshold = 1000) { + bool longPressed(uint32_t threshold = PF_SHARED_LONG_PRESS_MS) { if (!isDown()) return false; uint32_t now = millis(); if (!longPressFired && (now - pressStartMs) > threshold) {