ESPHome external component for M5Stack E220 LoRa UART modules.
Features:
- Read and write E220 module configuration over UART (
C1/C0commands). - Fixed point-to-point messaging (
fixed_tx,target_addr,target_ch). - Protocol-level frames:
PING,ACK,MSG,MSG_ACK. - Link monitoring (heartbeat) with configurable
ping_intervalandack_timeout. - Online state as text sensors (
"true"/"false"), including per-target sensors. - RSSI reporting to text sensor (e.g.
-78 dBm) when RSSI byte is enabled inregister_features. - ESPHome automations:
on_receive,on_message,on_ping,on_ping_ack,on_ping_timeout,on_msg_ack. - Runtime API from lambdas:
id(e220).read_config(),id(e220).send("..."),id(e220).send_msg().
Example for a GitHub repository:
external_components:
- source: github://grandalos/m5stack-lora-e220
components: [lora_e220]uart:
id: uart_lora
baud_rate: 9600
tx_pin: GPIO02
rx_pin: GPIO01
stop_bits: 1
parity: NONElora_e220:
id: e220
uart_id: uart_loraid(required): component ID.uart_id(required): UART bus ID.read_on_boot(default:true): read E220 config after boot.response_timeout(default:500ms): timeout for config command response.config_text_sensor: publishes full config string (ADDR/SPED/OPT/CH/REG3/CRYPT).rx_log(default:true): debug logging of received lines.
config:
addr: 0x0001
register_sped: 0x60
register_option: 0x20
ch: 0x02
register_features: 0xC3
crypt: 0x0000
auto_write: trueaddr(0x0000..0xFFFF)register_sped(0x00..0xFF): raw SPED register byte (UART/parity/air-rate setup).register_option(0x00..0xFF): raw OPTION register byte.ch(0x00..0xFF)register_features(0x00..0xFF): raw REG3 register byte (e.g. RSSI byte/fixed transmission bits).crypt(0x0000..0xFFFF)auto_write(default:true)
Notes:
crypton E220 is write-only in practice, and read-back may return0x0000.- Verification logic handles this behavior (CRYPT mismatch is ignored when all other fields match and desired CRYPT is non-zero).
tx_message: periodic/on-boot payload.send_on_boot(default:false)fixed_tx(default:false): required for address/channel-targeted frames.target_addr(default:0x0000)target_ch(default:0x02)
ping_interval(default:5s)ack_timeout(default:500ms)ping_targets: list of nodes to ping (receiver role).online_text_sensor: aggregated online state (trueonly when all targets are online in receiver role).online_target_text_sensors: optional explicit per-target text sensors.
If online_target_text_sensors is omitted, sensors are auto-generated from ping_targets with names:
Online 0xCH 0xADDR
rssi_text_sensor: publishes packet RSSI in dBm text.- Requires RSSI byte in module settings (
register_featuresbit 7 set, e.g.0xC3).
Behavior:
- Receiver role (node with
ping_targets): RSSI from incomingACK. - Sender role (node without
ping_targets): RSSI from incomingPING.
Triggered on every received text line.
Args:
x(std::string)
Triggered for protocol message MSG|<src>|<payload>.
Args:
src(std::string, format0x0001)msg(std::string)
Triggered when PING is received.
Args:
src(std::string)
Triggered when a matching ACK to previously sent PING is received.
Args:
src(std::string)
Triggered when ACK is not received before ack_timeout.
Args:
src(std::string)
Triggered when MSG_ACK for a sent MSG is received.
Args:
src(std::string)
lora_e220:
id: e220
uart_id: uart_lora
read_on_boot: true
fixed_tx: true
target_addr: 0x0001
target_ch: 0x02
ping_interval: 5s
ack_timeout: 1500ms
rx_log: true
config:
addr: 0x0002
register_sped: 0x60
register_option: 0x20
ch: 0x02
register_features: 0xC3
crypt: 0x0000
auto_write: true
config_text_sensor:
name: "E220 Config"
online_text_sensor:
name: "Online"
rssi_text_sensor:
name: "LoRa RSSI"
ping_targets:
- addr: 0x0001
ch: 0x02
on_ping_ack:
then:
- logger.log:
format: "ACK OK from %s"
args: ['src.c_str()']
level: DEBUG
on_ping_timeout:
then:
- logger.log:
format: "ACK TIMEOUT from %s"
args: ['src.c_str()']
level: DEBUG
on_message:
then:
- logger.log:
format: "RX from %s: %s"
args: ['src.c_str()', 'msg.c_str()']
level: INFOlora_e220:
id: e220
uart_id: uart_lora
read_on_boot: true
fixed_tx: true
target_addr: 0x0002
target_ch: 0x02
ping_interval: 5s
ack_timeout: 500ms
rx_log: true
config:
addr: 0x0001
register_sped: 0x60
register_option: 0x20
ch: 0x02
register_features: 0xC3
crypt: 0x0000
auto_write: true
config_text_sensor:
name: "E220 Config"
online_text_sensor:
name: "Online"
rssi_text_sensor:
name: "LoRa RSSI"
on_ping:
then:
- logger.log:
format: "PING from %s"
args: ['src.c_str()']
level: DEBUG
on_msg_ack:
then:
- logger.log:
format: "MSG_ACK from %s"
args: ['src.c_str()']
level: INFOUse lambda calls:
button:
- platform: template
name: "Read E220 config"
on_press:
- lambda: id(e220).read_config();
- platform: template
name: "Send custom MSG"
on_press:
- lambda: id(e220).send("HELLO WORLD");send("...") sends a protocol frame MSG|<self_addr>|<self_ch>|<payload> and waits for MSG_ACK.
- Heartbeat:
PING|<seq_dec>|<src_addr_hex4>|<src_ch_hex2>ACK|<src_addr_hex4>|<seq_dec>
- User message:
MSG|<src_addr_hex4>|<src_ch_hex2>|<payload>- Legacy receive compatibility:
MSG|<src_addr_hex4>|<payload>
- Legacy receive compatibility:
MSG_ACK|<src_addr_hex4>
- Receiver role: node with non-empty
ping_targets.- Sends periodic
PING. - Marks each target online/offline based on matching
ACK.
- Sends periodic
- Sender role: node without
ping_targets.- Answers every
PINGwithACK. - Sets own
online_text_sensortotruewhenPINGis seen, and back tofalseafter timeout. - Answers every incoming
MSGwithMSG_ACK.
- Answers every
If your board uses physical M0/M1 switches for mode selection, keep read/write config flow as-is and ensure module mode matches the command being used.