Poll the Si7021 every 100 ms and report cabin readings on change #92

Merged
rob merged 3 commits from feat/heater-sensor-rate into feat/heater-cabin-control 2026-10-10 14:23:17 +00:00
Collaborator

The Si7021 is now polled every 100 ms. ReadoutEndpoint gains an optional change threshold in the endpoint's own units: when non-zero, a status change or a change of at least the threshold marks it for report, on top of the 10 s heartbeat. Zero keeps today's periodic-only behaviour, so no other node changes. The cabin temperature and humidity readouts use a threshold of 1.

The motor is now written only when its level changes. LevelEndpoint::request marks the endpoint dirty even for an unchanged level (via noteChange), so that guard is what prevents a state frame per pass.

Stacks on #89, #90 and #91.

Humidity may report several times a second at the 0.1 % step; a deadband is the fix if the bench shows it is too chatty. Tested: pio test -e native and pio run -e node_heater. Not tested on hardware.

Ticket: CCS-WHP-15

The Si7021 is now polled every 100 ms. `ReadoutEndpoint` gains an optional change threshold in the endpoint's own units: when non-zero, a status change or a change of at least the threshold marks it for report, on top of the 10 s heartbeat. Zero keeps today's periodic-only behaviour, so no other node changes. The cabin temperature and humidity readouts use a threshold of 1. The motor is now written only when its level changes. `LevelEndpoint::request` marks the endpoint dirty even for an unchanged level (via `noteChange`), so that guard is what prevents a state frame per pass. Stacks on #89, #90 and #91. Humidity may report several times a second at the 0.1 % step; a deadband is the fix if the bench shows it is too chatty. Tested: `pio test -e native` and `pio run -e node_heater`. Not tested on hardware. Ticket: CCS-WHP-15
Add Si7021 cabin temperature and humidity to the heater node
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
08b6a5b24e
Fix humidity in the semantic class table and pin its value
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
c1ea6c75b1
Default the humidity reading to the uint16 unknown sentinel
All checks were successful
Build and test / build (pull_request) Successful in 1m38s
Build and test / release (pull_request) Has been skipped
3d3357a592
Drive the heater blower motor from the MOSFET on D2
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
ebd38a0524
Retry the blower edge when the lockout refuses it
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
341e4a40ca
Skip the blower edge while the motor is locked out
All checks were successful
Build and test / build (pull_request) Successful in 1m35s
Build and test / release (pull_request) Has been skipped
f7c56f6bee
Regulate the cabin by blower speed from a stored heater setpoint
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
fbac6d591b
Skip the idle blower write and test the setpoint edge cases
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
17e3b63e65
Run the blower controller every pass and integrate per reading
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
eca188558c
Pin the blower level holding between readings
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
083f0fb5f6
Apply the link-loss blower demand with failsafe origin
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
7d9de0af46
Note the Si7021 wiring in the heater node
All checks were successful
Build and test / build (pull_request) Successful in 1m56s
Build and test / release (pull_request) Has been skipped
1f46064102
Poll the Si7021 every 100 ms and report cabin readings on change
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
079194a00d
Claude left a comment

Two findings on 079194a.

Two findings on 079194a.
@ -92,0 +124,4 @@
const bool linkLost = heater.linkLost();
const can::Origin origin = linkLost ? can::Origin::Failsafe : can::Origin::Automation;
const uint16_t level = blowerControl.update(linkLost, heater.blowerOn(), cabinSensor.temperature(), heater.setpoint(), secondsSinceReading);
if (level != appliedBlowerLevel && blowerMotor.commandAllowed(origin, now)) {
Author
Collaborator

Dropping newReading from the guard changes behaviour beyond the task. Before, every new reading re-asserted the motor level. Now, if a human overrides the blower and the lockout expires, an unchanged level is never re-applied, so the manual value sticks until the level next changes. Either keep newReading || or confirm in the spec that automation only writes on level change.

Dropping `newReading` from the guard changes behaviour beyond the task. Before, every new reading re-asserted the motor level. Now, if a human overrides the blower and the lockout expires, an unchanged `level` is never re-applied, so the manual value sticks until the level next changes. Either keep `newReading ||` or confirm in the spec that automation only writes on level change.
@ -55,0 +73,4 @@
void test_a_one_unit_change_reports_at_threshold_one() { TEST_ASSERT_TRUE(reportsAfter(1, okAt(200), okAt(201))); }
void test_a_status_change_reports_at_threshold_one() { TEST_ASSERT_TRUE(reportsAfter(1, okAt(200), {0, can::units::Status::Fault, can::units::i16Fault})); }
Author
Collaborator

This test cannot fail if the status comparison is removed. 200 to i16Fault is a huge value change, so the threshold branch fires anyway. Use the same value with a different status, e.g. {0, Status::Fault, 200}.

This test cannot fail if the status comparison is removed. 200 to `i16Fault` is a huge value change, so the threshold branch fires anyway. Use the same value with a different status, e.g. `{0, Status::Fault, 200}`.
Test the status change on its own in the readout tests
Some checks failed
Build and test / release (pull_request) Has been cancelled
Build and test / build (pull_request) Has been cancelled
8596e44854
Limit blower writes to once a second and re-assert every 10 s
All checks were successful
Build and test / build (pull_request) Successful in 1m32s
Build and test / release (pull_request) Has been skipped
0ca99e367c
Claude left a comment

Nothing to act on in 0ca99e3.

Nothing to act on in 0ca99e3.
rob changed target branch from main to feat/heater-cabin-control 2026-10-10 14:21:52 +00:00
rob merged commit 1909942941 into feat/heater-cabin-control 2026-10-10 14:23:17 +00:00
rob deleted branch feat/heater-sensor-rate 2026-10-10 14:23:17 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/CampervanControlSystems!92
No description provided.