Skip to content

Control Types

A PanelGroup sketch is mostly a list of control objects — one per physical switch, knob, LED, or gauge. Inputs read hardware and fire CAN events; outputs receive DCS state and drive hardware. This page is the catalogue, with honest Phase status: most types are specified but not yet implemented.

Most control types are not implemented yet

Implemented today: LED, DrumDisplay, and NeedleGauge (outputs) and Switch2Pos + SwitchMultiPos + AnalogMultiPos + Switch3Pos + AnalogInput + RotaryEncoder (inputs), plus the PinRef abstraction. LED / Switch2Pos / PinRef are hardware-verified (Phase 3); DrumDisplay is hardware-verified (mux + readouts on real OLEDs). NeedleGauge is authored and compile-gated, with the on-hardware bench still pending; SwitchMultiPos, AnalogMultiPos, Switch3Pos, AnalogInput, and RotaryEncoder are hardware-verified. Everything marked Phase 4 or Phase 5 below is specified but not yet written — don't expect it to compile today.

PinRef — the hardware abstraction (implemented)

Every input and output takes a PinRef, not a raw pin number. One interface over three hardware backends:

Backend Constructor Notes
STM32 GPIO PinRef(pin) direct digitalRead/Write, analogRead/Write
MCP23017 PinRef(chip, PORT_A\|PORT_B, bit) digital I/O expander
ADS1115 PinRef(adc, channel) analog input, channels 0–3

Only direct STM32 GPIO can do PWM or servo output — isGpio() reports the backend type.

Routing is by controlId, not by class. The same input class drives a DCS-BIOS control or a HID button depending on the controlId you give it:

// DCS-BIOS route — controlId in 0x8000–0x86FF
OpenSkyhawk::Switch2Pos masterArm(DCSIN_ARM_MASTER, PinRef(PB5));

// HID route — controlId < 0x8000
OpenSkyhawk::Switch2Pos trigger(CTRL_TRIGGER, PinRef(PA6));

See DCS-BIOS vs HID for which to use.

Input classes

Class Status What it is
Switch2Pos Implemented Debounced 2-position switch (20 ms). value 0/1
Switch3Pos Implemented (hardware-verified) 3-position (ON-OFF-ON). value 0/1/2
SwitchMultiPos Implemented (hardware-verified) N-pin rotary, one active. value = index
AnalogMultiPos Implemented (hardware-verified) Resistor-ladder selector on one analog pin
ActionButton Phase 4 — not started Momentary; fires on press only
RotaryEncoder Implemented (dual-mode REL/DIR, #147) Quadrature encoder, relative. REL → ±step (continuous knobs); DIR → ±1 (no-indicator selectors)
RotaryAcceleratedEncoder Phase 4 — not started Encoder with slow/fast (4-value scheme)
RotarySwitch Phase 4 — not started Encoder used as an N-position absolute switch
AnalogInput Implemented (hardware-verified) Continuous analog, normalised to 16-bit (EWMA + hysteresis)
AngleSensorInput Phase 4 — not started Hall angle sensor (AS5600/MT6701) for flight axes
SwitchWithCover2Pos Phase 4 — not started Guarded switch (cover + switch)

All inputs normalise analog sources to 16-bit (0–65535) before sending. Inputs self-register at global scope; PanelGroup::loop() polls them and batches events into EVT_n CAN frames.

AngleSensor base class is a documented gap

AngleSensorInput wraps an AngleSensor abstract base (concrete AS5600Sensor / MT6701Sensor). The base-class TechSpec is missing — it's a known gap, not yet specified. Don't assume the API beyond begin() / readAngle().

Output classes

Class Status What it is
LED Implemented GPIO pin driven from one bit of a DCS value
DrumDisplay Implemented (hardware-verified) OLED rolling-drum readout — multi-digit gauges (speed, lat/lon, frequency, range) + optional 2-state flag. Own library; pulls U8g2
NeedleGauge Implemented (bench pending) Pointer/needle gauge — maps a DCS value to a motor angle over a swappable driver backend (linear or calibrated curve). Supersedes SwitecX25Output / AccelStepperOutput / ServoOutput
AnalogOutput Phase 5 — not started 16-bit DCS value → PWM duty (backlighting)
IntegerOutput Phase 5 — not started Raw 16-bit value to a user callback

Outputs use DCS-BIOS output addresses from the generated A4EC headers — the address constant plus its bitmask. Example for the implemented LED:

OpenSkyhawk::LED masterCaution(A_4E_C_MASTER_CAUTION, A_4E_C_MASTER_CAUTION_AM, PinRef(PB0));

Note the naming: A_4E_C_<NAME> for the address and A_4E_C_<NAME>_AM for the mask — not the old _A suffix. See DCS-BIOS Integration.

NeedleGauge drives gauge motors through a swappable backend

NeedleGauge does only the value→angle mapping. The drive lives in a reusable motor-driver layer (Firmware/Libraries/PanelGroup/Drivers/): a MotorDriver base with a StepperMotor backend today — non-blocking, driving four coils through PinRef (native GPIO or an MCP23017 expander). One air-core profile covers the X27.589 / VID-29 / BKA-30 family; homing is either a mechanical hard-stop or a digital home sensor (switch / reed / hall / opto). Drive the X27 at 5 V through a DRV8833. A ServoMotor backend is planned (#132).

DrumDisplay is a separate, opt-in library

DrumDisplay lives in Firmware/Libraries/DrumDisplay/ (not PanelGroup) so the U8g2 OLED driver only lands on nodes that actually use a display — add file://../../Libraries/DrumDisplay to a sketch's lib_deps to use it. Each readout (its digit sources, geometry, and optional flag) is described by a DrumReadout defined in the sketch, like the PinRef wiring map. Many same-address OLEDs can share one bus behind a TCA9548A via the I2cMux helper.

Wiring map convention

PinRef bit positions and mask values must be named constants, never inline literals. Define a wiring-map block at the top of each sketch — one named PinRef per physical connection, matching the schematic net label — so every pin appears exactly once and traces back to the schematic.