
| A | B | I2C 地址 |
|---|---|---|
| 0 | 0 | 0x74 |
| 1 | 0 | 0x73 |
| 0 | 1 | 0x72 |
| 1 | 1 | 0x71 |
G1 (NSS)、G2 (BUSY)、G37 (MOSI)、G35 (MISO)、G36 (SCK),中断 IO 为 G10 (IRQ),IO 扩展芯片地址为实物如下图所示:
Module13.2 LoRa-1262 支持多种参数配置,使用前请参考下方内容进行设置,若需各项参数的含义及其他详细信息请见数据手册,并根据实际需求调整案例程序中的参数设置,确保发送端与接收端参数一致。
频率 (LORA_FREQ)868 ~ 923 MHz 频段,请根据使用地区的无线电法规选择频率。带宽 (LORA_BW)扩频因子 (LORA_SF)编码率 (LORA_CR)4/5 ~ 4/8。同步字 (LORA_SYNC_WORD)发送功率 (LORA_TX_POWER)前导码长度 (LORA_PREAMBLE_LEN)NSS、IRQ、BUSY 三个引脚,但实际上 RadioLib 库会根据所使用的主控设备,自动映射剩余的 SPI 引脚 (MOSI、MISO、SCK),即使用 M5Unified 库初始化时根据不同设备默认定义的引脚,因此无需手动指定。#include <M5Unified.h>
#include <M5IOE1.h>
#include <RadioLib.h>
// M5IOE1 I2C address selected by SW2.
#define IO_EXPANDER_ADDRESS 0x74
// CoreS3 pins selected by the module DIP switches.
#define LORA_NSS_PIN GPIO_NUM_1
#define LORA_BUSY_PIN GPIO_NUM_2
#define LORA_IRQ_PIN GPIO_NUM_10
// M5IOE1 pins for LoRa reset, bypass, and power control.
#define PY_IO2_LORA_RST M5IOE1_PIN_2
#define PY_IO3_BYPASS M5IOE1_PIN_3
#define PY_IO5_PWR_EN M5IOE1_PIN_5
// LoRa parameters. These values must match on both devices.
#define LORA_FREQ 868.0f // Carrier frequency (MHz).
#define LORA_BW 125.0f // Bandwidth (kHz).
#define LORA_SF 12 // Spreading factor.
#define LORA_CR 5 // Coding rate: 4/5.
#define LORA_SYNC_WORD 0x34 // Sync word.
#define LORA_TX_POWER 22 // TX power (dBm).
#define LORA_CURRENT_LIMIT 140.0f // TX current limit (mA).
#define LORA_PREAMBLE_LEN 20 // Preamble length (symbols).
// SX1262 pins: NSS, IRQ, reset (controlled by M5IOE1), and BUSY.
SX1262 radio = new Module(LORA_NSS_PIN, LORA_IRQ_PIN, RADIOLIB_NC, LORA_BUSY_PIN);
M5Canvas canvas(&M5.Lcd);
M5IOE1 ioe1;
int transmissionState = RADIOLIB_ERR_NONE;
volatile bool transmittedFlag = false;
bool setExpanderOutput(uint8_t pin, uint8_t level)
{
// Configure one M5IOE1 pin as a push-pull output and set its level.
m5ioe1_err_t error = M5IOE1_OK;
ioe1.pinModeWithRes(pin, OUTPUT, &error);
if (error != M5IOE1_OK) {
return false;
}
if (ioe1.setDriveMode(pin, M5IOE1_DRIVE_PUSHPULL) != M5IOE1_OK) {
return false;
}
ioe1.digitalWriteWithRes(pin, level, &error);
return error == M5IOE1_OK;
}
bool initModuleControl()
{
// Initialize the M5IOE1 control interface.
const m5ioe1_err_t ioeState = ioe1.begin(
&M5.In_I2C, IO_EXPANDER_ADDRESS, M5IOE1_I2C_FREQ_100K,
M5IOE1_INT_MODE_DISABLED);
if (ioeState != M5IOE1_OK) {
Serial.printf("M5IOE1 init failed at 0x%02X, code: %d\n",
IO_EXPANDER_ADDRESS, ioeState);
return false;
}
// Hold reset, set the bypass control, and disable module power.
if (!setExpanderOutput(PY_IO2_LORA_RST, LOW) ||
!setExpanderOutput(PY_IO3_BYPASS, HIGH) ||
!setExpanderOutput(PY_IO5_PWR_EN, LOW)) {
return false;
}
delay(25);
// Enable module power before releasing reset.
if (!setExpanderOutput(PY_IO5_PWR_EN, HIGH)) {
return false;
}
delay(120);
// Release reset after the power rail is stable.
if (!setExpanderOutput(PY_IO2_LORA_RST, HIGH)) {
return false;
}
delay(120);
return true;
}
void IRAM_ATTR setFlag(void)
{
// Mark the packet-sent event for loop().
transmittedFlag = true;
}
void showSending(const String& payload, int count)
{
canvas.clear();
canvas.setCursor(0, 5);
canvas.printf("[SX1262]\nSending #%d packet......\n", count);
canvas.printf("Data:\n %s\n", payload.c_str());
canvas.pushSprite(0, 0);
}
void setup()
{
auto cfg = M5.config();
M5.begin(cfg);
Serial.begin(115200);
canvas.createSprite(320, 240);
canvas.setFont(&fonts::FreeMonoBold9pt7b);
if (!initModuleControl()) {
Serial.println(F("IO_EXP init failed"));
canvas.println(F("IO_EXP init failed"));
canvas.pushSprite(0, 0);
while (true) {
delay(1000);
}
}
// Initialize the SX1262.
Serial.print(F("[SX1262] Initializing ... "));
int state = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR,
LORA_SYNC_WORD, LORA_TX_POWER, LORA_PREAMBLE_LEN,
3.0f, true);
if (state != RADIOLIB_ERR_NONE) {
Serial.print(F("failed, code "));
Serial.println(state);
canvas.println(F("SX1262 init failed"));
canvas.pushSprite(0, 0);
while (true) {
delay(1000);
}
}
state = radio.setCurrentLimit(LORA_CURRENT_LIMIT);
if (state != RADIOLIB_ERR_NONE) {
Serial.print(F("current limit setup failed, code "));
Serial.println(state);
canvas.println(F("Current limit setup failed"));
canvas.pushSprite(0, 0);
while (true) {
delay(1000);
}
}
Serial.println(F("success!"));
// Register the callback for the packet-sent interrupt.
radio.setPacketSentAction(setFlag);
// Send an initial packet to start interrupt-driven transmission.
Serial.print(F("[SX1262] Sending first packet ... "));
transmissionState = radio.startTransmit("Transmitter Ready");
if (transmissionState != RADIOLIB_ERR_NONE) {
Serial.print(F("startTransmit failed, code: "));
Serial.println(transmissionState);
}
canvas.clear();
canvas.setCursor(0, 5);
canvas.println(F("[SX1262]"));
canvas.println(F("Transmitter Ready"));
canvas.pushSprite(0, 0);
}
int count = 0;
void loop()
{
// Wait until the packet-sent interrupt is received.
if (!transmittedFlag) {
return;
}
transmittedFlag = false;
if (transmissionState == RADIOLIB_ERR_NONE) {
Serial.println(F("Transmission finished!"));
canvas.println(F("Send successfully!"));
canvas.pushSprite(0, 0);
} else {
Serial.print(F("Send failed, code: "));
Serial.println(transmissionState);
canvas.println(F("Send failed"));
canvas.printf("code: %d\n", transmissionState);
canvas.pushSprite(0, 0);
}
// Finish the previous transmission before starting the next one.
radio.finishTransmit();
delay(1000);
// Start the next packet.
String payload = "Module13.2 LoRa-1262 #" + String(count);
Serial.printf("[SX1262] Sending #%d packet ... ", count);
transmissionState = radio.startTransmit(payload);
if (transmissionState != RADIOLIB_ERR_NONE) {
Serial.print(F("startTransmit failed, code: "));
Serial.println(transmissionState);
}
showSending(payload, count++);
}#include <M5Unified.h>
#include <M5IOE1.h>
#include <RadioLib.h>
// M5IOE1 I2C address selected by SW2.
#define IO_EXPANDER_ADDRESS 0x74
// CoreS3 pins selected by the module DIP switches.
#define LORA_NSS_PIN GPIO_NUM_1
#define LORA_BUSY_PIN GPIO_NUM_2
#define LORA_IRQ_PIN GPIO_NUM_10
// M5IOE1 pins for LoRa reset, bypass, and power control.
#define PY_IO2_LORA_RST M5IOE1_PIN_2
#define PY_IO3_BYPASS M5IOE1_PIN_3
#define PY_IO5_PWR_EN M5IOE1_PIN_5
// LoRa parameters. These values must match on both devices.
#define LORA_FREQ 868.0f // Carrier frequency (MHz).
#define LORA_BW 125.0f // Bandwidth (kHz).
#define LORA_SF 12 // Spreading factor.
#define LORA_CR 5 // Coding rate: 4/5.
#define LORA_SYNC_WORD 0x34 // Sync word.
#define LORA_TX_POWER 22 // TX power (dBm).
#define LORA_CURRENT_LIMIT 140.0f // TX current limit (mA).
#define LORA_PREAMBLE_LEN 20 // Preamble length (symbols).
// SX1262 pins: NSS, IRQ, reset (controlled by M5IOE1), and BUSY.
SX1262 radio = new Module(LORA_NSS_PIN, LORA_IRQ_PIN, RADIOLIB_NC, LORA_BUSY_PIN);
M5Canvas canvas(&M5.Lcd);
M5IOE1 ioe1;
// Set by the packet-received interrupt.
volatile bool receivedFlag = false;
bool setExpanderOutput(uint8_t pin, uint8_t level)
{
// Configure one M5IOE1 pin as a push-pull output and set its level.
m5ioe1_err_t error = M5IOE1_OK;
ioe1.pinModeWithRes(pin, OUTPUT, &error);
if (error != M5IOE1_OK) {
return false;
}
if (ioe1.setDriveMode(pin, M5IOE1_DRIVE_PUSHPULL) != M5IOE1_OK) {
return false;
}
ioe1.digitalWriteWithRes(pin, level, &error);
return error == M5IOE1_OK;
}
bool initModuleControl()
{
// Initialize the M5IOE1 control interface.
const m5ioe1_err_t ioeState = ioe1.begin(
&M5.In_I2C, IO_EXPANDER_ADDRESS, M5IOE1_I2C_FREQ_100K,
M5IOE1_INT_MODE_DISABLED);
if (ioeState != M5IOE1_OK) {
Serial.printf("M5IOE1 init failed at 0x%02X, code: %d\n",
IO_EXPANDER_ADDRESS, ioeState);
return false;
}
// Hold reset, set the bypass control, and disable module power.
if (!setExpanderOutput(PY_IO2_LORA_RST, LOW) ||
!setExpanderOutput(PY_IO3_BYPASS, HIGH) ||
!setExpanderOutput(PY_IO5_PWR_EN, LOW)) {
return false;
}
delay(25);
// Enable module power before releasing reset.
if (!setExpanderOutput(PY_IO5_PWR_EN, HIGH)) {
return false;
}
delay(120);
// Release reset after the power rail is stable.
if (!setExpanderOutput(PY_IO2_LORA_RST, HIGH)) {
return false;
}
delay(120);
return true;
}
void IRAM_ATTR setFlag(void)
{
// Mark the packet-received event for loop().
receivedFlag = true;
}
void setup()
{
auto cfg = M5.config();
M5.begin(cfg);
Serial.begin(115200);
canvas.createSprite(320, 240);
canvas.setFont(&fonts::FreeMonoBold9pt7b);
if (!initModuleControl()) {
Serial.println(F("IO_EXP init failed"));
canvas.println(F("IO_EXP init failed"));
canvas.pushSprite(0, 0);
while (true) {
delay(1000);
}
}
// Initialize the SX1262.
Serial.print(F("[SX1262] Initializing ... "));
int state = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR,
LORA_SYNC_WORD, LORA_TX_POWER, LORA_PREAMBLE_LEN,
3.0f, true);
if (state != RADIOLIB_ERR_NONE) {
Serial.print(F("failed, code "));
Serial.println(state);
canvas.println(F("SX1262 init failed"));
canvas.pushSprite(0, 0);
while (true) {
delay(1000);
}
}
state = radio.setCurrentLimit(LORA_CURRENT_LIMIT);
if (state != RADIOLIB_ERR_NONE) {
Serial.print(F("current limit setup failed, code "));
Serial.println(state);
canvas.println(F("Current limit setup failed"));
canvas.pushSprite(0, 0);
while (true) {
delay(1000);
}
}
Serial.println(F("success!"));
// Register the callback for the packet-received interrupt.
radio.setPacketReceivedAction(setFlag);
// Start interrupt-driven receive mode.
Serial.print(F("[SX1262] Starting to listen ... "));
state = radio.startReceive();
if (state != RADIOLIB_ERR_NONE) {
Serial.print(F("failed, code "));
Serial.println(state);
canvas.println(F("Receive start failed"));
canvas.pushSprite(0, 0);
while (true) {
delay(1000);
}
}
Serial.println(F("success!"));
canvas.setCursor(0, 5);
canvas.println(F("[SX1262]"));
canvas.println(F("Waiting for packet..."));
canvas.pushSprite(0, 0);
}
void loop()
{
// Wait until the packet-received interrupt is received.
if (!receivedFlag) {
return;
}
receivedFlag = false;
// Read the packet after the interrupt is received.
String payload;
int state = radio.readData(payload);
if (state == RADIOLIB_ERR_NONE) {
// Read link quality information for the received packet.
const float rssi = radio.getRSSI();
const float snr = radio.getSNR();
const float frequencyError = radio.getFrequencyError();
Serial.println(F("[SX1262] Received packet:"));
Serial.print(F("[SX1262] Data:\t\t"));
Serial.println(payload);
Serial.print(F("[SX1262] RSSI:\t\t"));
Serial.print(rssi);
Serial.println(F(" dBm"));
Serial.print(F("[SX1262] SNR:\t\t"));
Serial.print(snr);
Serial.println(F(" dB"));
Serial.print(F("[SX1262] Frequency error:\t"));
Serial.print(frequencyError);
Serial.println(F(" Hz"));
canvas.clear();
canvas.setCursor(0, 5);
canvas.printf("[SX1262]\nReceived packet:\n");
canvas.printf("Data:\n %s\n", payload.c_str());
canvas.printf("RSSI: %0.2f dBm\n", rssi);
canvas.printf("SNR: %0.2f dB\n", snr);
canvas.printf("Freq err: %0.2f Hz\n", frequencyError);
canvas.pushSprite(0, 0);
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
Serial.println(F("[SX1262] CRC error!"));
} else {
Serial.print(F("[SX1262] Receive failed, code: "));
Serial.println(state);
}
// Return to continuous receive mode.
radio.finishReceive();
radio.startReceive();
}
发送端会每秒发送一次包含计数的字符串,接收端会打印接收到的字符串,并显示 RSSI 等信息。
[SX1262] Sending #34 packet ... Transmission finished! [SX1262] Received packet:
[SX1262] Data: Module13.2 LoRa-1262 #34
[SX1262] RSSI: -0.00 dBm
[SX1262] SNR: 5.00 dB
[SX1262] Frequency error: 23.01 Hz