PWM and Passive Buzzer Control Boundaries
2026-10-04 development candidate, pending centralized testing/build; not deployed to the field. A successful software command does not mean the buzzer sounds, the load acts, or the physical output is off.
| Target | HIGH / LOW | toggle | blink | breathe |
|---|---|---|---|---|
| Native PWM | Max duty cycle / 0 | Supported | Supported | Supported |
| Native AO | Configured max / min | Supported | Not supported | Supported |
| Digital output passive=true | Half duty cycle / 0; 127/0 at 8-bit | Supported | Supported | Not supported |
| DAC | Controlled with the DAC value | Not supported | Not supported | Not supported |
0% in AO mode may represent the minimum 4mA or a non-zero duty cycle; it must not be called power-off. For a passive buzzer, the raw PWM value is still a duty cycle, and HIGH keeps the half-duty-cycle semantics; for the ordinary PWM type, HIGH keeps the full-duty-cycle semantics. The selected type, frequency, and wiring must match the actual hardware.
These actions share the generic source ownership of rules/MQTT/HTTP/screen. HTTP identity comes from the authenticated session; clients cannot self-report an actor. Manual ownership defaults to 30 seconds, competitors are rejected, and only a qualifying target-level review can confirm takeover; there is no automatic retry. The screen executes bound rules under the original saved layout identity. Dedicated thermostat/RESET/OE outputs cannot be overridden by ordinary commands.
CALL_PERIPHERAL supports the text toggle and the JSON action value {"action":"blink","value":"250"}; a blink without a value defaults to 500ms. Named scripts support PERIPH <id> TOGGLE and PERIPH <id> BLINK <milliseconds>; a script interval of 0 keeps the 500ms default, strictly limited to 0-65535. HTTP uses toggle=true or action=toggle; blink uses action=blink&intervalMs=250, and HTTP requires an explicitly valid interval of 1-65535. Do not mix in periodMs for breathe. These entry points cannot start a blink on AO or a breathe on passive.
Toggle flips based on the last successfully submitted logic level and does not measure the pin waveform. Toggle is rejected when the state is unknown; verify the field first, then explicitly request the absolute output you need. SDK failures, expired serial sources, timers that cannot stop, and HTTP 503 do not trigger automatic command retransmission.
stop, manual ownership expiry, and source retirement only stop subsequent waveform writes and keep the last output; to silence or zero it, explicitly send LOW / the corresponding value with permission and safe wiring, then confirm the actual feedback. A failed stop keeps the owner and error; do not forcibly clear the record or claim it is off. Legacy untagged timers from older versions need a confirmed restart/migration and must not be treated as takeover-ready.
Real devices, sound, PWM frequency/waveform, AO calibration, and long-term stability have not yet been accepted. Physical results from historical examples and old firmware behavior cannot be carried over as evidence that this candidate passes.

