diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json
index 371edf6..28393d1 100644
--- a/.obsidian/workspace.json
+++ b/.obsidian/workspace.json
@@ -100,12 +100,12 @@
"state": {
"type": "markdown",
"state": {
- "file": "99 Work/0 OneSec/OneSecNotes/10 Projects/OneSecServer/OneSec Server.md",
+ "file": "99 Work/0 OneSec/OneSecNotes/10 Projects/TeensyFlightcontroller/Parameter System Desing Document.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
- "title": "OneSec Server"
+ "title": "Parameter System Desing Document"
}
},
{
@@ -300,7 +300,7 @@
}
],
"direction": "horizontal",
- "width": 200
+ "width": 407.5
},
"right": {
"id": "1017d1cf4a473028",
@@ -495,6 +495,9 @@
},
"active": "8340cc400aabd495",
"lastOpenFiles": [
+ "99 Work/0 OneSec/OneSecNotes/10 Projects/OneSecServer/OneSec Server.md",
+ "99 Work/0 OneSec/OneSecNotes/10 Projects/TeensyFlightcontroller/Parameter System Desing Document.md",
+ "99 Work/0 OneSec/OneSecNotes/10 Projects/TeensyFlightcontroller",
"Attachments/Pasted image 20250922115441.png",
"Temporary/Untitled 15.md",
"Temporary/Webapp - Chirp Log Review.md",
@@ -520,7 +523,6 @@
"2 Personal/Home Lab/NAS/Jellyfin Installation.md",
"2 Personal/Home Lab/iPhone/Maps.md",
"Temporary/Drone Regulations Requirements.md",
- "0 Journal/0 Daily/2025-09-10.md",
"7 People/0_People.base",
"Attachments/Belts, Suspenders.mp3",
"Attachments/image 21.jpg",
@@ -537,11 +539,9 @@
"Dashboard Canvas.canvas",
"Attachments/Pasted image 20250627101456.png",
"99 Work/0 OneSec/OneSecNotes/40 - User Manuals",
- "Attachments/Pasted image 20250624161349.png",
"2 Personal/1 Skills/IT",
"Sync",
"Shared_Folder",
- "OneNote/BSM",
"99 Work/0 OneSec/OneSecNotes/30 Engineering Skills/Computer Science/Untitled.canvas",
"8 Work/OneSecNotes/Temporary/Untitled.canvas"
]
diff --git a/99 Work/0 OneSec/OneSecNotes/10 Projects/TeensyFlightcontroller/Parameter System Desing Document.md b/99 Work/0 OneSec/OneSecNotes/10 Projects/TeensyFlightcontroller/Parameter System Desing Document.md
new file mode 100644
index 0000000..cb3ae37
--- /dev/null
+++ b/99 Work/0 OneSec/OneSecNotes/10 Projects/TeensyFlightcontroller/Parameter System Desing Document.md
@@ -0,0 +1,596 @@
+---
+title: Parameter System Desing Document
+created_date: 2025-09-23
+updated_date: 2025-09-23
+aliases:
+tags:
+---
+# Parameter System Desing Document
+
+> Teensy 4.0 • Teensyduino/Arduino framework • No RTOS • ETL containers • Chirp protocol for messaging
+
+> **Scope:** Flight Controller (FC) side only, with **name-as-canonical** parameters and **immediate apply** (no staging yet).
+
+> Enums such as TypeId, ParamStatus, ParamOp come from the Chirp side. Include the relevant Chirp headers; do not redefine them here.
+
+---
+
+## 1) Current Rules / Constraints
+
+* **Platform:** Teensy 4.0, Arduino/Teensyduino framework, no RTOS.
+* **Transport:** **Chirp** is the sole protocol for Get/Set requests and responses.
+* **Design principles**
+
+ 1. **Name is canonical.** We identify parameters by a unique, human-readable path.
+ 2. **Shallow ownership tree.** Each module owns a **single sub-tree**; keep names shallow: `module.param`.
+ 3. **Module-facing interface.** Modules expose parameters via light interfaces; the manager never “reaches in” to private variables.
+* **Non-module params:** System-level, logging, build info, flight mode selectors, serial settings, etc. must also fit the same model.
+
+### Current Chirp Message Structure
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+---
+
+## 2) Naming
+
+* **Hierarchical dot paths: (Either with `group.name` combination, or directly for `name`)**
+ Examples:
+ `attitudectrl.kp_roll`, `attitudectrl.torque_limit`,
+ `servo_left_flap.offset_angle`, `serial_pilot.baud_rate`,
+ `i2c_line1.clock`,
+ `logger.sink_interface`, `system.fw_version`.
+
+* **Uniqueness:** Names are **case-sensitive** and must be unique within the FC.
+
+* **Ownership:** The **root token** (e.g., `attitudectrl`, `logger`, `serial_pilot`) identifies which module/adapter owns the parameter.
+
+---
+
+## 3) Core Components
+
+### 3.1 ParamRegistry (all descriptors)
+
+A single in-memory catalog describing **every configurable parameter** on the FC.
+The registry answers **what** exists (metadata), **how to read** it, and **how to apply** a new value—**without** the manager touching module internals.
+
+#### Descriptor structure (Globally defined across every system in drone)
+
+* `participant` : which device owns this parameter set (here: `"flight_controller"`).
+ *Note:* other devices (autopilot, co-pilot) will have their own registries in their repos.
+
+* `group` : high-level bucket **within the FC**, typically the module or subsystem root (`"attitudectrl"`, `"logger"`, `"system"`, `"serial_copilot"`, `"rc"`, `"imu_leftwing"`).
+
+* `name` : either the **full name of the parameter**, e.g., `"attitudectrl.kp_roll"` , or partial name so that group + name results in the unique name.
+
+* `type` : `TypeId` (shared enum across Chirp, e.g., `Float`, `Uint16`, …).
+
+* `length` : size in bytes for validation and arrays.
+
+* `default_value` : bytes for default initialization at boot.
+
+* `flags` : bitmask. Possible flags:
+
+ * `Writable` — if **unset**, parameter is read-only.
+ * `RebootRequired` — write accepted but **takes effect after reboot**.
+ * `ImmediateApply` — explicitly safe for immediate side-effects.
+
+* **Accessors (delegates):**
+
+ * `get(name) : bytes ` — read current value from the owning module.
+ * `set(name, bytes) : status` — parse/validate module-local rules and update the module.
+
+> The registry owns **only metadata and function delegates**. Actual values remain inside modules.
+
+#### Example C++ sketch
+
+```cpp
+
+struct ParamDescriptor {
+ const char* participant; // "flight_controller"
+ const char* group; // "attitudectrl", "logger", ...
+ const char* name; // "attitudectrl.kp_roll" (canonical, unique)
+ TypeId type;
+ uint16_t length; // sizeof(float) etc.
+ uint8_t flags; // bitmask of ParamFlag
+ const uint8_t* default_bytes; // pointer to default value bytes
+
+ // Delegates to module-owned code:
+ etl::delegate getter;
+ etl::delegate setter;
+};
+
+class ParamRegistry {
+public:
+ // Fixed capacity
+ bool register_param(const ParamDescriptor& d); // returns false on duplicate name or full
+ const ParamDescriptor* find(const char* name) const; // or find(group, name) pair depending on full name implementation
+
+private:
+ etl::unordered_map, ParamDescriptor, NUM_PARAMS> m_by_name; // pre-sized buckets
+};
+```
+**Note**: `TypeId` , `ParamStatus` definitions exist in relevant chirp header files.
+
+
+> **Getter/Setter notes**
+>
+> * `getter` returns the current committed value from the module into `out`.
+> * `setter` validates and updates the module, sets `reboot_required=true` if the new value won’t take effect until reboot.
+
+---
+
+### 3.2 ParameterManager (global singleton)
+
+The single entry point for **Get/Set** operations and the **Chirp** callback endpoint.
+
+**Responsibilities**
+
+* Own the **ParamRegistry**.
+* Expose a **simple API** used by both:
+
+ * Internal code, and
+ * The **Chirp handler** (by registering a callback for `ParameterRequest`).
+* Apply changes unless the descriptor is `RebootRequired`.
+
+**Minimal API sketch**
+
+```cpp
+class ParameterManager {
+public:
+ static ParameterManager& instance();
+
+ void init(ChirpHandler& chirp); // registers ParamRequest callback
+ ParamRegistry& registry();
+
+ // Internal helpers
+ ParamStatus get_by_name(const char* name, uint8_t* out, uint16_t& out_len, TypeId& out_type) const;
+ ParamStatus set_by_name(const char* name, const uint8_t* in, uint16_t in_len, TypeId type, bool& reboot_required);
+
+ // Chirp callback (wired to opcode for ParameterRequest)
+ void chirp_param_request_callback(const chirp::ParameterRequest& req);
+
+private:
+ ParameterManager() = default;
+ ParamRegistry m_reg;
+};
+```
+
+**Key behaviors**
+
+* **Name resolution** via registry.
+* **Immediate apply:** `set_by_name` calls the descriptor’s `setter`. If success:
+ * If `RebootRequired` flag OR setter sets `reboot_required=true` → reply `RebootRequired`.
+ * Else reply `Ok`.
+* **Get** calls the descriptor’s `getter`.
+* **Chirp bridge:** `chirp_param_request_callback` maps Chirp fields to the API above and produces a `ParameterResponse`.
+
+---
+
+## 4) Module Integration via `IFlightModule`
+
+Grouping flight controller modules under a capability interface like `IFlightModule` can be useful:
+
+* Keeps a **clean boundary**: modules **declare** what they own and how to apply changes—no global manager poking internal data.
+* Enables **testability**: I can unit-test a module’s param parsing/validation in PC native tests without wiring the entire FC.
+* Simplifies **registration**: each module lists its parameters and provides delegates once, during init.
+
+**Example interface**
+
+```cpp
+struct IFlightModule {
+ virtual const char* root_namespace() const = 0; // e.g., "attitudectrl"
+ virtual void enumerate_params(ParamRegistry& reg) = 0; // Optional. modules can expose dedicated getters/setters per param as how it is currently.
+ virtual ~IFlightModule() = default;
+};
+```
+
+**How it’s used**
+
+* On boot, each module calls `enumerate_params(reg)`.
+* Inside `enumerate_params`, the module **registers its parameters** by building `ParamDescriptor`s with **delegates bound to specific setter/getter methods of `this`**.
+
+**Example — Attitude Controller registers `kp_roll`**
+
+```cpp
+class AttitudeController : public IFlightModule {
+public:
+ const char* root_namespace() const override { return "attitudectrl"; }
+
+ void enumerate_params(ParamRegistry& reg) override {
+ static const float DEFAULT_KP_ROLL = 10.0f; // Either defined here or as a member variable
+ static const uint8_t* def = reinterpret_cast(&DEFAULT_KP_ROLL);
+
+ ParamDescriptor d {
+ "flight_controller",
+ "attitudectrl",
+ "kp_roll", // or "attitudectrl.kp_roll"
+ TypeId::Float,
+ (uint16_t)(sizeof(float)),
+ (uint8_t)(ParamFlag::Writable | ParamFlag::ImmediateApply),
+ def,
+ // getter: copy current kp into out
+ etl::delegate::create(this),
+ // setter: parse float, validate, assign
+ etl::delegate::create(this)
+ };
+ reg.register_param(d);
+ }
+
+ // Example getter/setter implementations:
+ ParamStatus get_kp_roll(uint8_t* out, uint16_t& out_len) {
+ const uint16_t needed_length = static_cast(sizeof(float));
+ if (out_len < needed_length) return ParamStatus::InvalidType;
+ memcpy(out, &m_kp_roll, needed_length);
+ out_len = needed_length;
+ return ParamStatus::Ok;
+ }
+
+ ParamStatus set_kp_gains(const uint8_t* in, uint16_t in_len, bool& reboot_required) {
+ const uint16_t needed_length = static_cast(sizeof(float));
+ if (in_len != needed_length) return ParamStatus::InvalidType;
+ float v;
+ memcpy(&v, in, needed_length);
+
+ if (v[i] < min || v[i] > max) return ParamStatus::InvalidValue;
+
+ reboot_required = false; // takes effect immediately
+ return ParamStatus::Ok;
+ }
+
+/* rest of the class definition ... */
+
+private:
+ float m_kp_roll = 10.0f;
+ ...
+};
+```
+
+> This pattern can be replicated this pattern for other attitude controller params like `torque_limit`, indi related params etc.
+
+> Alternatively, we can alter the structure of `ParamDescriptor` in the example without redefining the current parameter structure of attitude controller,
+keeping the array structure for kp, kd gains, torque limits etc. Design supports multiple value arrays as single parameter:
+
+```cpp
+ ParamDescriptor d {
+ "flight_controller",
+ "attitudectrl",
+ "kp_gains", // or "attitudectrl.kp_gains"
+ TypeId::Float,
+ (uint16_t)(3 * sizeof(float)),
+ (uint8_t)(ParamFlag::Writable | ParamFlag::ImmediateApply),
+ def,
+ // getter: copy current kp into out
+ etl::delegate::create(this),
+ // setter: parse float, validate, assign
+ etl::delegate::create(this)
+ };
+```
+
+where `kp_gains` is defined as:
+```cpp
+private:
+ etl::array m_kp_gains;
+```
+
+---
+
+## 5) Hardware Parameters via `IHardwareParamAdapter`
+
+Some parameters belong to **hardware drivers** (IMUs, serial baud, PWM ranges). I don’t want the ParameterManager to depend on driver APIs, and most drivers are used by multiple flight modules.
+
+**The adapter idea:** a **tiny object** owned by the module that uses the driver (or owned centrally) which exposes parameters for that hardware subtree, using the **same pattern** as a module:
+
+```cpp
+struct IHardwareParamAdapter {
+ virtual const char* root_namespace() const = 0; // e.g., "serial" or "imu"
+ virtual void enumerate_params(ParamRegistry& reg) = 0;
+ virtual ~IHardwareParamAdapter() = default;
+};
+
+// Example: Serial adapter exposing baud rate
+class SerialParamAdapter : public IHardwareParamAdapter {
+public:
+ const char* root_namespace() const override { return m_root_namespace; }
+
+ void set_root_namespace(char* root_namespace) { m_root_namespace = root_namespace; } // These setters can be needed for multiple instances of serial e.g `serial_pilot` , `serial_copilot`
+
+ void enumerate_params(ParamRegistry& reg) override {
+ static const uint32_t DefaultBaud = 115200;
+ static const uint8_t* def = reinterpret_cast(&DefaultBaud);
+
+ ParamDescriptor baud {
+ "flight_controller",
+ m_root_namespace,
+ "baud_rate",
+ TypeId::Uint32,
+ (uint16_t)sizeof(uint32_t),
+ (uint8_t)(ParamFlag::Writable | ParamFlag::RebootRequired),
+ def,
+ etl::delegate::create(this),
+ etl::delegate::create(this)
+ };
+ reg.register_param(baud);
+ }
+
+ ParamStatus get_baud(uint8_t* out, uint16_t& out_len) {
+ if (out_len < sizeof(uint32_t)) return ParamStatus::InvalidType;
+ memcpy(out, &m_baud, sizeof(uint32_t));
+ out_len = sizeof(uint32_t);
+ return ParamStatus::Ok;
+ }
+
+ ParamStatus set_baud(const uint8_t* in, uint16_t in_len, bool& reboot_required) {
+ if (in_len != sizeof(uint32_t)) return ParamStatus::InvalidType;
+ uint32_t v; memcpy(&v, in, sizeof(uint32_t));
+ if (v < 9600 || v > 3000000) return ParamStatus::InvalidValue;
+ m_baud = v;
+ reboot_required = true; // takes effect after re-init
+ return ParamStatus::Ok;
+ }
+
+private:
+ char* m_root_namespace;
+ uint32_t m_baud = 115200;
+ Serial* m_serial;
+};
+```
+
+**Why it’s useful**
+
+* Keeps **hardware side-effects** localized (re-init serial, reconfigure I2C, etc.).
+* Does not require any change to driver code
+* Multiple modules can **share** one adapter’s namespace (`serial.*`, `imu.*`).
+* Allows easy setup for *multiple hardware instances* (`serial_pilot`, `imu_leftwing`)
+* Adapters register like modules; the manager treats them equivalently.
+
+---
+
+## 6) Boot Sequence (step-by-step)
+
+1. **Construct** the global singleton:
+
+ ```cpp
+ auto& paramManager = ParameterManager::instance();
+ ```
+
+2. **Initialize Chirp and register callback:**
+
+ ```cpp
+ paramManager.init(chirp_handler); // internally: chirp_handler.register_callback(OPCODE_PARAM_REQUEST, &ParameterManager::chirp_param_request_callback);
+ ```
+
+3. **Create modules and adapters using factory method** (AttitudeController, Navigator, Logger, SerialParamAdapter, etc.).
+
+4. **Register parameters**:
+
+ Happens when modules are created, most likely in factory class.
+ ```cpp
+ attitudeController.enumerate_params(paramManager.registry());
+ logger.enumerate_params(paramManager.registry());
+ serialAdapter.enumerate_params(paramManager.registry());
+ // ... others
+ ```
+
+5. **Apply defaults** (optional):
+
+ * Iterate all descriptors and call their `setter(default_bytes, length)` to seed modules with defaults **once** at boot.
+
+6. **Ready for runtime**: FC now answers Chirp Get/Set by **name**.
+
+---
+
+## 7) Runtime Update Flow (example)
+
+**Scenario:** ground station sets `attitudectrl.kp_roll = 18.0f`.
+
+1. **Chirp RX:** `ChirpHandler` parses an incoming `ParameterRequest` with:
+
+ * `op = Set`
+ * `name = "attitudectrl.kp_roll"`
+ * `type = Float`
+ * `length = 4`
+ * `value = { bytes of 18.0f }`
+ * `transaction_id` = 42 (example)
+
+2. **Callback dispatch:** `ChirpHandler` calls
+ `ParameterManager::chirp_param_request_callback(req)`.
+
+3. **Manager logic (simplified):**
+
+ ```cpp
+ void ParameterManager::chirp_param_request_callback(const chirp::ParameterRequest& req) {
+ chirp::ParameterResponse resp{};
+ resp.transaction_id = req.transaction_id;
+ resp.timestamp = micros64(); // or timeManager.time() etc.
+
+ if (req.op == ParamOp::Set) {
+ bool reboot_required = false;
+ ParamStatus st = set_by_name((const char*)req.name, req.value, req.length, req.type, reboot_required);
+
+ resp.status = to_wire_status(st, reboot_required);
+ resp.name_size = req.name_size;
+ memcpy(resp.name, req.name, req.name_size);
+ // Echo type/length; optionally include the applied value via a getter or copy directly from request
+ send_parameter_response(resp);
+ }
+ else if (req.op == ParamOp::Get) {
+ uint8_t buf[256]; uint16_t len = sizeof(buf); TypeId t;
+ ParamStatus st = get_by_name((const char*)req.name, buf, len, t);
+
+ resp.status = to_wire_status(st, /*reboot_required*/false);
+ resp.type = t; resp.length = len;
+ memcpy(resp.value, buf, len);
+ send_parameter_response(resp);
+ }
+ }
+ ```
+
+4. **Validation path inside `set_by_name`:**
+
+ * **Lookup**: registry.find(name) → descriptor `d`; if not found → `NotFound`.
+ * **Type/length check**: `req.type == d.type` and `req.length == d.length`; else → `InvalidType`.
+ * **Access check**:
+
+ * If `!(d.flags & Writable)` → `AccessDenied`.
+ * If `is_armed() && (d.flags & FlightLocked)` → `AccessDenied`.
+ * **Apply**:
+
+ * Call `d.setter(req.value, req.length, reboot_required)`.
+ * If setter returns `InvalidValue` (e.g., out of bounds) → reply `InvalidValue`.
+ * If success and `(d.flags & RebootRequired) || reboot_required` → reply `RebootRequired`.
+ * Else → reply `Ok`.
+
+5. **Module effect:**
+
+ * AttitudeController’s `m_kp_roll` is updated to 18.0f and will be used by the next control step.
+
+---
+
+## 8) Validation Layers (where checks live)
+
+1. **Manager-level (generic)**
+
+ * Name exists in registry.
+ * Type/length match the descriptor.
+ * Flags / access policy (Writable, FlightLocked).
+
+2. **Module-level (semantic)**
+
+ * Bounds and relationships specific to the parameter. (Outside of min-max range, must be non-zero etc.)
+
+**Tiny helper for type/length check**
+
+```cpp
+static bool check_wire_matches_desc(TypeId wire_type, uint16_t wire_len, const ParamDescriptor& d) {
+ return (wire_type == d.type) && (wire_len == d.length);
+}
+```
+
+---
+
+## 9) Example: Wiring it Together
+
+**Register the Chirp callback once**
+
+```cpp
+void ParameterManager::init(ChirpHandler& chirp) {
+ chirp.register_callback(chirp::Opcode::ParameterRequest,
+ etl::delegate::create(this));
+}
+
+void ParameterManager::on_any_message(const chirp::MessageUnpacked& msg) {
+ // translate msg → ParameterRequest or ignore if not ParamRequest
+ chirp::ParameterRequest req;
+ if (!decode_parameter_request(msg, req)) return;
+ chirp_param_request_callback(req);
+}
+```
+
+**Set/Get APIs**
+
+```cpp
+ParamStatus ParameterManager::set_by_name(const char* name, const uint8_t* in, uint16_t in_len, TypeId type, bool& reboot_req) {
+ reboot_req = false;
+ const ParamDescriptor* d = m_reg.find(name);
+ if (!d) return ParamStatus::NotFound;
+
+ if (!check_wire_matches_desc(type, in_len, *d)) return ParamStatus::InvalidType;
+
+ if (!(d->flags & (uint8_t)ParamFlag::Writable)) return ParamStatus::AccessDenied;
+ if (is_armed() && (d->flags & (uint8_t)ParamFlag::FlightLocked)) return ParamStatus::AccessDenied;
+
+ ParamStatus st = d->setter(in, in_len, reboot_req);
+ return st;
+}
+
+ParamStatus ParameterManager::get_by_name(const char* name, uint8_t* out, uint16_t& out_len, TypeId& out_type) const {
+ const ParamDescriptor* d = m_reg.find(name);
+ if (!d) return ParamStatus::NotFound;
+ out_type = d->type;
+ return d->getter(out, out_len);
+}
+```
+
+---
+
+## 10) Non-Module/System Parameters
+
+Treat these as **their own roots** or grouped under `system.*` / `logger.*` / `build.*`, etc.
+
+* `logger.level` — writable, immediate.
+* `system.flight_mode` — writable, may be `FlightLocked`.
+* `build.git_sha` — read-only (no `Writable` flag).
+* `serial_pilot.baud_rate` — `RebootRequired` (or requires port re-init), adapter-owned.
+
+They register exactly like module parameters (same `ParamDescriptor` pattern).
+
+---
+
+## 11) Future Considerations
+
+* Parameter **Store** and **StagingBuffer** (two-phase commit).
+ → initial version applies immediately; I'd be a good idea to consider staging the changes and applying in a safe state for later versions.
+
+* Full-list enumeration & chunking (GetFullList) for sharing the parameter list with other devices.
+
+* Persistence to Flash/EEPROM
+
+---
+
+## 12) Checklist to Implement
+
+1. Implement **ParamRegistry** with ETL containers.
+2. Create **ParameterManager** singleton; wire to **ChirpHandler**.
+3. Convert 1–2 modules (e.g., `attitudectrl`, `navigator`) to implement **enumerate\_params** and delegate setters/getters.
+4. Add a **SerialParamAdapter** as a reference hardware example.
+5. Bring up **Get/Set by name** end-to-end and test on PC and on Teensy.
+
+---
+
+## TODOs
+
+Add the full list of current parameters in flight controller, to this document.
+
+Try to fit them in currently proposed structure with proper naming, flags.
\ No newline at end of file