diff --git a/firmware/patternflow/config.h b/firmware/patternflow/config.h index c396dce97..af335c10c 100644 --- a/firmware/patternflow/config.h +++ b/firmware/patternflow/config.h @@ -78,33 +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 -#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 +// +// 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 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 + #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 + #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 + #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 b5134678f..2ebb142d8 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) @@ -190,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 @@ -199,6 +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 +#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 cb4094307..f451f8ebf 100644 --- a/firmware/patternflow/patternflow.ino +++ b/firmware/patternflow/patternflow.ino @@ -270,7 +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; +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 29dfedcfc..d423228e1 100644 --- a/firmware/patternflow/src/core_bus.h +++ b/firmware/patternflow/src/core_bus.h @@ -23,13 +23,16 @@ #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 // 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 +// 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 { @@ -87,9 +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); - 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 84cd93e81..b0d61299a 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 @@ -73,7 +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 +// 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. @@ -86,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) { @@ -140,7 +145,7 @@ struct Button { bool pressed() { bool cur = digitalRead(pin); uint32_t now = millis(); - if (cur != lastState && (now - lastChangeMs) > 50) { + if (cur != lastState && (now - lastChangeMs) > PF_SHARED_BUTTON_DEBOUNCE_MS) { lastState = cur; lastChangeMs = now; if (cur == LOW) { @@ -166,7 +171,7 @@ struct Button { return c; } - 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) { 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())