M5Stack Cap LoRa-1262 (SKU U214)
A snap-on Cap for the M5Stack Cardputer Adv (K132-Adv) and CardputerZero that adds two independent radios in one accessory:
- LoRa: Semtech SX1262 sub-GHz transceiver, 868–923 MHz, +22 dBm TX, −147 dBm RX (LDR mode), FSK/GFSK/MSK/GMSK/LoRa/OOK, external RP-SMA antenna (3 dBi, 108×9.3 mm, included).
- GNSS: ATGM336H-6N@AT6668 multi-constellation receiver (GPS/QZSS/ BeiDou-2/BeiDou-3/Galileo/GLONASS), 50 channels, up to 10 Hz update, <1.5 m CEP50, built-in ceramic patch antenna. UART interface, default 115200 baud, 8N1.
Official docs: https://docs.m5stack.com/en/cap/Cap_LoRa-1262 Product page/SKU: https://docs.m5stack.com/en/products/sku/U214
Read this first — three things that will kill the cap or your bring-up
- Never power on without the LoRa antenna installed. M5Stack's docs are explicit: "the device hardware may be permanently damaged." An SX1262 driving +22 dBm into no load reflects power back into its PA. This is not a warning that can be softened — the antenna is a prerequisite, not an accessory.
- The RF antenna switch is gated by an I/O expander pin, not by the
SX1262 itself. SDA/SCL on the cap connector go to a PI4IOE5V6408
I/O expander whose P0 must be driven high to enable the RF path.
If your first
transmit()/receive()calls do nothing (RadioLib returning no error but no packets on the air / no packets received), you almost certainly never enabled P0. See the "Enabling the RF switch" section below. - The LoRa SPI bus is the microSD SPI bus. On the Cardputer Adv,
LoRa_SCK=G40,LoRa_MOSI=G14,LoRa_MISO=G39are the same pins used by the microSD slot (SD_CLK=G40,SD_MOSI=G14,SD_MISO=G39). Only the chip-select is separate (LoRa_NSS=G5vsSD_CS=G12). This works — SPI is designed for exactly this — but you must not init the SD bus and the LoRa bus as two independentSPIClassinstances with different clock/mode settings. Share oneSPIClass, or accept that accessing SD and LoRa concurrently requires bus mediation.
Compatibility
| Controller | Compatible? | Source |
|---|---|---|
| Cardputer Adv (K132-Adv) | Yes | Official docs page names it explicitly |
| CardputerZero | Yes | Official docs page names it explicitly (not independently verified for this skill) |
| Cardputer (original, K132) | No — explicitly listed as incompatible in the M5Stack cap-compatibility JSON (product_cap_compatible.json, U214 incompatible: ["K132"]). The original Cardputer's EXT header pinout / power path differs. |
Everything below is written against the Cardputer Adv GPIO mapping, because that's the controller M5Stack publishes GPIO numbers for. The CardputerZero mapping is on that board's own skill (not yet built at time of writing) — if the user is on a CardputerZero, verify the pin numbers against its schematic before running any of the example code.
Cap connector pinout (14-pin, from official docs)
The cap plugs onto the Cardputer Adv EXT header. Pin numbers below are the positions in the cap's own 14-pin connector table, not GPIO numbers.
| Pin | Signal | Pin | Signal |
|---|---|---|---|
| 1 | GPS_TX (from GNSS module) | 14 | LoRa_NSS (SX1262 chip-select) |
| 2 | GPS_RX (to GNSS module) | 13 | LoRa_MISO |
| 3 | SCL (I2C, drives I/O expander) | 12 | LoRa_MOSI |
| 4 | SDA (I2C, drives I/O expander) | 11 | LoRa_SCK |
| 5 | 5V_OUT | 10 | LoRa_BUSY |
| 6 | GND | 9 | LoRa_IRQ (DIO1) |
| 7 | 5V_IN | 8 | LoRa_RST |
Cardputer Adv GPIO mapping (official)
M5Stack's docs give this mapping directly for Cardputer Adv:
LoRa (SX1262 over SPI):
| Signal | Cardputer Adv GPIO |
|---|---|
| RST | G3 |
| IRQ (DIO1) | G4 |
| BUSY | G6 |
| SCK | G40 |
| MOSI | G14 |
| MISO | G39 |
| NSS (CS) | G5 |
GNSS (ATGM336H over UART, 115200 8N1):
| Signal | Cardputer Adv GPIO | ESP32-S3 UART note |
|---|---|---|
| GPS-RX (data to GNSS) | G13 | wire to UART.TX on the S3 |
| GPS-TX (data from GNSS) | G15 | wire to UART.RX on the S3 |
The names are from the perspective of the GNSS chip, not the ESP32.
GPS-RX is the pin the GNSS listens on, so it's the S3's TX; GPS-TX is
the pin the GNSS talks on, so it's the S3's RX. Use UART1 or UART2 on
the S3 — the S3 lets you route any UART peripheral to arbitrary GPIOs, so
call Serial1.begin(115200, SERIAL_8N1, /*RX=*/15, /*TX=*/13).
I2C bus for the I/O expander (see next section): SDA/SCL on the cap
connector share the Cardputer Adv internal I2C bus (SDA=G8, SCL=G9) —
the same bus that already carries the TCA8418 keyboard controller (0x34),
BMI270 IMU (0x68), and ES8311 audio codec (0x18). One more device on the
bus is fine; just don't re-init Wire after M5Cardputer.begin() has
already claimed it.
Cross-reference with the Cardputer Adv microSD slot:
| Signal | LoRa GPIO | SD GPIO | Same pin? |
|---|---|---|---|
| CLK / SCK | G40 | G40 | yes |
| MOSI | G14 | G14 | yes |
| MISO | G39 | G39 | yes |
| Chip select | G5 (NSS) | G12 (CS) | no — separate |
The two peripherals share the SPI bus by design, differentiated only by
CS. Use a single SPIClass instance and mediate access, or accept that
concurrent SD I/O and LoRa RX will need queuing.
Enabling the RF switch (PI4IOE5V6408 gotcha)
Community-sourced from M5Stack's official docs page: the SDA/SCL on the
cap goes to a PI4IOE5V6408 8-bit I/O expander, and P0 of that
expander drives the RF antenna switch. It must be set high before the
SX1262 can transmit or receive. M5Stack's own docs state this in one
sentence and do not publish an I2C address for the expander on this cap
specifically. The PI4IOE5V6408's default 7-bit address is 0x43 (with
its ADDR strap low) or 0x44 (with ADDR high) per the NXP
datasheet — which strap M5Stack chose is not documented on the product
page. Scan the bus (Wire.beginTransmission(addr); Wire.endTransmission())
for both if the first choice NAKs.
At the register level, the PI4IOE5V6408 needs:
- Write
0x03(I/O direction, 1 = output) to make P0 an output. - Write
0x05(output high-Z), clear bit 0 so the pin actively drives. - Write
0x01bit 0 = 1 to drive P0 high.
(Register offsets from the NXP PI4IOE5V6408 datasheet — verify against the datasheet before shipping; treat as unverified against M5Stack's schematic for this specific cap.)
A minimal Arduino snippet:
#include <Wire.h>
constexpr uint8_t IO_EXP_ADDR = 0x43; // try 0x44 if 0x43 NAKs
static bool ioexp_write(uint8_t reg, uint8_t val) {
Wire.beginTransmission(IO_EXP_ADDR);
Wire.write(reg); Wire.write(val);
return Wire.endTransmission() == 0;
}
bool enable_lora_rf_switch() {
// Direction: P0 = output (bit0 = 1)
if (!ioexp_write(0x03, 0x01)) return false;
// Ensure P0 is a hard drive, not high-Z (bit0 = 0 in the HighZ register)
if (!ioexp_write(0x05, 0x00)) return false;
// Drive P0 high — RF switch on
if (!ioexp_write(0x01, 0x01)) return false;
return true;
}
If the scan finds the expander at a different address, or if the register
map behaves differently, dump all eight registers first (0x00–0x07)
and cross-reference the datasheet before continuing.
LoRa: SX1262 with RadioLib
M5Stack's own examples use jgromes/RadioLib for the SX1262. Install it
via Arduino Library Manager or PlatformIO (lib_deps = jgromes/RadioLib).
#include <RadioLib.h>
// Cardputer Adv GPIO mapping from the official Cap LoRa-1262 docs
constexpr int PIN_LORA_NSS = 5;
constexpr int PIN_LORA_IRQ = 4; // DIO1
constexpr int PIN_LORA_RST = 3;
constexpr int PIN_LORA_BUSY = 6;
constexpr int PIN_LORA_SCK = 40;
constexpr int PIN_LORA_MOSI = 14;
constexpr int PIN_LORA_MISO = 39;
SPIClass loraSpi(HSPI); // or FSPI; both are usable on S3 — pick whichever
// the rest of your app isn't using
SX1262 radio = new Module(PIN_LORA_NSS, PIN_LORA_IRQ, PIN_LORA_RST, PIN_LORA_BUSY, loraSpi);
void setup() {
Serial.begin(115200);
Wire.begin(); // for the I/O expander
enable_lora_rf_switch(); // from the snippet above — DO THIS FIRST
loraSpi.begin(PIN_LORA_SCK, PIN_LORA_MISO, PIN_LORA_MOSI, PIN_LORA_NSS);
// 915.0 MHz here is a placeholder. Pick a frequency legal for your
// region: EU868 -> 868.1, US915 -> 902.3+ (channel plan), AS923 -> 923.2
// etc. The SX1262 hardware supports 868-923; the *radio regulator* in
// your country picks the actual channel.
int st = radio.begin(915.0);
if (st != RADIOLIB_ERR_NONE) {
Serial.printf("SX1262 begin failed: %d\n", st);
while (true) delay(1000);
}
}
void loop() {
int st = radio.transmit("hello from cap-lora-1262");
if (st == RADIOLIB_ERR_NONE) Serial.println("sent");
else if (st == RADIOLIB_ERR_PACKET_TOO_LONG) Serial.println("payload too long");
else if (st == RADIOLIB_ERR_TX_TIMEOUT) Serial.println("TX timeout");
else Serial.printf("TX err %d\n", st);
delay(5000);
}
Common bring-up failure modes:
begin()succeeds but no packets seen on a receiver: RF switch not enabled — see gotcha 2 above.begin()returns −2 (RADIOLIB_ERR_CHIP_NOT_FOUND): SPI wiring wrong, orloraSpi.begin()not called beforeradio.begin(), or the cap is not fully seated.begin()returns −707 (RADIOLIB_ERR_SPI_CMD_TIMEOUT):BUSYline wrong — Cardputer Adv wants G6 specifically; the SX1262 holdsBUSYhigh while calibrating and RadioLib times out if it never sees the falling edge.- Everything works but range is poor: check the antenna is on the RP-SMA jack (not just present in the box), and that P0 on the I/O expander is actually held high — a floating switch can attenuate 20+ dB without breaking anything visibly.
LoRa frequency and regional legality
The SX1262 hardware on this cap operates 868–923 MHz. That does not mean any frequency in that range is legal to transmit on where you are:
- EU868 (Europe): 863–870 MHz, duty-cycle limited (typically 1% per sub-band). LoRaWAN EU868 channel plan starts 868.1 MHz.
- US915 (North America): 902–928 MHz, frequency-hopping required above a certain duty cycle. LoRaWAN US915 uses 902.3 MHz + 0.2 MHz per channel across 64 uplink channels.
- AS923 (Asia-Pacific, several sub-plans): 915–928 MHz core, center frequency and channel plan differ by country.
- CN470 and KR920 exist but sit outside this cap's 868–923 MHz range — this cap can't legally serve those bands. Use a different M5Stack LoRa product for CN470 / KR920.
Don't hardcode a frequency in a shipped sketch without knowing which region the user is deploying to. If they haven't said, ask.
GNSS: ATGM336H with TinyGPSPlus
M5Stack recommends m5stack/TinyGPSPlus — their fork of Mikal Hart's
TinyGPSPlus with CASIC-protocol extensions the ATGM336H uses for its
BeiDou-specific sentences. The upstream mikalhart/TinyGPSPlus also
works for standard NMEA output but will not parse CASIC-specific fields.
#include <TinyGPSPlus.h>
constexpr int PIN_GPS_RX_FROM_MODULE = 15; // ESP32-S3 RX = module TX
constexpr int PIN_GPS_TX_TO_MODULE = 13; // ESP32-S3 TX = module RX
TinyGPSPlus gps;
void setup() {
Serial.begin(115200);
Serial1.begin(115200, SERIAL_8N1,
PIN_GPS_RX_FROM_MODULE, PIN_GPS_TX_TO_MODULE);
}
void loop() {
while (Serial1.available()) gps.encode(Serial1.read());
if (gps.location.isUpdated()) {
Serial.printf("lat=%.6f lon=%.6f alt=%.1fm sats=%u hdop=%.1f\n",
gps.location.lat(), gps.location.lng(),
gps.altitude.meters(),
gps.satellites.value(),
gps.hdop.hdop());
}
}
Expect cold-start TTFF around 30–60 s outdoors with a clear sky view. The ceramic patch antenna is built into the cap — it does not need an external GNSS antenna. Indoor fix is generally unreliable; test near a window or outside.
Baud rate is 115200 8N1 by default. The ATGM336H can be reconfigured (baud, update rate, active constellations) using CASIC protocol messages sent over the same UART — see the CASIC protocol spec linked from the official docs page if the user needs 10 Hz updates or wants to disable constellations to save power.
Power
- LoRa transmit: ~163.4 mA @ +22 dBm (per official docs).
- GNSS active: ~33.1 mA (per official docs).
- Both radios can run simultaneously. Peak draw during LoRa TX + GNSS acquisition can push the Cardputer Adv's total board draw past 300 mA; budget headroom in the battery/power path accordingly.
- Cap is powered from the Cardputer Adv's 5V rail (pin 7
5V_INon the cap connector). Pin 55V_OUTback-feeds 5V to anything else stacked on top, if applicable.
Quick reference
| Field | Value |
|---|---|
| M5Stack SKU | U214 |
| LoRa chip | Semtech SX1262 |
| GNSS chip | ATGM336H-6N (Allystar, AT6668 die) |
| I/O expander | PI4IOE5V6408 (RF switch enable on P0) |
| LoRa band | 868–923 MHz |
| LoRa modulations | FSK, GFSK, MSK, GMSK, LoRa, OOK |
| LoRa max bitrate | 300 kbps |
| LoRa TX power (max) | +22 dBm |
| LoRa RX sensitivity | −147 dBm (LDR mode) |
| LoRa interface | SPI (shared with Cardputer Adv microSD bus) |
| GNSS interface | UART, 115200 8N1 |
| GNSS constellations | GPS + QZSS + BeiDou-2/3 + Galileo + GLONASS |
| GNSS update rate | up to 10 Hz |
| GNSS accuracy | <1.5 m CEP50 |
| GNSS channels | 50 |
| LoRa antenna | RP-SMA, 3 dBi, 108×9.3 mm (included) |
| GNSS antenna | built-in ceramic patch |
| Current (LoRa TX) | ~163.4 mA |
| Current (GNSS on) | ~33.1 mA |
| Dimensions | 84.0×24.0×15.2 mm, 22.1 g (excl. antenna) |
| Officially compatible with | Cardputer Adv (K132-Adv), CardputerZero |
| Officially incompatible with | Cardputer original (K132) |
Libraries and resources
- Cap docs: https://docs.m5stack.com/en/cap/Cap_LoRa-1262
- SKU page: https://docs.m5stack.com/en/products/sku/U214
- LoRa (SX1262) driver: https://github.com/jgromes/RadioLib
- GNSS parser (M5Stack fork with CASIC support): https://github.com/m5stack/TinyGPSPlus
- Datasheets referenced on the docs page: Semtech SX1262, Allystar ATGM336H-6N, CASIC protocol spec (Allystar).
- Cardputer Adv host board skill:
plugins/cardputer/skills/cardputer-adv/(pinout, EXT header wiring, shared I2C bus). - Cardputer Adv microSD pinout: see
plugins/cardputer/skills/cardputer-adv/references/pinout.md— the SPI bus this cap uses is the same one the SD slot uses.