Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
// ═══════════════════════════════════════════════════════════
// PatternFlow - Network configuration (core)
//
// Everything the DEVICE needs to be on a network: the shared Wi-Fi
// connection, Improv provisioning, its own name and version, OTA, the
// browser self-update page, and the scale of the absolute bus.
//
// Nothing here belongs to a feature. Each feature's tunables — OSC ports,
// the audio WebSocket port, MQTT broker settings, weather polling — live in
// features/<name>/<name>_config.h, next to the code that reads them, and are
// only compiled when a composition carries that feature. Until 2026-09 they
// sat in this file, which made a core header 40 % feature settings and gave
// every flag two places to be defaulted.
//
// Per-device secrets and toggles live in patternflow_secrets.h (gitignored).
// That file is included FIRST below, so anything it #defines wins; the
// #ifndef blocks here — and in every feature's config header — only fill in
// defaults for whatever it left unset. No #undef gymnastics required: leave a
// line out of the secrets file to accept the default.
//
// Copy patternflow_secrets.example.h to patternflow_secrets.h to start.
// License: MIT
// ═══════════════════════════════════════════════════════════
#pragma once
// Per-device overrides first. Without this file every default below applies
// (Wi-Fi placeholders, OTA on).
//
// A feature's settings TUNE it; they never decide whether it is in the build.
// That is the composition's job — see features/features.h and docs/EDITIONS.md.
#if __has_include("patternflow_secrets.h")
#include "patternflow_secrets.h"
#endif
// ── 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)
// compiles out behind PF_WIFI_NEEDED in core_wifi.h.
#ifndef PF_WIFI_ENABLED
#define PF_WIFI_ENABLED 1
#endif
// Credentials normally come from patternflow_secrets.h. The placeholders
// here only keep a secret-less checkout compiling.
#ifndef PF_WIFI_SSID
#define PF_WIFI_SSID "YOUR_WIFI_SSID"
#endif
#ifndef PF_WIFI_PASS
#define PF_WIFI_PASS "YOUR_WIFI_PASSWORD"
#endif
// Maximum Wi-Fi transmit power, applied in core_wifi.h right after the join.
// The ESP32 default, 19.5 dBm, is the maximum the radio does.
//
// History: from 2026-08 to 2026-09-22 this was WIFI_POWER_13dBm, a cut taken
// from a radio test report against the EU 20 dBm EIRP limit. On 2026-09-22
// the hotspot bench showed what that cost: a phone right next to the panel
// received the panel's data frames so badly that a 10 KB console page took
// 4-9 s over the panel's own AP, and 0.5-1 s at full power (the uplink, the
// phone's own 20 dBm, was fine either way; a router's antenna and receiver
// had hidden the asymmetry on the home network). The owner retired the cap:
// full performance, conformance to be handled elsewhere. Valid values
// (WiFiGeneric.h): WIFI_POWER_19_5dBm, _19dBm, _18_5dBm, _17dBm, _15dBm,
// _13dBm, _11dBm, _8_5dBm, _7dBm, _5dBm, _2dBm.
#ifndef PF_WIFI_TX_POWER
#define PF_WIFI_TX_POWER WIFI_POWER_19_5dBm
#endif
// Wi-Fi is non-blocking (see core_wifi.h): boot never waits for the join.
// While disconnected, core_wifi.h re-issues WiFi.begin() at this interval
// until it links up.
#ifndef PF_WIFI_RETRY_INTERVAL_MS
#define PF_WIFI_RETRY_INTERVAL_MS 5000
#endif
// ── Improv-Serial Wi-Fi provisioning ─────────────────────────
// Lets the browser flasher (ESP Web Tools, behind the website's "Flash"
// button) set Wi-Fi over USB serial right after flashing, instead of baking
// credentials into the binary. The SSID/password are stored in NVS and used
// in preference to the placeholders above on the next boot. See
// src/core_improv.h. On by default; only compiled in when Wi-Fi is actually
// used (i.e. at least one of OTA/OSC/audio is enabled).
// ── Hotspot ───────────────────────────────────────────────────────────
// The panel as a network of its own (src/core_hotspot.h). `auto` raises
// it once the station has had no link for PF_HOTSPOT_AUTO_AFTER_MS and
// drops it when the station joins and nobody is on it; the mode and the
// password are settings on /wifi, these are their defaults. The SSID is
// the board alias (patternflow-a1b2). The password is the same on every
// panel by default, like FlowLocal's, so a person can join from memory;
// anyone who wants it private changes it on the console.
#ifndef PF_HOTSPOT_PASS
#define PF_HOTSPOT_PASS "patternflow"
#endif
#ifndef PF_HOTSPOT_AUTO_AFTER_MS
#define PF_HOTSPOT_AUTO_AFTER_MS 15000
#endif
// Station retries while the hotspot is up and empty: each one is a scan
// with the AP off the air for a second or two, so they are rare. With
// someone on the hotspot there are none (core_wifi.h).
#ifndef PF_HOTSPOT_STA_RETRY_MS
#define PF_HOTSPOT_STA_RETRY_MS 300000
#endif
#ifndef PF_HOTSPOT_MAX_CLIENTS
#define PF_HOTSPOT_MAX_CLIENTS 4
#endif
#ifndef PF_IMPROV_ENABLED
#define PF_IMPROV_ENABLED 1
#endif
// Firmware version string reported to the flasher (Improv device-info RPC).
// Keep in sync with web/public/flash/manifest.json.
#ifndef PF_IMPROV_FW_VERSION
#define PF_IMPROV_FW_VERSION "3.10.4"
#endif
// ── Variant identity (RFC: docs/rfc-core-and-variants.md) ────
// Which firmware this is. "core" is the maintained ground; a variant
// overrides this with its own name ("simone-pd", "radio", "iot"), and that
// string is what /api/status reports, what the site's variant list matches,
// and what stops the console's update banner from offering a core build on
// top of somebody's chosen firmware.
//
// A variant sets it from its own header rather than editing this file, so
// its diff against the core stays additions-only:
// -DPF_VARIANT='"simone-pd"' or #define PF_VARIANT "simone-pd"
#ifndef PF_VARIANT
#define PF_VARIANT "core"
#endif
// A variant's own release, separate from the core version it is built on.
// Both matter and they answer different questions: the variant version is
// what somebody downloaded, the core version is what it was built against,
// and a support conversation needs both. Empty on core, where the core
// version already is the answer.
//
// RFC §2.6 rule 3 asked for this and only the name half was ever built,
// so a variant reported its identity and then core's version number
// beside it — which reads as a claim to be core.
#ifndef PF_VARIANT_VERSION
#define PF_VARIANT_VERSION ""
#endif
// ── OTA (wireless flashing from Arduino IDE / espota.py) ─────
// On by default. Loop cost is one UDP poll per frame when idle.
#ifndef PF_OTA_ENABLED
#define PF_OTA_ENABLED 1
#endif
// mDNS hostname → reachable as "<hostname>.local". Change when running
// more than one device on the same network.
#ifndef PF_OTA_HOSTNAME
#define PF_OTA_HOSTNAME "patternflow"
#endif
// Upload password. Default "" = no authentication: the device never asks
// for a password, so espota.py / arduino-cli upload with zero friction.
//
// Arduino IDE 2.x is the exception — its network-upload dialog always
// prompts and refuses an empty field. With no-auth firmware the value is
// ignored by the device, so type any dummy character to get past it, or
// upload from the command line instead (see firmware/README.md).
#ifndef PF_OTA_PASSWORD
#define PF_OTA_PASSWORD ""
#endif
// ── Browser self-update (drop a .bin onto patternflow.local/update) ──
// The device serves an update page over plain LAN HTTP; dropping a firmware
// .bin there flashes it via Update.h and reboots (issue #232). No TLS, no
// certificates — the browser fetches the build over HTTPS, the device only
// sees a same-origin LAN upload. The POST endpoint only accepts firmware
// while the UPDATE screen is open on the device (hold K2 → NETWORK, turn
// K4), so nobody on the Wi-Fi can reflash a device that is just sitting
// there. Shares the audio-react HTTP server (port 80) when that is enabled;
// otherwise runs its own. Uses PF_OTA_HOSTNAME for the .local name.
#ifndef PF_WEBUPDATE_ENABLED
#define PF_WEBUPDATE_ENABLED 1
#endif
// With 1 (the default) /update accepts firmware at any time — drop a .bin
// whenever, no trip to the device. The tradeoff, stated plainly: anyone on
// the same Wi-Fi can flash the device from a phone browser. That is the
// same exposure ArduinoOTA's no-password default already has, and on a
// home/studio network it is the right UX call. Set 0 on shared, office, or
// exhibition Wi-Fi: uploads are then refused unless the UPDATE screen is
// open on the device (hold K2 → NETWORK, turn K4) — a physical arming
// switch only someone at the device can flip.
#ifndef PF_WEBUPDATE_ALWAYS_ARMED
#define PF_WEBUPDATE_ALWAYS_ARMED 1
#endif
// ── The absolute lanes (core) ────────────────────────────────
// Any feature may push a normalized 0..1 value onto one of the four lanes
// (audio bands, weather channels, a show) and the core's input layer turns
// 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.
//
// 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
// somebody's setting is worse than an untidy header.
#ifndef PF_LANE_MOTION_SCALE
#ifdef PF_AUDIO_VIRTUAL_KNOB_SCALE
#define PF_LANE_MOTION_SCALE PF_AUDIO_VIRTUAL_KNOB_SCALE
#else
#define PF_LANE_MOTION_SCALE 48.0f
#endif
#endif
07e931b46This file is pinned to the selected release. Download the original to open it in your design tool.