Repository navigation
Night mode: a bounded sensing suspension (protocol v4) - #14
Merged
Merged
Conversation
The 48 IR emitters are the counter's power budget: 24 series pairs behind
22 ohm ballast, all lit together for the settle+read window of every 5 ms
poll — roughly 0.5-1.0 A peak and, at the pulsed sampler's ~35% duty, an
average an order of magnitude above the ~18 mA the C6 and the three
expanders draw between them. European honey bees are diurnal (flight needs
light, and stops below roughly 10 C regardless), so every night that draw
buys nothing.
Add a control characteristic (8e8b0103) that HiveHub writes a suspension
DURATION to. The counter has no RTC, no NVS and no network, and this change
deliberately does not give it any: it is told how long to stay quiet, never
when. Everything that matters falls out of that.
* It expires. A HiveHub that dies cannot leave a counter blind.
* It is capped at MAX_IDLE_SECONDS (1 h), several times HiveHub's upload
cycle, so HiveHub re-arms per cycle and nothing accumulates. An
over-long request is clamped, not refused — refusing would leave the
emitters running all night because one field was too large.
* It is never persisted, so any reset comes back counting.
* It is refused during an OTA, which already parks the emitters itself.
Not deep sleep, which was the obvious implementation. Parking the emitters
captures >90% of the saving; sleeping for the rest costs the measurement
read (every night row would carry ok=false), the OTA relay path that is
specifically recommended for night use, the ability to cancel, the RAM
totals, and a truthful uptime_s — and the C6's deep-sleep timer runs off the
temperature-dependent internal RC oscillator, drifting minutes to half an
hour over a 8-12 h night.
The deadline arithmetic is a pure header (idle_state.h) with host tests
covering the millis() rollover and the "HiveHub stopped re-arming" case,
neither reproducible on a hive. Gate state machines are reset on both edges
so a half-finished pairing cannot span the gap and fabricate a crossing.
Protocol v4 reports it: idle_s counts the suspension down and status bit
0x80 marks it. Without them a night of zeros is indistinguishable from a
counter whose emitter FETs have died. The addition is purely additive, so
HiveHub's key-skipping parser reads v4 documents unchanged.
Also fixes documentation that has gone stale around this area: README's
power budget still claimed the emitters run continuously and could be
switched with a CMD_LEDS_OFF that went away with the wired link, and it
understated the emitter draw that is the whole reason this feature exists.
docs/ble-mode.md's "deep sleep is incompatible" line now says what is
actually true.
Adds a CI workflow — the repo had none, so neither the host tests nor the
ESP32 build were checked anywhere but a laptop.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016gFpezSuXW3sHMd1xaSYnd
The new CI workflow's first run failed with "This board doesn't support arduino framework!". Not a regression from night mode — a latent breakage this repo had no CI to notice. `platform = espressif32` was unpinned, so it resolves to the official Espressif platform, which at 7.0.1 still bundles Arduino core 2.0.17. That core has no ESP32-C6 Arduino support; C6 support arrived in Arduino-ESP32 3.x. Anyone running the documented `pio run` on a fresh install has been getting that error rather than a firmware image. Pin the pioarduino release HiveHub already builds this same chip with (55.03.39 = Arduino-ESP32 3.3.9 / ESP-IDF 5.5.4), and pin the libraries exactly while here — including NimBLE at 2.5.1. The caret range let it float, and the 2.x releases below 2.5.0 each trip a different C6 failure (a libbt.a link error, an init crash on core 3.3.7+, a scan crash on the C6), so "some 2.x" is not a safe thing to ask for on this board. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016gFpezSuXW3sHMd1xaSYnd
Companion to the HiveHub fix. These host tests build the pure headers against a bare compiler, where Arduino's few hundred all-caps macros do not exist — so a constant in the shared counter_protocol.h that collides with one of them passes every check here and fails only in the firmware build. HiveHub hit exactly that with an enumerator named DISABLED meeting esp32-hal-gpio.h's `#define DISABLED 0x00`. Define the macros that actually bite before the include, so the collision surfaces in seconds on a host compiler instead of partway through a firmware build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016gFpezSuXW3sHMd1xaSYnd
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds night mode to the counter, plus the documentation fixes around it.
Pairs with MacNite/HiveHub#167, which owns the schedule. Deploy order below.
Why
The 48 IR emitters are this board's power budget: 24 series pairs behind 22 Ω ballast, all lit together for the settle+read window of every 5 ms poll. That is roughly 0.5–1.0 A peak and, at the pulsed sampler's ~35 % duty, an average an order of magnitude above the ~18 mA the C6 and the three expanders draw between them.
European honey bees are diurnal — flight requires light, and stops below roughly 10 °C regardless — so overnight that current buys nothing.
What the counter gained
One control characteristic (
8e8b0103) that HiveHub writes a suspension duration to. The counter has no RTC, no NVS and no network, and this deliberately does not give it any: it is told how long to stay quiet, never when. Everything that matters falls out of that.MAX_IDLE_SECONDS(1 h), several times HiveHub's upload cycle, so HiveHub re-arms per cycle and drift never accumulates. An over-long request is clamped, not refused — refusing would leave the emitters running all night because one field was too large.Gate state machines are reset on both edges, so a half-finished pairing cannot span the gap and fabricate a crossing.
Why not deep sleep
Deep sleep was the obvious implementation and is the wrong one. Parking the emitters captures >90 % of the saving; sleeping for the rest costs:
bee_counter.ok=false;uptime_s— every morning would look like an unexplained reboot;Protocol v4
idle_scounts the suspension down and status bit0x80marks it. Without them a night of zeros is indistinguishable from a counter whose emitter FETs have died. The change is purely additive, so HiveHub's key-skipping parser reads v4 documents unchanged.Loose ends fixed while in the area
README.md§6 claimed the emitters run continuously and could be switched with aCMD_LEDS_OFFthat went away with the wired link. It also understated the emitter draw — the whole justification for this feature.docs/ble-mode.md's "deep sleep is incompatible with uninterrupted counting" now says what is actually true.SET_IDLEis a new way to deny counting for anyone in radio range. Strictly the lesser of the two exposures — it expires, changes nothing persistent, and anyone able to do it could already replace the firmware — but named rather than left to be discovered.Tests
include/idle_state.his a pure header covering themillis()rollover and the "HiveHub stopped re-arming" case, neither of which is reproducible on a hive.test_measurement_jsonpins the v4 document and asserts bit0x80does not collide.This repo had no CI, so neither the host tests nor the ESP32 build were checked anywhere but a laptop. Added a workflow that runs both, plus the
-DIR_DEBUGbench console, which has no environment of its own and would otherwise go stale unnoticed.Verified locally: all three host suites pass. The ESP32 build is checked by the new CI workflow only — the sandbox this was written in blocks the PlatformIO registry, so the firmware has not been compiled here.
Deploy order
HiveHub's tolerant parser must be deployed before any counter emitting
fw:4. In practice the parser already handled it (unknown keys are skipped), but HiveHub is what decides when to suspend, so it needs to understandidle_sfirst.Generated by Claude Code