PCF8574 IO Expander
The PCF8574 provides 8 logical digital inputs/outputs over a shared I2C bus. First confirm that the current firmware is compiled with IO expander capability, and enable "Generic IO Expander" (ioExpander) in Feature Configuration; compile-time capability, feature enablement, and driver initialization are three independent states.
Feature Enablement Order
The feature switch is off by default and applies after restart: saving only changes user intent, while bootEnabled reflects the value used for the current boot. When you see pendingRestart, save and restart first, then check the actual active state. Reloading configuration at runtime, rolling back after a failed save, or restoring defaults never applies the switch early. Configuration can be kept and edited while the feature is disabled; actual use is simultaneously constrained by "firmware compiled, user enabled, and runtime memory budget allows". When the memory budget denies it, check the feature status — a successful save must not be taken as the hardware already running.
Peripheral enablement order: first enable and save the shared I2C bus, then enable the expander parent device, and finally enable the logical input/output sub-channels. A bus referenced by an enabled peripheral must be an enabled I2C master (address=0).
Expander Parent Device
| Item | Description |
|---|---|
| Type | type=55, driver="PCF8574" |
| Address | 0x20–0x27 (JSON uses decimal 32–39) |
sampleInterval | 20–1000 ms |
| Count | Up to 4 expander parent devices |
Only the PCF8574 is currently implemented; PCF8574A addresses 0x38–0x3F are not within this driver's supported range.
Logical Input/Output Channels
| Configuration layer | Fields and ranges |
|---|---|
| Logical input | type=11, ioSource=1, references providerId, channel=0–7, pins=[] |
| Logical output | type=12, same reference rules; initialState=0/1 defines the software startup state |
| Input parameters | inputMode=0; configurable debounceMs, invertLogic, reportMode |
| Action capability | High, low, toggle, and low-speed blinking with an interval no shorter than 200 ms; inputs support debounce and button events |
Each channel of the same parent device can have only one logical owner; disabled entries still retain channel occupancy.
The PCF8574 is a quasi-bidirectional port: inputs release the port by writing 1 to the corresponding bit, and there is no MCU-style selectable pull-up/pull-down, ADC, PWM, or breathing-light capability. The current implementation does not connect the chip's INT pin and uses only bounded polling; these unsupported modes and actions are rejected.
Ports are high at chip power-up. initialState is the target level after firmware initialization and cannot eliminate the level window before firmware startup; loads that must not actuate spuriously must be guaranteed through external enable, driver circuitry, and hardware default states. When a single channel is disabled or deleted, that channel's configured startup value is restored; other outputs are not reset under normal conditions.
Ports must not directly connect relay coils, motors, 24 V industrial inputs, or mains loads; add input conditioning/isolation or output drivers per actual electrical requirements. The chip's drive capability, the board's terminal ratings, and the isolation capability cannot substitute for one another.
Shared Bus Rules
- The PCF8574 requires 100 kHz; use this frequency uniformly when coexisting with other sensors or display devices. Different physical buses or frequencies cannot be mixed by adding logical bus entries.
- Do not let another driver re-initialize the same
Wireon its own. - Pay special attention to LCD1602 adapter boards when sharing addresses: they may use the same address as the PCF8574, and their automatic address-scanning mode may also occupy the candidate address range. An address found by scanning only proves an acknowledge was received; it does not prove the chip model, wiring, or that the driver is fully functional.
- The shared bus lease limit is 16, and existing displays and the like also consume leases; this limit is not a guarantee that any arbitrary combination meets real-time and memory budget requirements.
Bus references use busPeripheralId; in this case the slave device's pins must be empty. In standalone wiring mode, clear the reference and provide two distinct SDA/SCL pins; the two forms cannot be filled in at the same time.
Status, Errors, and Recovery
A read failure is not the value 0. Web/API and rules must distinguish "valid reading", "not yet sampled", and "stale/unavailable", checking the sample time, valid, stale, and the error reason at the same time; disabled or invalidated data must not continue to participate in display or linkage as fresh values.
| Error or status | Handling |
|---|---|
IO_EXPANDER_NOT_COMPILED / IO_EXPANDER_DISABLED / IO_EXPANDER_BUDGET_DENIED | Check firmware, feature switch, and budget; after IO feature changes, confirm the restart has been applied |
I2C_REFERENCE_INVALID / I2C_BUS_CONFLICT / I2C_ADDRESS_CONFLICT | Fix bus references, enablement order, wiring/frequency, or address conflicts, then re-run the preflight check |
IO_CHANNEL_CONFLICT / IO_CHANNEL_INVALID | Remove duplicate channel owners; use channels 0–7 and plain digital input/output |
IO_CONFIG_PENDING / IO_SAMPLE_UNAVAILABLE / IO_PROVIDER_UNAVAILABLE | Configuration sync in progress or the parent device has not provided a fresh sample; wait for the state to recover, and if it persists, check power supply and the bus |
IO_WRITE_FAILED | This output operation failed; check the parent device and each output state, and do not assume the load has switched |
PCF8574 inputs are polled as a whole byte. The first failed real read invalidates the current sample; the output recovery flow is entered only after 3 consecutive read failures or one write failure with an uncertain result. During recovery, each channel's startup value is re-applied and the input baseline is rebuilt, avoiding phantom buttons caused by disconnection/recovery. Retry intervals for real faults gradually increase up to 2 s; transient bus busy and hardware faults are handled separately. When scheduling delays cause samples to go stale, output startup values are not replayed merely because of staleness.
When disabling, deleting, or updating a logical channel, if the bus is temporarily busy and it cannot be confirmed that the old channel has retired as agreed, the parent device enters IO_CONFIG_PENDING. Before retirement completes, other channels on the same parent device can still run, but the original channel cannot be bound to a new peripheral; retry that configuration only after the maintenance task completes synchronization. This prevents a new peripheral from inheriting the old output shadow and wrong levels.
Field Acceptance
Select at least one no-PSRAM target and one target from a different chip family: verify a single module first, then coexistence on the same bus, and finally add the existing network, rules, Web, and display load. Until the following items are completed, keep the status "pending verification"; passing compilation or host tests cannot substitute for physical conclusions.
| Check item | Operation and pass criteria |
|---|---|
| Wiring and preflight | Record module/chip, bus address, SDA/SCL, pull-up level, power supply, and load; preflight has no unresolved conflicts |
| Configuration recovery | Save, restart, reopen the form; references, channels, enablement states, and effective features are consistent |
| IO startup and isolation | Check startup levels with an observable test load; toggle each channel high/low/toggle while other channels do not change |
| IO input events | Press/release, click, bounce, long press, and held at startup; no duplicate events or phantom triggers |
| IO exit and recovery | Disabling/deleting an output restores its configured startup value; disconnection/write failure shows unknown; reconnection restores startup values without phantom buttons |
| Rule chain | Input toggles output; configuration changes immediately invalidate old samples; verify existing platform thing-model reporting as needed |
| Shared bus | Sensors, IO, existing display, and scanning concurrently; no re-initialization conflicts, unintended output changes, or long-term starvation |
| Feature and budget | Switch, restart application, memory budget denial, recovery; user configuration is preserved and the status can explain why it is unavailable |
| 10 real restart rounds | Save, restart, restore configuration loop; compare heap, instances, and references at the same stable point; no persistent resource decline |
| 24 h mixed run | With the same load, record heap, largest contiguous block, execution/sampling latency, error counts, and reconnections; no crashes or persistent resource growth |
| Rollback | Disable related rules and peripherals, restore the accompanying old configuration/firmware, and confirm the original functions and actual load states |

