ESPHome external component for Wi-Fi HaLow (IEEE 802.11ah) using the Morse Micro MM6108 SoC. Wi-Fi HaLow operates in sub-1 GHz spectrum (902-928 MHz in North America), offering up to 1 km range with low power consumption -- ideal for IoT devices that need to reach beyond standard Wi-Fi range.
This component provides a drop-in network interface for ESPHome, replacing wifi: with halow:. It works like the built-in W5500/Ethernet SPI support: configure your pins, SSID, and password in YAML, and get full Home Assistant connectivity over HaLow.
HaLow uses standard IP networking — TCP, UDP, DHCP, mDNS, MQTT, and the ESPHome API all work natively. No protocol translation, no gateway bridges, no TTN accounts. Your HaLow device is just another IP device on your network that Home Assistant sees directly. OTA updates work over the same connection. Bidirectional control is instant (7ms latency). One AP supports 1000 devices with managed scheduling.
LoRa still wins on raw range (10-15km vs ~1km) and power efficiency for simple one-way sensors, but for anything that needs reliable bidirectional communication, HaLow is dramatically cleaner.
| Module | Form Factor | Notes |
|---|---|---|
| Seeed XIAO HaLow Hat (WIO-WM6180) | XIAO hat | Plug-and-play with XIAO ESP32-S3 |
| Quectel FGH100M-H | Module | Same MM6108 SoC, different form factor |
| Board | Status | Notes |
|---|---|---|
| Seeed XIAO ESP32-S3 | Tested | Default pin mapping matches HaLow Hat |
| Other ESP32-S3 boards | Should work | Configure SPI pins in YAML |
| ESP32-C6 | Untested | Supported by MM-IoT-SDK |
| AP | Status | Notes |
|---|---|---|
| GL-iNet HaLowLink 2 | Tested | MM8108-based, WPA3-SAE, bridges to 2.4 GHz |
| Morse Micro reference APs | Should work | Any 802.11ah AP |
- ESPHome 2024.2+ with ESP-IDF framework support
- Git (for automatic SDK download on first compile)
The Morse Micro MM-IoT-SDK is downloaded automatically on first compile. No manual setup needed. No patches required.
esphome:
name: halow-sensor
friendly_name: HaLow Sensor
esp32:
board: seeed_xiao_esp32s3
framework:
type: esp-idf
version: recommended
external_components:
- source: github://sweitzja/esphome-halow
components: [halow]
halow:
type: MM6108 # MM6108 (station) or MM8108 (station/AP)
mode: STATION # STATION or AP (AP requires MM8108)
ssid: "my-halow-ap"
password: "my-password"
country_code: "US"
security: SAE # SAE (WPA3), OWE, or OPEN
# sub_bands: false # false=max throughput (8MHz), true=max range (1-2MHz)
# manual_ip: # Optional static IP
# static_ip: 192.168.12.100
# gateway: 192.168.12.1
# subnet: 255.255.255.0
# Pins default to XIAO HaLow Hat. Override for other boards:
# clk_pin: 7 / mosi_pin: 9 / miso_pin: 8 / cs_pin: 4
# irq_pin: 3 / reset_pin: 1 / wake_pin: 2 / busy_pin: 5
# Signal sensors
rssi:
name: "HaLow RSSI"
link_quality:
name: "HaLow Link Quality"
# Radio adaptation sensors
mcs:
name: "HaLow MCS"
bandwidth:
name: "HaLow Bandwidth"
tx_success_rate:
name: "HaLow TX Success Rate"
# Traffic sensors
tx_pps:
name: "HaLow TX Rate"
rx_pps:
name: "HaLow RX Rate"
tx_kbps:
name: "HaLow TX Throughput"
rx_kbps:
name: "HaLow RX Throughput"
# Diagnostic sensors
noise_floor:
name: "HaLow Noise Floor"
tx_frames_dropped:
name: "HaLow TX Dropped"
rx_frames_dropped:
name: "HaLow RX Dropped"
ccmp_failures:
name: "HaLow CCMP Failures"
hw_restarts:
name: "HaLow HW Restarts"
# Channel scan (button + results)
scan_channel:
name: "HaLow Channel Scan"
scan_results:
name: "HaLow Scan Results"
# Network info sensors
ip_address:
name: "HaLow IP"
gateway_address:
name: "HaLow Gateway"
subnet_mask:
name: "HaLow Subnet"
connected_ssid:
name: "HaLow SSID"
bssid:
name: "HaLow BSSID"
mac_address:
name: "HaLow MAC"
firmware_version:
name: "HaLow FW Version"
api:
logger:
ota:
platform: esphome
sensor:
- platform: uptime
name: UptimeThe MM6108 is a dedicated Wi-Fi HaLow SoC that communicates with the ESP32 host over SPI using an SDIO-over-SPI protocol. Unlike a simple peripheral, the MM6108 runs its own 802.11ah MAC/PHY stack and requires firmware loaded at every boot.
SPI (40 MHz)
ESP32-S3 ◄────────────────────────► MM6108 (HaLow SoC)
│ │
├─ ESPHome ├─ 802.11ah MAC/PHY
├─ LWIP (via mmipal) ├─ WPA3-SAE
├─ Home Assistant API ├─ Sub-1 GHz radio
└─ OTA updates └─ Up to 1 km range
- mmhal_init() -- Initialize SPI bus, GPIO pins (reset, wake, busy, IRQ)
- mmwlan_init() -- Initialize WLAN subsystem
- mmwlan_boot() -- Load
mm6108.mbinfirmware +bcf_mf16858.mbinboard config over SPI - mmipal_init() -- Initialize LWIP network stack with DHCP
- mmwlan_sta_enable() -- Start WPA3-SAE association with the AP
- Link comes up, DHCP assigns IP address. Full setup takes ~10s from cold boot.
Default pin mapping for the Seeed XIAO HaLow Hat:
| Function | GPIO | XIAO Pin | Direction |
|---|---|---|---|
| SPI SCK | 7 | D8 | Output |
| SPI MISO | 8 | D9 | Input |
| SPI MOSI | 9 | D10 | Output |
| SPI CS | 4 | D4 | Output |
| SPI IRQ | 3 | D3 | Input (data-ready interrupt) |
| RESET_N | 1 | D1 | Output (active low) |
| WAKE | 2 | D2 | Output |
| BUSY | 5 | D5 | Input (rising edge) |
Source: Seeed mm-iot-esp32 Kconfig
The MM6108 has no persistent firmware. Two binary files are embedded in the ESP32 flash and loaded over SPI at every boot:
| File | Purpose | Size | Source |
|---|---|---|---|
mm6108.mbin |
MM6108 SoC firmware (v1.17.6) | ~400 KB | MM-IoT-SDK framework/morsefirmware/ |
bcf_mf16858.mbin |
Board config for FGH100M-H | ~2 KB | MM-IoT-SDK framework/morsefirmware/mm6108/bcfs/ |
Important: Firmware and BCF must be from the same SDK version.
All testing performed with XIAO ESP32-S3 + XIAO HaLow Hat + GL-iNet HaLowLink 2:
| Test | Result |
|---|---|
| SPI communication (SDIO registers) | Pass -- CCCR and FBR registers readable |
| MM6108 firmware boot | Pass -- FW v1.17.6, morselib v2.10.4-esp32, chip ID 0x306 |
| HaLow network scan | Pass -- Found AP at -41 dBm, 8 MHz BW |
| WPA3-SAE authentication | Pass -- Link up in ~10 seconds |
| DHCP IP acquisition | Pass -- 192.168.12.164 from gateway 192.168.12.1 |
| LWIP TCP/UDP stack | Pass -- iperf server operational |
| ESPHome compile (IDF 5.5.2) | Pass -- 1.08MB firmware, 59% flash usage |
| ESPHome full boot with sensors | Pass -- RSSI (-35 dBm), IP, MAC, BSSID, FW version all reporting |
| mDNS discovery | Pass -- halow-test.local registered on HaLow interface |
| LAN ping (power save off) | Pass -- 10/10, 0% loss, 7-11ms latency |
| ESPHome API from LAN | Pass -- TCP connect to port 6053 succeeded |
| 20-min soak test | Pass -- Heap stable (zero delta), 0 errors |
| Range walk reconnect | Pass -- 16s recovery via FreeRTOS timer, no crash |
| AP power cycle reconnect | Pass -- 53s recovery (AP boot time) |
| OTA over HaLow | Pass -- 1.05MB in 10s, 3 consecutive OTA cycles clean |
The HaLowLink 2 must be in AP mode and configured for bridging so that Home Assistant and other LAN devices can reach HaLow devices. Without this setup, the ESP32 will connect to the HaLow AP but will not be reachable from your network — HA won't be able to discover or communicate with the device.
- Set the HaLowLink 2 to AP mode using the mode button or web UI
- Connect your LAN cable to the HaLowLink 2's WAN port (not LAN — the wizard bridges WAN↔HaLow)
- Access the web UI at
https://<halowlink-ip>(check your router's DHCP leases for the IP) - Go to Wizard and select "HaLow devices get IP on your existing router's network"
- SSH in and disable masquerade:
ssh root@<halowlink-ip> # password is on the device label uci set firewall.wlan.masq=0 uci commit firewall /etc/init.d/firewall restart
Important: Step 5 is critical. Even in bridge mode, the HaLowLink 2 enables NAT (masquerade) on the HaLow interface by default. Without disabling it, HaLow devices can reach the internet but nothing on your LAN can reach them — no ping, no API, no OTA, no HA discovery.
- Power save disabled — The MM6108 BUSY pin has a firmware bug (confirmed by Morse Micro). Power save is disabled to ensure reliable bidirectional communication. Monitor firmware releases for a fix.
- TWT not yet implemented — Requires working BUSY pin for battery operation. A GPIO-based workaround exists but is not integrated.
| Option | Type | Default | Description |
|---|---|---|---|
type |
enum | MM6108 |
Chip type: MM6108 or MM8108 |
mode |
enum | STATION |
Operating mode: STATION or AP (AP requires MM8108) |
ssid |
string | required | HaLow AP SSID (station mode) |
password |
string | required | WPA3-SAE passphrase |
security |
enum | SAE |
Security type: SAE (WPA3), OWE, or OPEN |
bcf_file |
string | bcf_mf16858.mbin |
Board configuration file |
sub_bands |
bool | false |
false = 8 MHz max throughput, true = 1-2 MHz max range |
manual_ip |
schema | (DHCP) | Static IP config (static_ip, gateway, subnet, dns1, dns2) |
country_code |
string | "US" |
Regulatory domain (US, AU, EU, JP, etc.) |
clk_pin |
int | 7 |
SPI clock GPIO |
mosi_pin |
int | 9 |
SPI MOSI GPIO |
miso_pin |
int | 8 |
SPI MISO GPIO |
cs_pin |
int | 4 |
SPI chip select GPIO |
irq_pin |
int | 3 |
SPI interrupt GPIO |
reset_pin |
int | 1 |
Module reset GPIO (active low) |
wake_pin |
int | 2 |
Module wake GPIO |
busy_pin |
int | 5 |
Module busy GPIO |
All sensors are optional. Add only the ones you need to your YAML. All sensors
accept the standard ESPHome sensor configuration options (name, id, filters,
on_value, etc.). Sensors update every 10 seconds while connected.
Type: Numeric sensor (dBm) | Device class: signal_strength
Received Signal Strength Indicator — measures signal power from the AP.
| RSSI Range | Signal Quality | Typical Scenario |
|---|---|---|
| -20 to -40 dBm | Excellent | Same room as AP, antenna tight |
| -40 to -60 dBm | Good | Adjacent room, 10-50m |
| -60 to -75 dBm | Fair | Through walls, 50-200m |
| -75 to -85 dBm | Weak | Edge of range, packet loss likely |
| Below -85 dBm | Critical | Connection will drop |
rssi:
name: "HaLow RSSI"Type: Text sensor
Human-friendly summary of the radio link, derived from MCS and TX success rate. This is the single sensor most users should monitor — it combines the raw radio metrics into a plain-English assessment.
| Value | MCS | TX Success Rate | What it means |
|---|---|---|---|
| Excellent | 5-7 | >95% | Full speed, strong signal. Best throughput. |
| Good | 3-4 | >90% | Moderate speed, reliable. Normal operation. |
| Fair | 1-2 | >80% | Reduced speed but usable. Some retransmissions. |
| Poor | 0 | >50% | Minimum speed, marginal link. Expect latency spikes. |
| Critical | any | <50% | Severe packet loss. Connection at risk of dropping. |
link_quality:
name: "HaLow Link Quality"The HaLow radio automatically adapts its modulation and coding in real time based on channel conditions — similar to how LTE switches between bars of signal. These sensors let you observe the adaptation.
Type: Numeric sensor (0-7)
Modulation and Coding Scheme — how the radio trades speed for reliability. The equivalent of LoRa's spreading factor, but changed automatically per-frame.
| MCS | Modulation | Approx Data Rate (8 MHz) | Use Case |
|---|---|---|---|
| 7 | 64-QAM | 32.5 Mbps | Strong signal, max throughput |
| 6 | 64-QAM | 26 Mbps | Strong signal |
| 5 | 16-QAM | 19.5 Mbps | Good signal |
| 4 | 16-QAM | 13 Mbps | Moderate signal |
| 3 | QPSK | 9.75 Mbps | Moderate signal |
| 2 | QPSK | 6.5 Mbps | Weak signal |
| 1 | BPSK | 3.25 Mbps | Weak signal |
| 0 | BPSK | 1.625 Mbps | Weakest signal, max range |
The rate controller probes upward conservatively (one MCS step every ~10s) and drops instantly on packet loss. During testing, we observed MCS climb from 0 back to 7 over approximately 90 seconds after restoring a good antenna connection.
mcs:
name: "HaLow MCS"Type: Numeric sensor (MHz)
Channel width the radio is currently using. With sub_bands: false (default), this
stays at the full operating bandwidth negotiated with the AP (typically 8 MHz).
With sub_bands: true, the radio may drop to 1, 2, or 4 MHz for better sensitivity
at extreme range.
| Bandwidth | Relative Throughput | Relative Range |
|---|---|---|
| 8 MHz | Highest | Shortest |
| 4 MHz | ~50% | ~1.4x |
| 2 MHz | ~25% | ~2x |
| 1 MHz | ~12.5% | ~2.8x |
bandwidth:
name: "HaLow Bandwidth"Type: Numeric sensor (%)
Percentage of frames delivered to the AP without retransmission in the last update period (10 seconds). This is calculated from delta-based tracking of the MM6108's rate control statistics — it shows current performance, not lifetime average.
| Success Rate | Meaning |
|---|---|
| 95-100% | Clean channel, no interference |
| 80-95% | Some retransmissions, still reliable |
| 50-80% | Significant packet loss, link degrading |
| Below 50% | Severe loss, connection at risk |
tx_success_rate:
name: "HaLow TX Success Rate"Type: Numeric sensor (packets per second)
Transmit and receive packet rates over the HaLow link. Useful for HA dashboard charts showing traffic patterns. Typical values with HA connected:
| Condition | TX PPS | RX PPS |
|---|---|---|
| Idle (HA connected) | 1-3 | 15-20 |
| Sensor updates | 3-5 | 20-30 |
| OTA upload | 50-100 | 10-20 |
tx_pps:
name: "HaLow TX Rate"
rx_pps:
name: "HaLow RX Rate"Type: Numeric sensor (kilobits per second)
Throughput sensors measuring actual data transferred over the HaLow link.
TX bytes are counted at the LWIP link-output layer; RX bytes are counted
via a wrapped tcpip_input hook.
tx_kbps:
name: "HaLow TX Throughput"
rx_kbps:
name: "HaLow RX Throughput"Type: Numeric sensor (dBm)
Background noise level measured during the last channel scan. Updated each time the
scan_channel button is pressed. Use this to identify interference on your channel.
| Noise Floor | Meaning |
|---|---|
| -90 to -95 | Clean — no interference |
| -80 to -85 | Light interference |
| -70 to -75 | Moderate — consider changing AP channel |
| -60 or higher | Heavy interference — change channel immediately |
noise_floor:
name: "HaLow Noise Floor"Type: Numeric sensor (total increasing)
Counts of frames dropped due to TX or RX queue overflow in the UMAC. Non-zero values indicate the radio can't keep up — usually caused by sustained interference or extremely high traffic.
Type: Numeric sensor (total increasing)
Count of CCMP (AES encryption) decryption failures. Non-zero values indicate bit errors from interference corrupting encrypted frames.
Type: Numeric sensor (total increasing)
Count of MM6108 hardware restart events. Should remain at 0 during normal operation. Non-zero indicates severe issues (firmware crash, SPI failure, or extreme interference).
Type: Button
Triggers an on-demand channel scan. The MM6108 scans all available channels and
reports every AP it finds. Results are published to the scan_results text sensor
and the noise_floor sensor is updated from the scan data.
Type: Text sensor (JSON)
JSON array of all APs found during the last channel scan. Each entry includes:
[
{"ssid":"my-ap","bssid":"50:2E:91:D2:C9:E4","rssi":-41,"noise":-92,"freq":924000,"bw":8}
]Fields: ssid, bssid, rssi (dBm), noise (noise floor in dBm),
freq (center frequency in kHz), bw (bandwidth in MHz).
scan_channel:
name: "HaLow Channel Scan"
scan_results:
name: "HaLow Scan Results"Type: Text sensor | Shows the device's current IPv4 address (DHCP or static).
Type: Text sensor | Shows the gateway/router IP address.
Type: Text sensor | Shows the network subnet mask.
Type: Text sensor | Shows the SSID of the connected HaLow AP. Published once on connect.
Type: Text sensor | Shows the MAC address of the connected AP (BSSID).
Type: Text sensor | Shows the MM6108's own MAC address. Published once on connect.
Type: Text sensor | Shows the MM6108 firmware version (e.g., "1.17.6"). Published once on connect.
ip_address:
name: "HaLow IP"
gateway_address:
name: "HaLow Gateway"
mac_address:
name: "HaLow MAC"
firmware_version:
name: "HaLow FW Version"This component depends on the Morse Micro MM-IoT-SDK (auto-downloaded on first compile) which provides:
- morselib -- Prebuilt binary library containing the WLAN driver and crypto
- mm_shims -- ESP-IDF HAL layer (SPI, GPIO, FreeRTOS integration)
- mmipal -- IP abstraction layer (LWIP netif binding)
- mmutils -- Utility functions (OS abstraction, config store)
- morsefirmware/ -- MM6108 firmware and board config binaries
The SDK is licensed under Apache-2.0 (shims/examples) and a Morse Micro BDL (binary libraries).
- Morse Micro MM-IoT-SDK -- Auto-downloaded by this component (v2.10.4, native IDF 5.5.2)
- Seeed MM-IoT-SDK -- Seeed fork (v2.6.4, legacy)
- Morse Micro morse-firmware -- Additional BCF files for Quectel modules
- Morse Micro MM6108 Datasheet -- SoC specifications
- Seeed Wiki: Getting Started with Wi-Fi HaLow -- Hardware setup guide
- IEEE 802.11ah Standard -- Wi-Fi HaLow specification
- Xiao-Halow-to-WiFi-Bridge -- Related community project
Apache-2.0. Morse Micro binary libraries are subject to the Morse Micro BDL.