Skip to content

Add ADC resistor-ladder binary inputs (analog buttons)#35

Open
teancom wants to merge 3 commits into
OpenDisplay:mainfrom
teancom:feat/analog-buttons
Open

Add ADC resistor-ladder binary inputs (analog buttons)#35
teancom wants to merge 3 commits into
OpenDisplay:mainfrom
teancom:feat/analog-buttons

Conversation

@teancom
Copy link
Copy Markdown
Contributor

@teancom teancom commented Jun 2, 2026

I was thinking about what I could do with the button on my XTEINK X4 when I realized that they are analog and not supported just via a config change. So I added support for analog buttons, fully tested on my X4 (I'll have a py-opendisplay PR open to match shortly). I tried to make it a generic feature, not just for the X4, but am very happy to tweak things / restructure / whatever if this approach isn't copacetic.

Robot commit message:

BinaryInputs input_type == 2: several buttons share one ADC pin, distinguished by voltage. Polled (no edge interrupt) and reported through the same MSD button byte as digital buttons, so hosts decode one uniform contract. Wire layout (num_buttons, id_base, N+1 LE uint16 descending thresholds in reserved[]) is documented in structs.h.

device_control.cpp adds classify/register/poll with sample debounce, per-pin ADC attenuation, a static_assert guarding reserved[] capacity, and rejection of non-descending thresholds from any host. ESP32 only; NRF gets an empty poll stub.

Verified on an ESP32-C3 (XTEINK X4): valid ladders register and report presses; a malformed (non-descending) config is skipped at init with a diagnostic while a valid ladder beside it still works.

@teancom teancom requested a review from jonasniesner as a code owner June 2, 2026 16:53
@teancom
Copy link
Copy Markdown
Contributor Author

teancom commented Jun 2, 2026

Added another commit to do more validation / give feedback on invalid configs, based on testing I was doing with the py-opendisplay side of this.

@jonasniesner
Copy link
Copy Markdown
Member

Thank you for your work. BinaryInputs input_type == 2 is reserved for switches already, please use BinaryInputs input_type == 3. Also, please try to use the existing struct(we can use alternative names for each element) so we dont need to change py-opendisplay.

@jonasniesner jonasniesner marked this pull request as draft June 4, 2026 18:19
teancom and others added 3 commits June 4, 2026 12:04
BinaryInputs input_type == 2: several buttons share one ADC pin, distinguished by voltage. Polled (no edge interrupt) and reported through the same MSD button byte as digital buttons, so hosts decode one uniform contract. Wire layout (num_buttons, id_base, N+1 LE uint16 descending thresholds in reserved[]) is documented in structs.h.

device_control.cpp adds classify/register/poll with sample debounce, per-pin ADC attenuation, a static_assert guarding reserved[] capacity, and rejection of non-descending thresholds from any host. ESP32 only; NRF gets an empty poll stub.

Verified on an ESP32-C3 (XTEINK X4): valid ladders register and report presses; a malformed (non-descending) config is skipped at init with a diagnostic while a valid ladder beside it still works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
registerAdcLadder stored id_base raw and the poll path masked the
reported id with & 0x07. A host sending id_base + count > 8 (3-bit id
field) produced wrapped, colliding button ids that HA cannot
disambiguate — silently, with no feedback. The firmware is the
validation boundary for any host, not just py-opendisplay, so reject it.

Also give the existing count and byte_index guards the same diagnostic
the descending-threshold check already emits; previously they dropped a
malformed config silently.

Hardware-verified on XTEINK X4: a GPIO2 ladder with id_base=6, N=4 is
now rejected ("exceeds 3-bit id space ... skipping") while a valid GPIO1
ladder registers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per maintainer review on OpenDisplay#35: BinaryInputs input_type == 2 is reserved
for the host-side switch feature. Renumber the ADC resistor-ladder type
to 3 and document 2 = switch in the struct contract. Wire layout and the
reused reserved[] fields are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@teancom teancom force-pushed the feat/analog-buttons branch from 57002ec to 50da667 Compare June 4, 2026 19:05
@teancom teancom marked this pull request as ready for review June 4, 2026 19:06
@teancom
Copy link
Copy Markdown
Contributor Author

teancom commented Jun 4, 2026

I rebased to pull in the other changes in main, but I still didn't see where 2 was reserved, sorry. :-/ I added a comment and used 3, no problem, just more wondering what I'm missing. I updated the matching py-opendisplay PR as well, which I think is still necessary? OpenDisplay/py-opendisplay#78

The one PR I haven't opened yet is the one to the HA integration, where I was planning on exposing these buttons as entities. I'm not as familiar with HACS and whatnot, and didn't want to submit anything I haven't tested myself in the real world.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants