ESPHome firmware and a custom PCB to talk to the keypad bus of an Arrowhead Alarm Products EliteControl ESL-2 panel, so it can be integrated into Home Assistant through the crow_alarm_panel external component. It builds on my crow_alarm_panel implementation, which handles the AAP keypad bus protocol.
| PCB | PCB with Atom Lite | Side View |
|---|---|---|
![]() |
![]() |
![]() |
The ESL-2 keypad bus is a shared clock/data bus that regular AAP keypads talk to. This project adds an M5Stack Atom Lite or M5Stack AtomS3 Lite to the bus as an extra keypad, so it can read zone/arm state and issue arm/disarm/output commands over Wi-Fi, without touching the panel's existing keypads.
The panel supplies +12V to power the bus, but the clock/data lines themselves run at 5V, while the AtomS3 Lite module's GPIOs are 3.3V. The custom PCB sits between the two:
- An
AP63205WU-7buck converter steps the bus's +12V supply down to +5V, used as the CLK/DATA logic-high reference and to power the module. - Two
BSS138MOSFETs form bidirectional logic-level shifters for the CLK and DATA lines, translating between the bus side (5V) and the module side (3.3V). - The PCB's pin header/Grove-compatible footprint matches the Atom Lite, so it plugs straight onto the PCB with no jumper wires — only the bus connection (panel to PCB) needs wiring, covered in Connection diagram.
The header footprint matches the M5Stack ATOM family in general, so the original M5Stack Atom Lite (ESP32) works as a drop-in alternative to the AtomS3 Lite — it needs the plain esp32 variant instead of esp32s3, plus different clock_pin/data_pin values in the YAML, since the Atom Lite exposes a different set of GPIOs than the AtomS3 Lite.
There is no dedicated address pin/DIP switch — the keypad address used on the bus is set entirely in the ESPHome YAML (see Configuration below).
All manufacturing files (schematic, gerbers, BOM, pick-and-place, and the EasyEDA source project) are in docs/pcb, ready to order from a board house such as JLCPCB.
Files required to order boards from JLCPCB:
The top side mounts the AtomS3 Lite and carries the buck converter and level shifters. The bottom side breaks out the bus connection, silkscreened with the wire colors used on the ESL-2 keypad harness:
| Silkscreen | Signal | Wire color |
|---|---|---|
| +12V | Bus power | Red |
| GND | Ground | Black |
| CLK | Clock | Yellow |
| DAT | Data | Blue |
| Top | Bottom |
|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
Two options are provided for connecting to the bus:
CN1— a 4-pin screw terminal (Phoenix Contact 1861959), for connecting bare wires directly.CN2— a 5-pin pluggable connector (Molex 22035055), for a crimped/pre-made harness — compatible with AAP's Keypad Bus Connection Cables. ARR14, ARR15
+--------------------+ +-----------------+
| ESL-2 Panel | | Custom PCB |
| | | |
| +12V (red) o---------------------o +12V |
| GND (black) o---------------------o GND |
| CLK (yellow) o---------------------o CLK |
| DAT (blue) o---------------------o DAT |
+--------------------+ +-----------------+Two example YAML configs are provided, both using the same PCB and wiring. Copy the one you need (dropping .example) and fill in your own zone/keypad names — the copy is gitignored so your real panel layout doesn't end up in the repo:
aap_esl_keypad_interface.example.yaml→aap_esl_keypad_interface.yaml— registers as an active keypad on the bus (address: 5) and exposes arm/disarm/stay, zone sensors, and switch outputs to Home Assistant.aap_esl_keypad_monitor.example.yaml→aap_esl_keypad_monitor.yaml— passive/listen-only variant (noaddressclaimed on the bus), useful for monitoring bus traffic without presenting as a keypad.
Both use clock_pin: GPIO7, data_pin: GPIO5 for the AtomS3 Lite (GPIO23/GPIO22 instead if using an original Atom Lite, along with esp32 instead of esp32s3) with the crow_alarm_panel external component. For everything else — keypads, zones, passive monitor mode, outputs, bypass switches, the full option reference — see the component's own README, which documents the schema in more detail than duplicated here.
Create a secrets.yaml (gitignored) alongside your copy with:
wifi_ssid: "..."
wifi_password: "..."
fallback_hotspot_password: "..."
alarm_code: 1234
api_encryption_key: "..."Two ways to get this onto the device:
Local CLI — this project uses uv to manage the ESPHome dependency (see pyproject.toml). Install uv if you don't have it, then:
uv sync
uv run esphome config aap_esl_keypad_interface.yaml # to validate configuration
uv run esphome run aap_esl_keypad_interface.yamlESPHome Builder (Home Assistant add-on) — no local ESPHome install needed:
- In Home Assistant, open the ESPHome dashboard and
+ NEW DEVICEto create a device and flash the stub firmware over USB (this sets upwifi,api, andotafor you, with the encryption key stored by the add-on). - Once the device shows up and adopts,
EDITit and replace the generated YAML'sesp32,logger,external_components, andcrow_alarm_panel/text_sensor/alarm_control_panel/button/switchblocks with the equivalent sections fromaap_esl_keypad_interface.example.yaml, filled in with your own zones/keypads. - Leave the wizard-generated
wifi/api/otasections as-is — no need to bring insecrets.yamlsince the add-on manages Wi-Fi credentials and the encryption key itself. INSTALLto push the update over-the-air.
crow_alarm_panel was built on the work done by jallier and jesserockz









