ESP32-C3 + weatherproof reed on OmnissiahsReach WiFi (no Zigbee mesh) for true stowed/not-stowed state: BOM, wiring, ESPHome config, HA/bridge integration, optional truthful template cover, and the Option-2 (wire-to-CAN-node) note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.3 KiB
Awning "Closed" Sensor — Spec (Option 1: standalone WiFi reed node)
Goal: give the campsite awning a true stowed/not-stowed state instead of the
assumed_state the CAN cover currently reports. A magnetic reed switch at the
fully-retracted position, read by a small ESP running ESPHome over the existing
OmnissiahsReach WiFi — no Zigbee mesh, no coordinator, no new ecosystem.
This is "Option 1" (standalone node). Option 2 (wire the reed back to a spare GPIO
on the CAN node 192.168.69.18 so the cover entity itself becomes truthful) is the
more-integrated alternative but needs a 2-wire run from the awning to the control
panel; it's noted at the end. Pairs with the current-sensing auto-retract already
in esphome/onecontrol-canbus.yaml (README "awning" section): current does the
dynamic stop during retract, the reed gives persistent truth afterward.
What it delivers (and what it doesn't)
- Reliable CLOSED (stowed) detection. Awnings extend to an arbitrary spot, so there's no meaningful "fully open" endpoint — but the fully-retracted position is hard and repeatable. That's the state that matters (don't drive off / don't let it flap): a single reed there answers stowed vs. not.
- It does not measure extension %. If you ever want an "opening/closing" animation you already get that from the CAN motion byte; the reed just pins down the resting truth.
Bill of materials (~$15–20)
| Part | Notes |
|---|---|
| ESP32-C3 SuperMini (or Wemos D1 mini / ESP8266) | Tiny, WiFi, 3.3 V logic. C3 recommended (native ESP-IDF, cheap, plenty of GPIO). |
| Weatherproof reed switch | RV-compartment / garage-door style, potted + leaded, normally-open. Get a wide-gap one (pull-in ≥ 15–20 mm) so alignment is forgiving. |
| Bar/block magnet | Match/exceed the reed's rated gap. Rare-earth block for margin. |
| 12 V → 5 V buck (MP1584 / mini360 or an automotive USB buck) | Tap the awning motor's 12 V; always-on beats battery/deep-sleep for instant state and no maintenance. |
| Small IP65 enclosure + gland | Mount the ESP + buck out of the weather. |
| Fused 12 V tap (inline 1 A) | Protect the tap off the motor harness. |
Wiring
awning 12V harness --[inline 1A fuse]--> buck IN+ buck OUT+ (5V) --> ESP 5V
chassis GND -----> buck IN- ----buck OUT- ------> ESP GND
reed leg A --> ESP GPIO4
reed leg B --> ESP GND
- Reed to GND with the internal pull-up enabled: pin idles HIGH (not stowed), goes LOW when the magnet is present (stowed). The ESPHome config below inverts that so the sensor reads ON = stowed.
- No external resistor needed. An optional 0.1 µF across the reed helps debounce,
but the software
delayed_on/offbelow is enough. - Avoid the C3 strapping pins (GPIO2, 8, 9); GPIO4 is safe. GPIO8 has the onboard LED if you want a status blink.
Mounting
- Magnet on the moving part (the lead rail / roller endcap that seats when stowed); reed on the fixed part (mounting rail or a bracket on the coach).
- Aim for the magnet to land within the reed's pull-in gap when the awning is pulled in tight — the same "closed" position the current-stall stop lands on. Expect a test-fit: mark where the rail seats, mount, confirm the sensor flips.
- Outdoor: use the potted reed, point the gland down, silicone the entry, and strain-relief the lead so awning motion doesn't fatigue it.
ESPHome config
New device awning-sensor (mirror the conventions in onecontrol-canbus.yaml /
gazebo-fan-proxy.yaml: secrets.yaml for WiFi + API key, OTA after first USB
flash, fallback AP + captive portal). DHCP on OmnissiahsReach.
substitutions:
name: awning-sensor
friendly_name: Awning Sensor
esphome:
name: ${name}
friendly_name: ${friendly_name}
esp32:
board: esp32-c3-devkitm-1
framework:
type: esp-idf
logger:
api:
encryption:
key: !secret api_key
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid # OmnissiahsReach
password: !secret wifi_password
ap:
ssid: "Awning-Sensor Fallback"
password: !secret fallback_ap_password
captive_portal:
binary_sensor:
- platform: gpio
name: "Awning Stowed"
id: awning_stowed
pin:
number: GPIO4
mode:
input: true
pullup: true
inverted: true # reed->GND: magnet present = LOW = ON (stowed)
filters:
- delay_on: 200ms # debounce the rail seating
- delay_off: 200ms
# ON = awning pulled in tight (stowed / closed)
# OFF = not stowed (extended, or mid-travel)
secrets.yaml needs wifi_ssid, wifi_password, fallback_ap_password,
api_key (generate with esphome). First flash over USB (esphome run ... ),
thereafter OTA — same as the other campsite nodes.
HA integration
The reed is a separate device from the CAN node, so it lands as its own entity:
- Campsite Pi auto-discovers it via the ESPHome integration →
binary_sensor.awning_sensor_awning_stowed(rename tobinary_sensor.awning_stowed). ON = stowed. - Bridge to home HA — add to
/config/packages/mqtt_bridge.yaml(repo:canbus/ha/mqtt_bridge_onecontrol.yaml) next to the other campsite entities: a discovery block (homeassistant/binary_sensor/campsite/awning_stowed/config, devicecampsite_onecontrol,device_class: "opening"reads on=open/off=closed, or leave classless for a plain stowed/clear), plus a state-forward trigger onbinary_sensor.awning_stowed→campsite/binary_sensor/awning_stowed/state. Appears on home asbinary_sensor.campsite_onecontrol_awning_stowed. - Add the tile to home HA Overview → Camper → OneControl section.
Optional: make the cover truthful (template cover)
Right now cover.onecontrol_can_awning is assumed_state. With the reed you can
wrap it in a template cover on the Pi whose closed state is the real reed, while
commands still hit the CAN cover:
cover:
- platform: template
covers:
awning_true:
friendly_name: "Awning"
device_class: awning
value_template: "{{ 'closed' if is_state('binary_sensor.awning_stowed','on') else 'open' }}"
close_cover:
- action: cover.close_cover # -> CAN cover auto-retract
target: { entity_id: cover.onecontrol_can_awning }
stop_cover:
- action: cover.stop_cover
target: { entity_id: cover.onecontrol_can_awning }
Bridge cover.awning_true to home instead of the raw CAN cover, and you get a
cover that shows genuine open/closed. (Keep payload_open disabled as today.)
Automations this unlocks
- Left-it-out alarm: awning
not stowed+ (leaving geofence / wind gust > X / rain) → Discord ping, or auto-firecover.close_cover. - Confirm the retract actually seated: after an auto-retract, if the current stall fired but the reed is still OFF a few seconds later → alert (mis-seat).
Option-1 limitation: because the reed is on a separate device from the CAN node, the auto-retract's stall gate can't consume it on-device — any reed↔retract logic is a cross-device HA automation, not firmware. If you want the ESP to stop the motor on the reed itself (belt-and-suspenders with the current stall), that's Option 2 (wire the reed to a spare GPIO on the CAN node
192.168.69.18; the cover entity then reads it directly and becomes truthful with no template cover).
Touchpoints checklist (campsite ESPHome node)
secrets.yaml(WiFi = OmnissiahsReach, API key, fallback AP pw)- USB flash once, confirm on
OmnissiahsReach(DHCP192.168.69.x); OTA after - Adopt in campsite Pi HA (ESPHome integration), rename entity
- Mount reed + magnet, verify it flips exactly at the stowed position
mqtt_bridge.yaml: discovery + state-forward → home HA; add dashboard tile- (optional) template cover
awning_true; bridge it instead of the raw cover - (optional) automations: left-out alarm, retract-seat confirmation
Validation
- USB-flash, join WiFi, adopt in HA. Toggle by hand with a magnet →
awning_stowedflips ON/OFF with the 200 ms debounce. - Mount, retract via the dashboard (auto-retract): at full stow the reed should go ON right around when the current-stall stop fires.
- Extend at the OEM wall switch → reed OFF. Confirm home HA mirrors it.