I have the SX1262 868M LoRa Hat that comes along with test code. This sums up the function call to initialize the device.
node = sx126x.sx126x(serial_num = "/dev/ttyS0", freq=868, addr=0, power=22, rssi=True, air_speed=2400, relay=False)
Now, I am trying to set this Hat as a receiver (the code already does that), and the ESP32 (which also is equipped with an SX1262) as the sender with this code:
LoRaTransmitter.h
#include "LoRaWan_APP.h"
#include "Arduino.h"
class LoRaTransmitter {
private:
RadioEvents_t RadioEvents;
static LoRaTransmitter* instance;
bool txDone;
LoRaTransmitter();
public:
static LoRaTransmitter* getInstance();
void send(const char *message);
void send(const String &message);
static void OnTxDone(void);
static void OnTxTimeout(void);
};
LoRaTransmitter.cpp
#include "LoRaTransmitter.h"
#define RF_FREQUENCY 868000000 // Hz
#define TX_OUTPUT_POWER 22 // dBm
#define LORA_BANDWIDTH 2 // [0: 125 kHz, 1: 250 kHz, 2: 500 kHz, 3: Reserved]
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: 4/5, 2: 4/6, 3: 4/7, // 4: 4/8]
#define LORA_PREAMBLE_LENGTH 4 // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 0 // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define BUFFER_SIZE 512
LoRaTransmitter* LoRaTransmitter::instance;
LoRaTransmitter::LoRaTransmitter() {
Mcu.begin();
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
Radio.Init(&RadioEvents);
Radio.SetChannel(RF_FREQUENCY);
Radio.SetTxConfig(
MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000);
txDone = true;
}
LoRaTransmitter* LoRaTransmitter::getInstance() {
if (instance == nullptr)
instance = new LoRaTransmitter();
return instance;
}
void LoRaTransmitter::send(const char *message) {
if (txDone) {
txDone = false;
Serial.println("Sending message: " + String(message) + ", Length: " + String(strlen(message)));
char packet[BUFFER_SIZE];
strncpy(packet, message, BUFFER_SIZE);
Radio.Send((uint8_t *)packet, strlen(packet));
}
Radio.IrqProcess();
}
void LoRaTransmitter::send(const String &message) {
send(message.c_str());
}
void LoRaTransmitter::OnTxDone(void) {
Serial.print("TX done......");
instance->txDone = true;
}
void LoRaTransmitter::OnTxTimeout(void) {
Radio.Sleep();
Serial.print("TX Timeout......");
}
Now, I'll also copy there the setup class that I used for the Hat:
import RPi.GPIO as GPIO
import serial
import time
class sx126x:
M0 = 22
M1 = 27
# if the header is 0xC0, then the LoRa register settings don't get lost when it powers off, and 0xC2 will be lost.
# cfg_reg = [0xC0, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0x00, 0x17, 0x43, 0x00, 0x00]
cfg_reg = [0xC2, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0x00, 0x12, 0x43, 0x00, 0x00]
get_reg = bytes(12)
rssi = False
addr = 65535
serial_n = ""
addr_temp = 0
# start frequency of the two LoRa modules
# E22-400T22S E22-900T22S
# 410~493MHz or 850~930MHz
start_freq = 850
# offset between start and end frequency of the two LoRa modules
# E22-400T22S E22-900T22S
# 410~493MHz or 850~930MHz
offset_freq = 18
# power = 22
# air_speed =2400
SX126X_UART_BAUDRATE_1200 = 0x00
SX126X_UART_BAUDRATE_2400 = 0x20
SX126X_UART_BAUDRATE_4800 = 0x40
SX126X_UART_BAUDRATE_9600 = 0x60
SX126X_UART_BAUDRATE_19200 = 0x80
SX126X_UART_BAUDRATE_38400 = 0xA0
SX126X_UART_BAUDRATE_57600 = 0xC0
SX126X_UART_BAUDRATE_115200 = 0xE0
SX126X_PACKAGE_SIZE_240_BYTE = 0x00
SX126X_PACKAGE_SIZE_128_BYTE = 0x40
SX126X_PACKAGE_SIZE_64_BYTE = 0x80
SX126X_PACKAGE_SIZE_32_BYTE = 0xC0
SX126X_Power_22dBm = 0x00
SX126X_Power_17dBm = 0x01
SX126X_Power_13dBm = 0x02
SX126X_Power_10dBm = 0x03
lora_air_speed_dic = {
1200:0x01,
2400:0x02,
4800:0x03,
9600:0x04,
19200:0x05,
38400:0x06,
62500:0x07
}
lora_power_dic = {
22:0x00,
17:0x01,
13:0x02,
10:0x03
}
lora_buffer_size_dic = {
240:SX126X_PACKAGE_SIZE_240_BYTE,
128:SX126X_PACKAGE_SIZE_128_BYTE,
64:SX126X_PACKAGE_SIZE_64_BYTE,
32:SX126X_PACKAGE_SIZE_32_BYTE
}
def __init__(self, serial_num, freq, addr, power, rssi, air_speed=2400, net_id=0, buffer_size = 240, crypt=0, relay=False, lbt=False, wor=False):
self.rssi = rssi
self.addr = addr
self.freq = freq
self.serial_n = serial_num
self.power = power
# Initial the GPIO for M0 and M1 Pin
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(self.M0, GPIO.OUT)
GPIO.setup(self.M1, GPIO.OUT)
GPIO.output(self.M0, GPIO.LOW)
GPIO.output(self.M1, GPIO.HIGH)
# The hardware UART of Pi3B+, Pi4B is /dev/ttyS0
self.ser = serial.Serial(serial_num, 9600)
self.ser.flushInput()
self.set(freq, addr, power, rssi, air_speed, net_id, buffer_size, crypt, relay, lbt, wor)
def set(self, freq, addr, power, rssi, air_speed=2400, \
net_id=0, buffer_size = 240, crypt=0, \
relay=False, lbt=False, wor=False):
self.send_to = addr
self.addr = addr
# We should pull up the M1 pin when setting the module
GPIO.output(self.M0, GPIO.LOW)
GPIO.output(self.M1, GPIO.HIGH)
time.sleep(0.1)
low_addr = addr & 0xff
high_addr = addr >> 8 & 0xff
net_id_temp = net_id & 0xff
if freq > 850:
freq_temp = freq - 850
self.start_freq = 850
self.offset_freq = freq_temp
elif freq >410:
freq_temp = freq - 410
self.start_freq = 410
self.offset_freq = freq_temp
air_speed_temp = self.lora_air_speed_dic.get(air_speed, None)
# if air_speed_temp != None
buffer_size_temp = self.lora_buffer_size_dic.get(buffer_size, None)
# if air_speed_temp != None:
power_temp = self.lora_power_dic.get(power, None)
#if power_temp != None:
if rssi:
# enable printing rssi value
rssi_temp = 0x80
else:
# disable printing rssi value
rssi_temp = 0x00
# get crypt
l_crypt = crypt & 0xff
h_crypt = crypt >> 8 & 0xff
if relay==False:
self.cfg_reg[3] = high_addr
self.cfg_reg[4] = low_addr
self.cfg_reg[5] = net_id_temp
self.cfg_reg[6] = self.SX126X_UART_BAUDRATE_9600 + air_speed_tempreadingto read noise rssi value when add 0x20 as follows
self.cfg_reg[7] = buffer_size_temp + power_temp + 0x20
self.cfg_reg[8] = freq_temp
# it will output a packet rssi value following received message
# when enabling the eighth bit with 06H register(rssi_temp = 0x80)
self.cfg_reg[9] = 0x43 + rssi_temp
self.cfg_reg[10] = h_crypt
self.cfg_reg[11] = l_crypt
else:
self.cfg_reg[3] = 0x01
self.cfg_reg[4] = 0x02
self.cfg_reg[5] = 0x03
self.cfg_reg[6] = self.SX126X_UART_BAUDRATE_9600 + air_speed_temp
# it will enable to read noise rssi value when add 0x20 as follow
self.cfg_reg[7] = buffer_size_temp + power_temp + 0x20
self.cfg_reg[8] = freq_temp
# it will output a packet rssi value following received message
# when enabling the
self.cfg_reg[9] = 0x03 + rssi_temp
self.cfg_reg[10] = h_crypt
self.cfg_reg[11] = l_crypt
self.ser.flushInput()
for i in range(2):
self.ser.write(bytes(self.cfg_reg))
r_buff = 0
time.sleep(0.2)
if self.ser.inWaiting() > 0:
time.sleep(0.1)
r_buff = self.ser.read(self.ser.inWaiting())
if r_buff[0] == 0xC1:
pass
# print("parameters setting is :", end='')
# for i in self.cfg_reg:
# print(hex(i), end=' ')
# print('\r\n')
# print("parameters return is :", end='')
# for i in r_buff:
# print(hex(i), end=' ')
# print('\r\n')
else:
pass
#print("parameters setting fail :", r_buff)
break
else:
print("setting fail, setting again")
self.ser.flushInput()
time.sleep(0.2)
print('\x1b[1A', end='\r')
if i == 1:
print("setting fail, Press Esc to Exit and run again")
# time.sleep(2)
# print('\x1b[1A', end='\r')
GPIO.output(self.M0, GPIO.LOW)
GPIO.output(self.M1, GPIO.LOW)
time.sleep(0.1)
def get_settings(self):
# the pin M1 of lora HAT must be high when entering setting mode and getting parameters
GPIO.output(M1, GPIO.HIGH)
time.sleep(0.1)
# send command to get setting parameters
self.ser.write(bytes([0xC1, 0x00, 0x09]))
if self.ser.inWaiting() > 0:
time.sleep(0.1)
self.get_reg = self.ser.read(self.ser.inWaiting())
# check the return characters from hat and print the setting parameters
if self.get_reg[0] == 0xC1 and self.get_reg[2] == 0x09:
fre_temp = self.get_reg[8]
addr_temp = self.get_reg[3] + self.get_reg[4]
air_speed_temp = self.get_reg[6] & 0x03
power_temp = self.get_reg[7] & 0x03
print("Frequence is {0}.125MHz.", fre_temp)
print("Node address is {0}.", addr_temp)
print("Air speed is {0} bps"+ lora_air_speed_dic.get(None, air_speed_temp))
print("Power is {0} dBm" + lora_power_dic.get(None, power_temp))
GPIO.output(M1, GPIO.LOW)
# the data format is as follows
# "node address, frequency, payload"
# "20, 868, Hello World"
def send(self, data):
GPIO.output(self.M1, GPIO.LOW)
GPIO.output(self.M0, GPIO.LOW)
time.sleep(0.1)
self.ser.write(data)
# if self.rssi == True:
# self.get_channel_rssi()
time.sleep(0.1)
def receive(self):
if self.ser.inWaiting() > 0:
time.sleep(0.5)
r_buff = self.ser.read(self.ser.inWaiting())
print("receive message from node address with frequence\033[1;32m %d, %d.125MHz\033[0m"%((r_buff[0]<<8)+r_buff[1], r_buff[2]+self.start_freq), end='\r\n', flush = True)
print("message is "+str(r_buff[3:-1]), end='\r\n')
# print the rssi
if self.rssi:
# print('\x1b[3A', end='\r')
print("the packet rssi value: -{0}dBm".format(256-r_buff[-1:][0]))
self.get_channel_rssi()
else:
pass
#print('\x1b[2A', end='\r')
def get_channel_rssi(self):
GPIO.output(self.M1, GPIO.LOW)
GPIO.output(self.M0, GPIO.LOW)
time.sleep(0.1)
self.ser.flushInput()
self.ser.write(bytes([0xC0, 0xC1, 0xC2, 0xC3, 0x00, 0x02]))
time.sleep(0.5)
re_temp = bytes(5)
if self.ser.inWaiting() > 0:
time.sleep(0.1)
re_temp = self.ser.read(self.ser.inWaiting())
if re_temp[0] == 0xC1 and re_temp[1] == 0x00 and re_temp[2] == 0x02:
print("the current noise rssi value: -{0}dBm".format(256-re_temp[3]))
# print("the last receive packet rssi value: -{0}dBm".format(256-re_temp[4]))
else:
# pass
print("receive rssi value fail")
# print("receive rssi value fail: ", re_temp)
I imagine the two devices should be able to communicate, due to the fact that in the end they are the same. Does someone have an idea of why the receiver HAT does not receive any message from the ESP32 LoRa sender?
But that's the issue here. While they are, at the core, the same – two SX1262 – they are fundamentally not. One, the ebyte crap, is a managed LoRa module, that you access through UART, and have very little control over. The other one is a "real" SX1262 that you manage yourself, and have control over, via SPI.
One of the things that you should have noticed is that the settings between both are VERY different. The main differences between the ebyte and normal SX1262 modules are:
addr
address setting in the ebyte? WTH is that?The 2 first points, which are I believe related, make it near impossible to use ebyte modules with normal LoRa modules. Their firmware seems to derive an SF value from that "air speed" thing. I wasn't able to match these airspeeds to BW / SF / CR values. For instance, looking at the LoRa calculator, the very common settings of SF 10, BW 7, CR 4/5 give you a data rate of 977 bauds per second. There's no way to twist these setting into something that nears 2,400.
I read somewhere of a guy who managed to make an ebyte communicate with an SX1276. Can't remember where or how, but it sounded complicated. Your best guess is to either stay within the ebyte ecosystem (which is crap anyway), or work with decent products, either SPI or UART-managed. There's a lot out there, my favorites being RAKwireless (for quality) and Heltec (for the cheaper stuff).