Skip to content

About

ESPHome external component for Wi-Fi HaLow (IEEE 802.11ah) using XIAO ESP32-S3 + WIO-WM6180 (Morse Micro MM6108)

Resources

Stars

5 stars

Watchers

0 watching

Forks

Latest commit

 

History

57 Commits

Folders and files

Repository files navigation

esphome-halow

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.

Why HaLow over LoRa?

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.

Supported Hardware

HaLow Modules (MM6108-based)

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

Host MCUs

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

Access Points

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

Quick Start

Prerequisites

  • 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.

Example YAML

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: Uptime

How It Works

Architecture

The 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

Boot Sequence

  1. mmhal_init() -- Initialize SPI bus, GPIO pins (reset, wake, busy, IRQ)
  2. mmwlan_init() -- Initialize WLAN subsystem
  3. mmwlan_boot() -- Load mm6108.mbin firmware + bcf_mf16858.mbin board config over SPI
  4. mmipal_init() -- Initialize LWIP network stack with DHCP
  5. mmwlan_sta_enable() -- Start WPA3-SAE association with the AP
  6. Link comes up, DHCP assigns IP address. Full setup takes ~10s from cold boot.

Pin Mapping

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

Firmware Files

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.

Verified Test Results

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

Access Point Setup (Required)

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.

  1. Set the HaLowLink 2 to AP mode using the mode button or web UI
  2. Connect your LAN cable to the HaLowLink 2's WAN port (not LAN — the wizard bridges WAN↔HaLow)
  3. Access the web UI at https://<halowlink-ip> (check your router's DHCP leases for the IP)
  4. Go to Wizard and select "HaLow devices get IP on your existing router's network"
  5. 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.

Known Limitations

  • 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.

Component Configuration

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

Sensors

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.

Signal Sensors

rssi

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"
link_quality

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"

Radio Adaptation Sensors

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.

mcs

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"
bandwidth

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"
tx_success_rate

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"

Traffic Sensors

tx_pps / rx_pps

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"
tx_kbps / rx_kbps

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"

Diagnostic Sensors

noise_floor

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"
tx_frames_dropped / rx_frames_dropped

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.

ccmp_failures

Type: Numeric sensor (total increasing)

Count of CCMP (AES encryption) decryption failures. Non-zero values indicate bit errors from interference corrupting encrypted frames.

hw_restarts

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).

Channel Scan

scan_channel

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.

scan_results

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"

Network Info Sensors

ip_address

Type: Text sensor | Shows the device's current IPv4 address (DHCP or static).

gateway_address

Type: Text sensor | Shows the gateway/router IP address.

subnet_mask

Type: Text sensor | Shows the network subnet mask.

connected_ssid

Type: Text sensor | Shows the SSID of the connected HaLow AP. Published once on connect.

bssid

Type: Text sensor | Shows the MAC address of the connected AP (BSSID).

mac_address

Type: Text sensor | Shows the MM6108's own MAC address. Published once on connect.

firmware_version

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"

Dependencies

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).

References

License

Apache-2.0. Morse Micro binary libraries are subject to the Morse Micro BDL.

About

ESPHome external component for Wi-Fi HaLow (IEEE 802.11ah) using XIAO ESP32-S3 + WIO-WM6180 (Morse Micro MM6108)

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages