
| A | B | I2C Address |
|---|---|---|
| 0 | 0 | 0x74 |
| 1 | 0 | 0x73 |
| 0 | 1 | 0x72 |
| 1 | 1 | 0x71 |
G1 (NSS), G2 (BUSY), G37 (MOSI), G35 (MISO), and G36 (SCK), the interrupt IO is G10 (IRQ), and the IO expander address is shown in the physical setup below:
Module13.2 LoRa-1262 supports multiple parameter configurations. Refer to the following information before use. For the meaning of each parameter and other details, see the Datasheet. Adjust the parameters in the example program as needed and ensure that the transmitter and receiver use matching parameters.
Frequency (LORA_FREQ)868 ~ 923 MHz band. Select a frequency according to the radio regulations in your region.Bandwidth (LORA_BW)Spreading Factor (LORA_SF)Coding Rate (LORA_CR)4/5 ~ 4/8.Sync Word (LORA_SYNC_WORD)Transmit Power (LORA_TX_POWER)Preamble Length (LORA_PREAMBLE_LEN)NSS, IRQ, and BUSY pins. The RadioLib library automatically maps the remaining SPI pins (MOSI, MISO, and SCK) according to the controller being used. When initialized with M5Unified, the default pins are defined for each device, so they do not need to be specified manually.#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();
}
The transmitter sends a string containing a counter every second. The receiver prints the received string and displays RSSI and other information.
[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