Add support for Wi-Fi HaLow on Seeed XIAO ESP32-S3

This commit is contained in:
Ben Meadors
2026-05-16 09:48:17 -05:00
parent 2a91d186eb
commit a1acad6dc4
54 changed files with 10407 additions and 4 deletions
@@ -0,0 +1,20 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define USB_VID 0x2886
#define USB_PID 0x0059
// I2C is unused on this variant (HaLow BUSY claims GPIO 5), but the Arduino
// framework's default Wire still needs valid SDA/SCL values to compile.
static const uint8_t SDA = 47;
static const uint8_t SCL = 48;
// Default SPI is shared with the HaLow module. SS = HaLow CS.
static const uint8_t MISO = 8;
static const uint8_t SCK = 7;
static const uint8_t MOSI = 9;
static const uint8_t SS = 4;
#endif /* Pins_Arduino_h */
@@ -0,0 +1,104 @@
[env:seeed-xiao-s3-halow]
; HW_MODEL: using PRIVATE_HW (255) until a SEEED_XIAO_S3_HALOW slot is allocated upstream.
custom_meshtastic_hw_model = 255
custom_meshtastic_hw_model_slug = SEEED_XIAO_S3_HALOW
custom_meshtastic_architecture = esp32-s3
custom_meshtastic_actively_supported = false
custom_meshtastic_support_level = 1
custom_meshtastic_display_name = Seeed Xiao ESP32-S3 + Wi-Fi HaLow
custom_meshtastic_tags = Seeed, HaLow, Experimental
custom_meshtastic_requires_dfu = true
custom_meshtastic_partition_scheme = 8MB
extends = esp32s3_base
board = seeed-xiao-s3
; Morse Micro mm-iot-esp32 requires ESP-IDF 5.1.x; PlatformIO's default
; espressif32 platform ships IDF 4.4.7. Override with the pioarduino fork +
; vidplace7's v5.1 framework artifact (same combination the ESP32-C6 variant
; uses). This unlocks IDF 5.1 driver APIs (spi_master, gpio) at the cost of
; some Arduino-ESP32 2.x APIs that aren't in 3.x — expect to compile-fix
; downstream callers (BLE/NimBLE most likely; that's why we keep BLE on by
; default but accept it may need to be disabled).
platform =
https://github.com/Jason2866/platform-espressif32/archive/22faa566df8c789000f8136cd8d0aca49617af55.zip
platform_packages =
framework-arduinoespressif32 @ https://github.com/vidplace7/platform-espressif32/releases/download/meshtastic-esp32c6/framework-arduinoespressif32-all-release_v5.1-124d64e.zip
board_level = pr
board_check = true
board_build.partitions = default_8MB.csv
upload_protocol = esptool
upload_speed = 921600
build_unflags =
${esp32s3_base.build_unflags}
-DARDUINO_USB_MODE=1
build_flags =
${esp32s3_base.build_flags}
-D SEEED_XIAO_S3_HALOW
-I variants/esp32s3/seeed_xiao_s3_halow
-DBOARD_HAS_PSRAM
-DARDUINO_USB_MODE=0
; Compile the HaLow transport into the firmware. The SDK glue inside the
; transport is gated on USE_MM_IOT_ESP32, which is OFF here until the
; mm-iot-esp32 ESP-IDF component is wired into the PlatformIO build (Phase 0
; step 1 of the plan). With it OFF the variant builds as a no-radio node.
; NimBLE-Arduino 1.4.3 (pinned in esp32-common.ini) doesn't compile against
; IDF 5.1, so disable Bluetooth on this variant for now. Phone API still
; works over Wi-Fi/HaLow. Mirrors the workaround the ESP32-C6 variant uses.
-DHAS_BLUETOOTH=0
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
; The v5.1 framework already adds --specs=nano.specs; tell esp32_extra.py
; to skip its own addition or the linker fails on a duplicate spec.
-DMESHTASTIC_SKIP_NANO_SPECS=1
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
-DMESHTASTIC_EXCLUDE_WEBSERVER=1
; No I2C peripherals on the HaLow stack (BUSY claims SDA pin). The cardKB
; rescan was spamming "Unknown error at address 0x1f" etc on boot. Skipping
; just the rescan path is enough — main.cpp's I2C scan is already gated on
; HAS_WIRE which we set to 0. We avoid MESHTASTIC_EXCLUDE_I2C because
; InputBroker.cpp unconditionally references cardKbI2cImpl.
-DI2C_NO_RESCAN=1
-DUSE_HALOW_RADIO=1
-DUSE_HALOW_WIFI=1
-DUSE_MM_IOT_ESP32=1
; Pin map fed to the vendored mm_shims (replaces Kconfig defaults at
; framework/mm_shims/Kconfig — same values, just delivered as -D flags
; because PlatformIO/Arduino doesn't run idf.py menuconfig).
-DCONFIG_MM_RESET_N=1
-DCONFIG_MM_WAKE=2
-DCONFIG_MM_BUSY=5
-DCONFIG_MM_SPI_SCK=7
-DCONFIG_MM_SPI_MOSI=9
-DCONFIG_MM_SPI_MISO=8
-DCONFIG_MM_SPI_CS=4
-DCONFIG_MM_SPI_IRQ=3
-DCONFIG_MM_SPI_FREQ_HZ=20000000
-DCONFIG_MM_SPI_HOST=2
-DMMPKTMEM_TX_POOL_N_BLOCKS=20
-DMMPKTMEM_RX_POOL_N_BLOCKS=23
; HaLow STA credentials. Empty SSID = chip boots and idles (default — useful
; for first-flash hardware verification). To actually join an AP, override
; in your own platformio_override.ini or pass at the pio command line:
; -DHALOW_SSID=\\\"MyHaLowAP\\\" -DHALOW_PASSPHRASE=\\\"secret\\\"
; The trailing escaped quotes survive shell+SCons quoting.
-DHALOW_SSID=\"\"
-DHALOW_PASSPHRASE=\"\"
; HaLow is the only transport on this variant — UDP multicast mesh-over-IP
; needs to be on by default or the firmware boots into a useless state.
-DUSERPREFS_NETWORK_ENABLED_PROTOCOLS=meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST
; libmorse.a is precompiled for esp32-xtensa-lx7. The .mbin.o firmware blobs
; are picked up via library.json's srcFilter; libmorse needs an explicit
; -L/-l on the link line.
-Llib/MorseWlan/lib/esp32-xtensa-lx7
-lmorse
lib_ignore =
${esp32_common.lib_ignore}
NimBLE-Arduino
libpax
build_src_filter =
${esp32_common.build_src_filter} -<mesh/http>
@@ -0,0 +1,38 @@
/*
* Seeed Studio XIAO ESP32-S3 + Wi-Fi HaLow add-on (Quectel FGH100M-H / Morse Micro MM6108)
*
* Pin map fixed by the Seeed HaLow add-on hardware. SCK/MOSI/MISO are the
* same XIAO pins the LoRa hat uses (7/9/8), but CS/IRQ/RST/WAKE/BUSY collide
* with the L76K GPS standby (GPIO 1) and I2C SDA (GPIO 5) used by the LoRa
* variant — so this variant deliberately ships without LoRa, GPS, or the
* SSD1306 OLED. The HaLow module replaces all of them.
*
* Hardware: https://wiki.seeedstudio.com/getting_started_with_wifi_halow_module_for_xiao/
*/
#define LED_POWER 48
#define LED_STATE_ON 1
#define BUTTON_PIN 21
#define BUTTON_NEED_PULLUP
// No battery monitoring on this variant — leaving BATTERY_PIN undefined skips
// the legacy adc1_* ADC code in Power.cpp that isn't compatible with IDF 5.1.
// HaLow BUSY claims GPIO 5 (the I2C SDA used by the LoRa variant), and HaLow
// RST claims GPIO 1 (the GPS standby). Disable I2C, the OLED screen, and GPS
// so nothing else drives those lines.
#define HAS_WIRE 0
#define HAS_SCREEN 0
#define HAS_GPS 0
#define NO_GPS 1
// HaLow module pin map (Seeed XIAO HaLow add-on, fixed by hardware)
#define HALOW_SPI_SCK 7
#define HALOW_SPI_MISO 8
#define HALOW_SPI_MOSI 9
#define HALOW_CS 4
#define HALOW_IRQ 3
#define HALOW_RST 1
#define HALOW_WAKE 2
#define HALOW_BUSY 5