Interfacing Zerodrag Nexus1 2.4GHz ELRS Receiver with Arduino
If you have been following us for some time, you must have read the LoRa tutorial we published before. In that tutorial, we covered almost everything you need to know about the LoRa RF technology and showed how you can interface the RA-01/RA-02 LoRa modules with Arduino. RA-01/RA-02 uses the SX1278 LoRa chipset from Semtech. Today, we have a new LoRa product to interface. It is the Nexus1 ELRS receiver from Zerodrag Technologies, an avionics design and manufacturing company from India. Nexus1 uses the Semtech SX1280 LoRa chipset and is specially designed for drones and other small fixed wing aircrafts. It is used as a wireless receiver to receive control signals from a wireless controller. In this tutorial, we will learn everything about the Nexus1 receiver and learn how to interface it with any Arduino boards.
Zerodrag Nexus1
The Nexus1 is a 2.4 GHz ELRS receiver is based on the SX1280 LoRa chipset from Semtech. Unlike the SX1278, the SX1280 utilizes the 2.4 GHz ISM (Industrial, Scientific, Medical) radio frequency band for communication. 2.4 GHz is also the same frequency band used by Wi-Fi, Bluetooth and other wireless standards. The uniqueness of LoRa modulation is the ability to perform long range communication with a low power budget. Nexus1 utilizes this feature to facilitate reliable communication between a drone and its operator. The default firmware that comes with the Nexus1 receiver is the ELRS, which is an open-source software stack for radio communication. Nexus1 is an ELRS-certified receiver and can be programmed to talk to multiple types of radio transmitters (controllers) that support the ELRS protocol. Even though the SX1280 can both transmit and receive (half-duplex only), the Nexus1 receiver is primarily used as a receiver because its transmit power is limited. The board also integrates an ESP8285 Wi-Fi SoC from Espressif as well as a 2.4 GHz chip antenna for Wi-Fi communication.
Specifications
Specification | Value |
---|---|
Microcontroller | Espressif ESP8285 |
RF Transceiver Chip | Semtech SX1280 |
Working Frequency | 2.4450 GHz |
RF Technology | LoRa, FLRC, FSK, Wi-Fi (through ESP8285) |
Maximum Transmit Power | Up to +12.5 dBm (17.78 mW) |
Receiver Sensitivity | Up to -132 dBm |
Maximum Data Rate | LoRa 202 kbps, FLRC 1300 kbps |
Duplex Type | Half-Duplex |
Antenna Types | 1. UMCC (U.FL) for 2.4 GHz LoRa 2. Chip Antenna for 2.4 GHz Wi-Fi |
Supply Voltage | 5 V (5.5V Max) |
Interface/Logic Voltage | 3.3 V |
Maximum Current Consumption | – |
Dimensions | 20 x 13 mm |
Weight | 1.2 g |
Pinout
Schematic
Zerodrag does not release the schematic of the Nexus1 receiver. But the design common across many manufacturers that adheres to the ELRS standard. Following is an open-source 2.4GHz ELRS receiver design schematic that is based on the ESP8285 and the SX1281 instead of the SX1280. It also uses a PA+LNA (Power Amplifier + Low Noise Amplifier) chip to increase the output power and the sensitivity. If you ignore the PA+LNA and the small difference between the LoRa chipset, the remaining parts are almost the same. So this can be a good reference for the Nexus1 receiver.
SX1280
SX1280 and SX1281 are low-power, and long-range RF transceiver chips from Semtech. SX1280 supports LoRa, FLRC, GFSK, Ranging Engine and Advanced Engine. The SX1281 lacks the latter two features. Both SX1280 and SX1281 are available in a 4×4 mm QFN-24 SMD package.
Pinout
Pin Number | Pin Name | Type (I = Input, O = Output) | SPI Description | UART Description |
---|---|---|---|---|
0 | GND | – | Exposed Ground pad | |
1 | VR_PA | – | Regulated supply for the PA | |
2 | VDD_IN | I | Regulated supply input. Connect to Pin 12. | |
3 | NRESET | I | Reset signal, active low with internal pull-up at 50kO | |
4 | XTA | – | Reference oscillator connection or TCXO input | |
5 | GND | – | Ground | |
6 | XTB | – | Reference oscillator connection | |
7 | BUSY | O | Transceiver busy indicator | |
8 | DIO1 | I/O | Optional multi-purpose digital I/O | |
9 | DIO2 | I/O | Optional multi-purpose digital I/O | |
10 | DIO3 | I/O | Optional multi-purpose digital I/O | |
11 | VBAT_IO | I | Supply for the Digital IO interface (1.8V to 3.7V). Must be VBAT. | |
12 | DCC_FB | O | Regulated output voltage from the internal regulator | |
13 | GND | – | Ground | |
14 | DCC_SW | O | DC-DC Switcher Output | |
15 | VBAT | I | Supply for the RFIC (1.8V to 3.7V). Must be VBAT_IO. | |
16 | MISO_TX | O | SPI peripheral output | UART Transmit pin |
17 | MOSI_RX | I | SPI peripheral input | UART Receive pin |
18 | SCK_RTSN | I | SPI clock | UART Request To Send |
19 | NSS_CTS | I | SPI Chip Select | UART Clear To Send |
20 | GND | – | Ground | |
21 | GND | – | Ground | |
22 | RFIO | I/O | RF transmit output and receive input | |
23 | GND | – | Ground | |
24 | GND | – | Ground |
If you are new to the LoRa technology, we highly recommend reading the following tutorial from us. Even though it is written for the SX1278, the details are still applicable to the SX1280.
Interfacing RA-01/RA-02 SX1278 LoRa Modules with ESP32 using Arduino
Breakout Board
Because the Nexus1 receiver does not have a RESET or BOOT button, we soldered the board on a perfboard and added the button externally. We also added an auto-programming circuit so that we can use the RTS and DTR signal to program the board automatically. But for some reason, the auto-programming did not work.
To program the board, you need to press and hold the BOOT button (connecting the GPIO0
to GND) and then press and release the RESET button. This will cause the ESP8285 to go into the bootloader mode. You can also apply the power to the board while the BOOT button is pressed to achieve the same.
Wiring
Wiring the Nexus1 receiver is simple. The board breaks out 4 pins – 5V, TX, RX and GND. You can supply +5V to the power pin to start the module. Do not supply more than 5.5V to the pins as it can damage the LDO on the board. The TX and RX are UART pins coming from the ESP8285 and they can be connected to the UART pins of any Arduino board.
We will use a low-cost FT232L USB-Serial converter to power the Nexus1 boards and to communicate with it via a serial monitor. You only need to connect the 5V, TX, RX and GND pins. The RTS and DTR pins need not to be connected (we modified our FT232L adapters to break out the DTR pin instead of the CTS pin, if you are wondering).
ELRS
As mentioned earlier, ELRS is a completely open-source software stack for radio communication in different frequency bands. The name ELRS is an acronym for Express Long Range System. The ELRS firmware relies on the SX12xx series LoRa chipsets from Semtech and implements a light-weight datagram protocol on top of the LoRa physical interface (PHY). Due to the advantage of LoRa, devices with the ELRS firmware can achieve high packet rates up to 1 KHz as well as long ranges of up to several tens of kilometres. Currently, numerous manufacturers have ELRS-supported products including transmitters and receivers. To make the best use of the technology, ELRS also provide a configurator software to customize just about anything of your ELRS-supported products. You can find more details about the ELRS project from this FAQ page.
If you are good at embedded C/C++, you can clone/fork the ELRS project from GitHub and customize it as however you want. The software is a PlatformIO project that you can modify and build using VS Code and the PlatformIO extension. If you are new to VS Code and PlatformIO, we have great tutorials to get you started.
Getting Started with PlatformIO – Unified IDE for Embedded Software Development
How to Use VS Code for Creating and Uploading Arduino Sketches
FLRC
An exciting feature of the SX128x series of chips is the FLRC modem. FLRC stands for Fast Long Range Communication and as the name implies, it allows us to send and receive faster data packets with increased sensitivity (more range). FLRC uses the GFSK (Gaussian Frequency Shift Keying) modulation compared to the regular FSK (Frequency Shift Keying) modulation. But don’t be fooled by the name of FLRC itself, because LoRa will still give you a longer range compared to FLRC for the given transmitted power. But FLRC gives you more data rate with a decent range which is better than the regular FSK. You can combine FLRC with FEC (Forward Error Correction) to increase the robustness of the FLRC packets at a reduced data rate, due to the error correction overhead.
LoRa Ranging
LoRa Ranging is another new feature of the SX1280. It uses a packet similar to the LoRa Explicit (Variable Length) packet, and with the help of a ranging request originating from a transmitter, and a receiver responding to the ranging request, we can deduce the distance between both ends from the time it takes for the message to travel (Time-of-Flight).
SX12XX-LoRa Arduino Library
In the RA-01/RA-02 LoRa module tutorial, we used the LoRa Arduino library from Sandeep Mistry. This time, we can use the SX12XX-LoRa library from Stuart Robinson. It is a fully featured driver for the SX12xx series LoRa chipsets including the SX1280. The library has examples for every model, and supports both LoRa and FLRC transmissions on the SX128x series chips. We will modify two of the examples from the project to create an Arduino sketch that we can run on the Nexus1 receiver.
Arduino Code
LoRa Sender & Receiver
Following is the Arduino code we created to test the basic transmit and receive functionalities of the SX1280 LoRa chip on the Nexus1 receiver. We combined the examples 3_LoRa_Transmitter and 4_LoRa_Receiver to create it. You can download the project as a PlatformIO project and build it with the help of VS Code.
//===========================================================================================//
// Includes
#include <SPI.h>
#include <SX128XLT.h>
//===========================================================================================//
// Macros
#define PIN_LORA_CS 15
#define PIN_LORA_BUSY 5
#define PIN_LORA_RESET 2
#define PIN_LORA_DIO1 4
#define PIN_LORA_ANTSEL 9
#define PIN_LED1 16
#define LORA_DEVICE DEVICE_SX1280 // We need to define the device we are using
#define RXBUFFER_SIZE 255 //RX buffer size
//===========================================================================================//
// Globals
//LoRa Modem Parameters
const uint32_t Frequency = 2445000000; // Frequency of transmissions
const int32_t Offset = 0; // Offset frequency for calibration purposes
const uint8_t Bandwidth = LORA_BW_0400; // LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF7; // LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; // LoRa coding rate
const int8_t TXpower = 10; // Power for transmissions in dBm
const uint16_t packet_delay = 1000; // mS delay between packets
uint8_t TXPacketL;
uint32_t TXPacketCount, startmS, endmS;
uint8_t buff[] = "Hello World 1234567890";
uint32_t RXpacketCount;
uint32_t errors;
uint8_t RXBUFFER [RXBUFFER_SIZE]; // Create the buffer that received packets are copied into
uint8_t RXPacketL; // Stores length of packet received
int16_t PacketRSSI; // Stores RSSI of received packet
int8_t PacketSNR; // Stores signal to noise ratio of received packet
SX128XLT loraDevice; // Create a library class instance called loraDevice
//===========================================================================================//
// Forward Declarations
void initLoRa();
void initLoRaTx();
void initLoRaRx();
void loraTask();
void loraSender();
void loraReceiver();
void printTxPacketOK();
void printTxPacketError();
void printRxPacketOK();
void printRxPacketError();
void printElapsedTime();
void flashLED (uint16_t flashes, uint16_t delaymS);
//===========================================================================================//
void setup() {
Serial.begin (115200);
while (!Serial);
Serial.println();
Serial.println ("SX1280 LoRa Test");
ESP.wdtDisable();
*((volatile uint32_t*) 0x60000900) &= ~(1); // Hardware WDT OFF
// *((volatile uint32_t*) 0x60000900) |= 1; // Hardware WDT ON
SPI.begin();
initLoRa();
}
//===========================================================================================//
void loop() {
loraTask();
}
//===========================================================================================//
void initLoRa() {
#if OPT_IS_SENDER == 1
initLoRaTx();
#elif OPT_IS_SENDER == 0
initLoRaRx();
#endif
}
//===========================================================================================//
void initLoRaTx() {
pinMode (PIN_LED1, OUTPUT);
// pinMode (PIN_LORA_ANTSEL, OUTPUT);
// digitalWrite (PIN_LORA_ANTSEL, LOW); // Setting Antenna Select to onboard one
flashLED (2, 125); // Two quick LED flashes to indicate program start
Serial.println (F("initLoRaTx(): Starting transmitter.."));
// SPI beginTranscation is normally part of library routines, but if it is disabled in library
// a single instance is needed here, so uncomment the program line below
// SPI.beginTransaction (SPISettings (8000000, MSBFIRST, SPI_MODE0));
// Setup hardware pins used by device, then check if device is found.
if (loraDevice.begin (PIN_LORA_CS, PIN_LORA_RESET, PIN_LORA_BUSY, PIN_LORA_DIO1, -1, -1, LORA_DEVICE)) {
Serial.println (F("initLoRaTx(): LoRa device is found."));
flashLED (2, 125); // Two further quick LED flashes to indicate device found
delay (1000);
}
else {
Serial.println (F("initLoRaTx(): No device is responding."));
while (1) {
flashLED (50, 50); // Long fast speed LED flash indicates device error
}
}
// The 'Setup LoRa device' list below can be replaced with a single function call;
// loraDevice.setupLoRa (Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
// Setup LoRa device
loraDevice.setMode (MODE_STDBY_RC);
loraDevice.setRegulatorMode (USE_LDO);
loraDevice.setPacketType (PACKET_TYPE_LORA);
loraDevice.setRfFrequency (Frequency, Offset);
loraDevice.setBufferBaseAddress (0, 0);
loraDevice.setModulationParams (SpreadingFactor, Bandwidth, CodeRate);
loraDevice.setPacketParams (12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0);
loraDevice.setDioIrqParams (IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0);
loraDevice.setHighSensitivity();
Serial.println();
loraDevice.printModemSettings(); // Reads and prints the configured LoRa settings, useful check
Serial.println();
loraDevice.printOperatingSettings(); // Reads and prints the configured operating settings, useful check
Serial.println();
Serial.println();
loraDevice.printRegisters (0x900, 0x9FF); // Print contents of device registers
Serial.println();
Serial.println();
Serial.println (F("initLoRaTx(): Transmitter is ready."));
Serial.println();
}
//===========================================================================================//
void initLoRaRx() {
pinMode (PIN_LED1, OUTPUT); // Setup pin as output for indicator LED
flashLED (2, 125); // Two quick LED flashes to indicate program start
Serial.println (F("initLoRaRx(): Starting receiver.."));
// SPI beginTranscation is normally part of library routines, but if it is disabled in the library,
// a single instance is needed here, so uncomment the program line below.
// SPI.beginTransaction (SPISettings (8000000, MSBFIRST, SPI_MODE0));
// Setup hardware pins used by device, then check if device is found.
if (loraDevice.begin (PIN_LORA_CS, PIN_LORA_RESET, PIN_LORA_BUSY, PIN_LORA_DIO1, -1, -1, LORA_DEVICE)) {
Serial.println (F("initLoRaRx(): LoRa device is found."));
flashLED (2, 125);
delay (1000);
}
else {
Serial.println (F("initLoRaRx(): No device is responding."));
while (1) {
flashLED (50, 50); // Long fast speed LED flash indicates device error
}
}
// The 'Setup LoRa device' list below can be replaced with a single function call;
// loraDevice.setupLoRa (Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
// Setup LoRa device
loraDevice.setMode (MODE_STDBY_RC);
loraDevice.setRegulatorMode (USE_LDO);
loraDevice.setPacketType (PACKET_TYPE_LORA);
loraDevice.setRfFrequency (Frequency, Offset);
loraDevice.setBufferBaseAddress (0, 0);
loraDevice.setModulationParams (SpreadingFactor, Bandwidth, CodeRate);
loraDevice.setPacketParams (12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0);
loraDevice.setDioIrqParams (IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0);
loraDevice.setHighSensitivity();
Serial.println();
loraDevice.printModemSettings(); // Reads and prints the configured LoRa settings, useful check
Serial.println();
loraDevice.printOperatingSettings(); // Reads and prints the configured operting settings, useful check
Serial.println();
Serial.println();
loraDevice.printRegisters (0x900, 0x9FF); // Print contents of device registers
Serial.println();
Serial.println();
Serial.print (F("initLoRaRx(): Receiver is ready - RXBUFFER_SIZE "));
Serial.println (RXBUFFER_SIZE);
Serial.println();
}
//===========================================================================================//
void loraTask() {
#if OPT_IS_SENDER == 1
loraSender();
#elif OPT_IS_SENDER == 0
loraReceiver();
#endif
}
//===========================================================================================//
void loraSender() {
Serial.print (F("loraSender(): Sending message with power "));
Serial.print (TXpower); // Print the transmit power defined
Serial.println (F(" dBm.."));
Serial.println (F("loraSender(): Packet = "));
Serial.flush();
TXPacketL = sizeof (buff); // Set TXPacketL to length of array
buff [TXPacketL - 1] = '*'; // Replace null character at buffer end so its visible on reciver
loraDevice.printASCIIPacket (buff, TXPacketL); // Print the buffer (the sent packet) as ASCII
digitalWrite (PIN_LED1, HIGH);
startmS = millis(); // Start transmit timer
if (loraDevice.transmit (buff, TXPacketL, 10000, TXpower, WAIT_TX)) { // Will return packet length sent if OK, otherwise 0 if transmit, timeout 10 seconds
endmS = millis(); // Packet sent, note end time
TXPacketCount++;
Serial.println();
printTxPacketOK();
}
else {
Serial.println();
printTxPacketError(); // Transmit packet returned 0, there was an error
}
digitalWrite (PIN_LED1, LOW);
Serial.println();
delay (packet_delay); // Have a delay between packets
}
//===========================================================================================//
void loraReceiver() {
RXPacketL = loraDevice.receive (RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); // Wait for a packet to arrive with 60seconds (60000mS) timeout
digitalWrite (PIN_LED1, HIGH); // Something has happened
PacketRSSI = loraDevice.readPacketRSSI(); // Read the recived RSSI value
PacketSNR = loraDevice.readPacketSNR(); // Read the received SNR value
Serial.print (F("loraReceiver(): RSSI = "));
Serial.print (PacketRSSI); // Print the RSSI value
Serial.print (F(", SNR = "));
Serial.print (PacketSNR); // Print the SNR value
Serial.println (F(" dB"));
if (RXPacketL == 0) { // If the loraDevice.receive() function detects an error, RXpacketL == 0
printRxPacketError();
}
else {
printRxPacketOK();
}
digitalWrite (PIN_LED1, LOW); // LED off
Serial.println();
}
//===========================================================================================//
void printTxPacketOK() {
uint16_t localCRC;
Serial.println ("printTxPacketOK(): Status:");
Serial.print (F("BytesSent = "));
Serial.println (TXPacketL); // Print transmitted packet length
localCRC = loraDevice.CRCCCITT (buff, TXPacketL, 0xFFFF);
Serial.print (F("CRC = 0x"));
Serial.println (localCRC, HEX); // Print CRC of sent packet
Serial.print (F("Transmit Time = "));
Serial.print (endmS - startmS); // Print transmit time of packet
Serial.println (F(" mS"));
Serial.print (F("Packets Counter = "));
Serial.println (TXPacketCount); // Print total of packets sent OK
}
//===========================================================================================//
void printTxPacketError() {
//if here there was an error transmitting packet
uint16_t IRQStatus;
IRQStatus = loraDevice.readIrqStatus(); //read the the interrupt register
Serial.print (F(" SendError,"));
Serial.print (F("Length,"));
Serial.print (TXPacketL); //print transmitted packet length
Serial.print (F(",IRQreg,"));
Serial.print (IRQStatus, HEX); //print IRQ status
loraDevice.printIrqStatus(); //prints the text of which IRQs set
}
//===========================================================================================//
void printRxPacketOK() {
uint16_t IRQStatus, localCRC;
IRQStatus = loraDevice.readIrqStatus(); //read the LoRa device IRQ status register
RXpacketCount++;
printElapsedTime(); //print elapsed time to Serial Monitor
Serial.println (F(", Received Packet = "));
loraDevice.printASCIIPacket (RXBUFFER, RXPacketL); //print the packet as ASCII characters
Serial.println();
localCRC = loraDevice.CRCCCITT (RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER
Serial.print (F("CRC = 0x")); //contents, not the LoRa device internal CRC
Serial.println (localCRC, HEX);
Serial.print (F("RSSI = "));
Serial.print (PacketRSSI);
Serial.println (F(" dBm"));
Serial.print (F("SNR = "));
Serial.print (PacketSNR);
Serial.println (F(" dB"));
Serial.print (F("Length = "));
Serial.println (RXPacketL);
Serial.print (F("Packets = "));
Serial.println (RXpacketCount);
Serial.print (F("Errors = "));
Serial.println (errors);
Serial.print (F("IRQreg = 0x"));
Serial.print (IRQStatus, HEX);
Serial.println();
}
//===========================================================================================//
void printRxPacketError() {
uint16_t IRQStatus;
IRQStatus = loraDevice.readIrqStatus(); // Read the LoRa device IRQ status register
printElapsedTime(); // Print elapsed time to Serial Monitor
if (IRQStatus & IRQ_RX_TIMEOUT) { // Check for an RX timeout
Serial.println (F("printRxPacketError(): RXTimeout"));
}
else {
errors++;
Serial.println (F("printRxPacketError(): Status:"));
Serial.print (F("RSSI = "));
Serial.print (PacketRSSI);
Serial.println (F(" dBm"));
Serial.print (F("SNR = "));
Serial.print (PacketSNR);
Serial.println (F(" dB"));
Serial.print (F("Length = "));
Serial.println (loraDevice.readRXPacketL()); // Get the real packet length
Serial.print (F("Packets = "));
Serial.println (RXpacketCount);
Serial.print (F("Errors = "));
Serial.println (errors);
Serial.print (F("IRQreg = 0x"));
Serial.print (IRQStatus, HEX);
loraDevice.printIrqStatus(); // Print the names of the IRQ registers set
}
delay (250); // Gives a longer buzzer and LED flash for error
}
//===========================================================================================//
void flashLED (uint16_t flashes, uint16_t delaymS) {
uint16_t index;
for (index = 1; index <= flashes; index++) {
digitalWrite (PIN_LED1, HIGH);
delay (delaymS);
digitalWrite (PIN_LED1, LOW);
delay (delaymS);
}
}
//===========================================================================================//
void printElapsedTime() {
float seconds;
seconds = millis() / 1000;
Serial.print (F("Time = "));
Serial.print (seconds, 0);
Serial.print (F("s"));
}
//===========================================================================================//
main.cppSince we have both the transmitter and receiver code in a single main.cpp file, we are using the PlatformIO configuration file to create two targets Sender and Receiver. There is a C/C++ macro called OPT_IS_SENDER
in the configuration file with different values for the Sender and Receiver targets. When you change the PlatformIO build targets, the code will be automatically compiled for both the Sender and Receiver examples. Following is our platformio.ini configuration file.
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env]
platform = espressif8266
board = esp8285
framework = arduino
lib_deps =
https://github.com/StuartsProjects/SX12XX-LoRa.git
extra_scripts = extra_upload.py
[env:Sender]
upload_protocol = esptool
upload_speed = 2000000
upload_port = COM26
build_flags = -D OPT_IS_SENDER=1
[env:Receiver]
upload_protocol = esptool
upload_speed = 2000000
upload_port = COM13
build_flags = -D OPT_IS_SENDER=0
INITo help with uploading the programs to two different devices, we have also added an extra Python script that you can run with then following command in the Terminal. Running the command will compile both targets and upload them sequentially. Make sure to edit the upload_port
variable in the configuration file as per the COM port assigned in your computer.
pio run -t upload
TerminalFollowing is the Python script.
import os
from SCons.Script import DefaultEnvironment
env = DefaultEnvironment()
def after_build (source, target, env):
print ("Uploading to Sender...")
env.Execute ("pio run -t upload -e Sender")
print ("Uploading to Receiver...")
env.Execute ("pio run -t upload -e Receiver")
env.AddPostAction ("buildprog", after_build)
extra_upload.pyYou can press the BOOT button and then press and release the RESET button to begin uploading the new code. After the uploading is complete, you can open any serial monitor applications and create two instances; one for the Sender and one for the Receiver. We are using the VS Code’s Serial Monitor plugin here. The LED on the Sender will flash whenever it is sending a message. The LED on the Receiver will blink when it receives a message. Following is the serial monitor log from the Sender. We are transmitting at an output power of 10 dBm here.
SX1280 LoRa Test
initLoRaTx(): Starting transmitter..
initLoRaTx(): LoRa device is found.
SX1280,PACKET_TYPE_LORA,2444999936hz,SF7,BW406250,CR4:5
SX1280,PACKET_TYPE_LORA,Preamble_12,Explicit,PayloadL_255,CRC_ON,IQ_NORMAL,LNAgain_HighSensitivity
Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x900 80 FF 77 41 20 FA BC 13 C1 80 00 00 00 00 00 61
0x910 9C 44 00 00 00 19 00 00 00 19 87 65 43 21 7F FF
0x920 FF FF FF 00 70 37 12 50 D0 80 00 C0 5F D2 8F 0A
0x930 00 C0 00 00 00 24 00 21 28 B0 30 0D 01 51 63 0C
0x940 58 0B 32 0A 16 24 6B 96 00 18 00 00 00 00 00 00
0x950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x960 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF
0x970 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 04
0x980 00 0B 18 70 00 00 00 4C 00 F0 64 00 00 00 00 00
0x990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9A0 00 08 EC B8 9D 8A E6 66 04 00 00 00 00 00 00 00
0x9B0 00 08 EC B8 9D 8A E6 66 04 00 00 00 00 00 00 00
0x9C0 00 16 00 3F E8 01 FF FF FF FF 5E 4D 25 10 55 55
0x9D0 55 55 55 55 55 55 55 55 55 55 55 55 55 00 00 00
0x9E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
initLoRaTx(): Transmitter is ready.
loraSender(): Sending message with power 10 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 21 mS
Packets Counter = 1
loraSender(): Sending message with power 10 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 21 mS
Packets Counter = 2
loraSender(): Sending message with power 10 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 21 mS
Packets Counter = 3
loraSender(): Sending message with power 10 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 21 mS
Packets Counter = 4
loraSender(): Sending message with power 10 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 21 mS
Packets Counter = 5
loraSender(): Sending message with power 10 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 21 mS
Packets Counter = 6
loraSender(): Sending message with power 10 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 20 mS
Packets Counter = 7
Transmitter Serial MonitorFollowing is the serial monitor output from the Receiver.
SX1280 LoRa Test
initLoRaRx(): Starting receiver..
initLoRaRx(): LoRa device is found.
SX1280,PACKET_TYPE_LORA,2444999936hz,SF7,BW406250,CR4:5
SX1280,PACKET_TYPE_LORA,Preamble_12,Explicit,PayloadL_255,CRC_ON,IQ_NORMAL,LNAgain_HighSensitivity
Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x900 80 FF 77 41 20 FA BC 13 C1 80 00 00 00 00 00 61
0x910 9C 44 00 00 00 19 00 00 00 19 87 65 43 21 7F FF
0x920 FF FF FF 00 70 37 12 50 D0 80 00 C0 5F D2 8F 0A
0x930 00 C0 00 00 00 24 00 21 28 B0 30 0D 01 51 63 0C
0x940 58 0B 32 0A 16 24 6B 96 00 18 00 00 00 00 00 00
0x950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x960 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF
0x970 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 04
0x980 00 0B 18 70 00 00 00 4C 00 F0 64 00 00 00 00 00
0x990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9A0 00 08 EC B8 9D 8A E6 66 04 00 00 00 00 00 00 00
0x9B0 00 08 EC B8 9D 8A E6 66 04 00 00 00 00 00 00 00
0x9C0 00 16 00 3F E8 01 FF FF FF FF 5E 4D 25 10 55 55
0x9D0 55 55 55 55 55 55 55 55 55 55 55 55 55 00 00 00
0x9E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
initLoRaRx(): Receiver is ready - RXBUFFER_SIZE 255
loraReceiver(): RSSI = -51, SNR = 13 dB
Time = 3s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -51 dBm
SNR = 13 dB
Length = 23
Packets = 1
Errors = 0
IRQreg = 0x8012
loraReceiver(): RSSI = -51, SNR = 13 dB
Time = 4s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -51 dBm
SNR = 13 dB
Length = 23
Packets = 2
Errors = 0
IRQreg = 0x8012
loraReceiver(): RSSI = -50, SNR = 11 dB
Time = 5s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -50 dBm
SNR = 11 dB
Length = 23
Packets = 3
Errors = 0
IRQreg = 0x8012
loraReceiver(): RSSI = -51, SNR = 14 dB
Time = 6s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -51 dBm
SNR = 14 dB
Length = 23
Packets = 4
Errors = 0
IRQreg = 0x8012
loraReceiver(): RSSI = -51, SNR = 14 dB
Time = 7s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -51 dBm
SNR = 14 dB
Length = 23
Packets = 5
Errors = 0
IRQreg = 0x8012
loraReceiver(): RSSI = -50, SNR = 13 dB
Time = 8s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -50 dBm
SNR = 13 dB
Length = 23
Packets = 6
Errors = 0
IRQreg = 0x8012
loraReceiver(): RSSI = -50, SNR = 13 dB
Time = 9s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -50 dBm
SNR = 13 dB
Length = 23
Packets = 7
Errors = 0
IRQreg = 0x8012
Receiver Serial MonitorThe RSSI (Received Signal Strength Indicator) indicates how strong the received RF signal is. Its value will increase when the signal strength increases. You can test this by moving the transmitter and receiver away from and then closer to each other. The signal strength will drop significantly if you keep the antennas too close to each other, contrary to what you might expect. The SNR (Signal to Noise Ratio) indicates the ratio between noise (any signals that can not be decoded) and the information signal. Its value will also increase when the signal quality increases compared to the background noise.
Code Explained
Following is the overview of the steps we need to follow to get the transmitter to working.
- Initialize the SX1280 as a transmitter by loading the registers with the transmitter configurations. This include common parameters for the LoRa PHY as well as transmitter specific values. The communication between the microcontroller and the SX1280 is carried out through the SPI. If the configuration is loaded correctly, proceed to the next step.
- Use the built-in library function to load the data buffer and supply the transmit power and a wait time. The function will try to transmit the data within the specified time period.
- Wait for the interrupt from the
DIO1
pin or the IRQ software register. If the transmission is not complete in time, print the error status. - If the packet is transmitted within the specified time, print the time it took to transmit the data.
- Count the number of packets sent.
In the Arduino’s setup()
function, we have the initLoRa()
function to initialize the LoRa chip. initLoRa()
is a common function that will initialize the SX1280 based on the target type, which is determined by the value of OPT_IS_SENDER
. If the value is 1
, then the SX1280 is initialized as a transmitter and if the value is 0
, it is initialized as a receiver. Functions initLoRaTx()
and initLoRaTx()
are used for the initializations.
In the initLoRaTx()
function, we first try to initialize the SPI interface and try to communicate with the SX1280. If we can not communicate with the chip, it could mean that the pin assignments or the device type configurations are not correct. After that, we can load the RF configuration to the SX1280 with the following lines.
// Setup LoRa device
loraDevice.setMode (MODE_STDBY_RC);
loraDevice.setRegulatorMode (USE_LDO);
loraDevice.setPacketType (PACKET_TYPE_LORA);
loraDevice.setRfFrequency (Frequency, Offset);
loraDevice.setBufferBaseAddress (0, 0);
loraDevice.setModulationParams (SpreadingFactor, Bandwidth, CodeRate);
loraDevice.setPacketParams (12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0);
loraDevice.setDioIrqParams (IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0);
loraDevice.setHighSensitivity();
C++For some of the parameters, we are using custom values and for some other we are using the default values. If you only want to specify the important values and keep the other parameters to their defaults, you can use following single line to load the configuration.
loraDevice.setupLoRa (Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);
C++After that we can print the configuration and the snapshot of the configuration registers. The code then proceeds to the loop()
function where it repeatedly calls the LoRa related function loraTask()
. Based on the type of target, the function will call either loraSender()
or the loraReceiver()
. In the loraSender()
function, we simply fetch the data from a buffer, calculate the length and use the transmit()
function to send it. If the transmission is successful, we will print the time it took to transmit it. If the transmission ends up in an error, we will print the error status.
The process for the receiver is similar.
- Initialize the SX1280 as a transmitter by loading the registers with the receiver configurations, which is the same as the transmitter. The communication between the microcontroller and the SX1280 is carried out through the SPI. If the configuration is loaded correctly, proceed to the next step.
- Wait for a message to arrive. The SX1280 will generate an interrupt through the
DIO1
pin of the IRQ registers. - When an interrupt is received, read the receive buffer of the SX1280 and save it to a local buffer and print it along with the RSSI and SNR values.
FLRC Sender & Receiver
Here, we combined the examples 52_FLRC_Transmitter and 53_FLRC_Receiver to create a single PlatformIO project. Similar to the previous example, you can choose the target ton compile and upload the code.
//===========================================================================================//
// Includes
#include <SPI.h>
#include <SX128XLT.h>
//===========================================================================================//
// Macros
#define PIN_LORA_CS 15
#define PIN_LORA_BUSY 5
#define PIN_LORA_RESET 2
#define PIN_LORA_DIO1 4
#define PIN_LORA_ANTSEL 9
#define PIN_LED1 16
#define LORA_DEVICE DEVICE_SX1280 // We need to define the device we are using
#define RXBUFFER_SIZE 127 // RX buffer size
//===========================================================================================//
// Globals
//FLRC Modem Parameters
const uint32_t Frequency = 2445000000; // Frequency of transmissions
const int32_t Offset = 0; // Offset frequency for calibration purposes
const uint8_t BandwidthBitRate = FLRC_BR_1_300_BW_1_2; // FLRC bandwidth and bit rate, 1.3Mbs
const uint8_t CodingRate = FLRC_CR_1_0; // FLRC coding rate
const uint8_t BT = RADIO_MOD_SHAPING_BT_1_0; // FLRC BT
const uint32_t Syncword = 0x01234567; // FLRC uses syncword
// Transmitter configuration
const int8_t TXpower = 0; // Power for transmissions in dBm
const uint16_t packet_delay = 1000; // mS delay between packets
uint8_t TXPacketL;
uint32_t TXPacketCount, startmS, endmS;
uint8_t buff[] = "Hello World 1234567890";
// Receiver configuration
uint32_t RXpacketCount;
uint32_t errors;
uint8_t RXBUFFER [RXBUFFER_SIZE]; // Create the buffer that received packets are copied into
uint8_t RXPacketL; // Stores length of packet received
int16_t PacketRSSI; // Stores RSSI of received packet
int8_t PacketSNR; // Stores signal to noise ratio of received packet
SX128XLT loraDevice; // Create a library class instance called loraDevice
//===========================================================================================//
// Forward Declarations
void initLoRa();
void initLoRaTx();
void initLoRaRx();
void loraTask();
void loraSender();
void loraReceiver();
void printTxPacketOK();
void printTxPacketError();
void printRxPacketOK();
void printRxPacketError();
void printElapsedTime();
void flashLED (uint16_t flashes, uint16_t delaymS);
//===========================================================================================//
void setup() {
Serial.begin (115200);
while (!Serial);
Serial.println ("\nSX1280 LoRa Test");
ESP.wdtDisable();
*((volatile uint32_t*) 0x60000900) &= ~(1); // Hardware WDT OFF
// *((volatile uint32_t*) 0x60000900) |= 1; // Hardware WDT ON
SPI.begin();
initLoRa();
}
//===========================================================================================//
void loop() {
loraTask();
}
//===========================================================================================//
void initLoRa() {
#if OPT_IS_SENDER == 1
initLoRaTx();
#elif OPT_IS_SENDER == 0
initLoRaRx();
#endif
}
//===========================================================================================//
void initLoRaTx() {
pinMode (PIN_LED1, OUTPUT);
// pinMode (PIN_LORA_ANTSEL, OUTPUT);
// digitalWrite (PIN_LORA_ANTSEL, LOW); // Setting Antenna Select to onboard one
flashLED (2, 125); // Two quick LED flashes to indicate program start
Serial.println (F("initLoRaTx(): Starting FLRC transmitter.."));
// SPI beginTranscation is normally part of library routines, but if it is disabled in library
// a single instance is needed here, so uncomment the program line below
// SPI.beginTransaction (SPISettings (8000000, MSBFIRST, SPI_MODE0));
// Setup hardware pins used by device, then check if device is found.
if (loraDevice.begin (PIN_LORA_CS, PIN_LORA_RESET, PIN_LORA_BUSY, PIN_LORA_DIO1, -1, -1, LORA_DEVICE)) {
Serial.println (F("initLoRaTx(): FLRC device is found."));
flashLED (2, 125); // Two further quick LED flashes to indicate device found
delay (1000);
}
else {
Serial.println (F("initLoRaTx(): No FLRC device is responding."));
while (1) {
flashLED (50, 50); // Long fast speed LED flash indicates device error
}
}
//Setup FLRC
loraDevice.setupFLRC (Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
//The full details of the setupFLRC function call above are listed below
//loraDevice.setMode (MODE_STDBY_RC);
//loraDevice.setRegulatorMode (USE_LDO);
//loraDevice.setPacketType (PACKET_TYPE_FLRC);
//loraDevice.setRfFrequency (Frequency, Offset);
//loraDevice.setBufferBaseAddress (0, 0);
//loraDevice.setModulationParams (BandwidthBitRate, CodingRate, BT);
//loraDevice.setPacketParams (PREAMBLE_LENGTH_32_BITS, FLRC_SYNC_WORD_LEN_P32S, RADIO_RX_MATCH_SYNCWORD_1, RADIO_PACKET_VARIABLE_LENGTH, 127, RADIO_CRC_3_BYTES, RADIO_WHITENING_OFF);
//loraDevice.setDioIrqParams (IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); //set for IRQ on TX done and timeout on DIO1
//loraDevice.setSyncWord1 (Syncword);
Serial.println();
loraDevice.printModemSettings(); // Reads and prints the configured LoRa settings, useful check
Serial.println();
loraDevice.printOperatingSettings(); // Reads and prints the configured operating settings, useful check
Serial.println();
Serial.println();
loraDevice.printRegisters (0x900, 0x9FF); // Print contents of device registers
Serial.println();
Serial.println();
Serial.println (F("initLoRaTx(): FLRC transmitter is ready."));
Serial.println();
}
//===========================================================================================//
void initLoRaRx() {
pinMode (PIN_LED1, OUTPUT); // Setup pin as output for indicator LED
flashLED (2, 125); // Two quick LED flashes to indicate program start
Serial.println (F("initLoRaRx(): Starting FLRC receiver.."));
// SPI beginTranscation is normally part of library routines, but if it is disabled in the library,
// a single instance is needed here, so uncomment the program line below.
// SPI.beginTransaction (SPISettings (8000000, MSBFIRST, SPI_MODE0));
// Setup hardware pins used by device, then check if device is found.
if (loraDevice.begin (PIN_LORA_CS, PIN_LORA_RESET, PIN_LORA_BUSY, PIN_LORA_DIO1, -1, -1, LORA_DEVICE)) {
Serial.println (F("initLoRaRx(): FLRC device is found."));
flashLED (2, 125);
delay (1000);
}
else {
Serial.println (F("initLoRaRx(): No FLRC device is responding."));
while (1) {
flashLED (50, 50); // Long fast speed LED flash indicates device error
}
}
// Setup FLRC
loraDevice.setupFLRC (Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
// The full details of the setupFLRC function call above are listed below.
//loraDevice.setMode (MODE_STDBY_RC);
//loraDevice.setRegulatorMode (USE_LDO);
//loraDevice.setPacketType (PACKET_TYPE_FLRC);
//loraDevice.setRfFrequency (Frequency, Offset);
//loraDevice.setBufferBaseAddress (0, 0);
//loraDevice.setModulationParams (BandwidthBitRate, CodingRate, BT);
//loraDevice.setPacketParams (PREAMBLE_LENGTH_32_BITS, FLRC_SYNC_WORD_LEN_P32S, RADIO_RX_MATCH_SYNCWORD_1, RADIO_PACKET_VARIABLE_LENGTH, 127, RADIO_CRC_3_BYTES, RADIO_WHITENING_OFF);
//loraDevice.setDioIrqParams (IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0); // Set for IRQ on TX done and timeout on DIO1
//loraDevice.setSyncWord1 (Syncword);
loraDevice.setFLRCPayloadLengthReg (127); // FLRC will filter packets on receive according to length, so set to longest packet
Serial.println();
loraDevice.printModemSettings(); // Reads and prints the configured modem settings, useful check
Serial.println();
loraDevice.printOperatingSettings(); // Reads and prints the configured operting settings, useful check
Serial.println();
Serial.println();
loraDevice.printRegisters (0x900, 0x9FF); // Print contents of device registers
Serial.println();
Serial.println();
Serial.print (F("initLoRaRx(): Receiver is ready - RXBUFFER_SIZE "));
Serial.println (RXBUFFER_SIZE);
Serial.println();
}
//===========================================================================================//
void loraTask() {
#if OPT_IS_SENDER == 1
loraSender();
#elif OPT_IS_SENDER == 0
loraReceiver();
#endif
}
//===========================================================================================//
void loraSender() {
Serial.print (F("loraSender(): Sending message with power "));
Serial.print (TXpower); // Print the transmit power defined
Serial.println (F(" dBm.."));
Serial.println (F("loraSender(): Packet = "));
Serial.flush();
TXPacketL = sizeof (buff); // Set TXPacketL to length of array
buff [TXPacketL - 1] = '*'; // Replace null character at buffer end so its visible on receiver
loraDevice.printASCIIPacket (buff, TXPacketL); // Print the buffer (the sent packet) as ASCII
digitalWrite (PIN_LED1, HIGH);
startmS = millis(); // Start transmit timer
TXPacketL = loraDevice.transmit (buff, TXPacketL, 10000, TXpower, WAIT_TX); //will return 0 if transmit fails, timeout 10 seconds
if (TXPacketL > 0) { // Will return packet length sent if OK, otherwise 0 if transmit, timeout 10 seconds
endmS = millis(); // Packet sent, note end time
TXPacketCount++;
Serial.println();
printTxPacketOK();
}
else {
Serial.println();
printTxPacketError(); // Transmit packet returned 0, there was an error
}
digitalWrite (PIN_LED1, LOW);
Serial.println();
delay (packet_delay); // Have a delay between packets
}
//===========================================================================================//
void loraReceiver() {
RXPacketL = loraDevice.receive (RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); // Wait for a packet to arrive with 60seconds (60000mS) timeout
digitalWrite (PIN_LED1, HIGH); // Something has happened
PacketRSSI = loraDevice.readPacketRSSI(); // Read the recived RSSI value
PacketSNR = loraDevice.readPacketSNR(); // Read the received SNR value
Serial.print (F("loraReceiver(): RSSI = "));
Serial.print (PacketRSSI); // Print the RSSI value
Serial.print (F(", SNR = "));
Serial.print (PacketSNR); // Print the SNR value
Serial.println (F(" dB"));
if (RXPacketL == 0) { // If the loraDevice.receive() function detects an error, RXpacketL == 0
printRxPacketError();
}
else {
printRxPacketOK();
}
digitalWrite (PIN_LED1, LOW); // LED off
Serial.println();
}
//===========================================================================================//
void printTxPacketOK() {
uint16_t localCRC;
Serial.println ("printTxPacketOK(): Status:");
Serial.print (F("BytesSent = "));
Serial.println (TXPacketL); // Print transmitted packet length
localCRC = loraDevice.CRCCCITT (buff, TXPacketL, 0xFFFF);
Serial.print (F("CRC = 0x"));
Serial.println (localCRC, HEX); // Print CRC of sent packet
Serial.print (F("Transmit Time = "));
Serial.print (endmS - startmS); // Print transmit time of packet
Serial.println (F(" mS"));
Serial.print (F("Packets Counter = "));
Serial.println (TXPacketCount); // Print total of packets sent OK
}
//===========================================================================================//
void printTxPacketError() {
//if here there was an error transmitting packet
uint16_t IRQStatus;
IRQStatus = loraDevice.readIrqStatus(); //read the the interrupt register
Serial.print (F(" SendError,"));
Serial.print (F("Length,"));
Serial.print (TXPacketL); //print transmitted packet length
Serial.print (F(",IRQreg,"));
Serial.print (IRQStatus, HEX); //print IRQ status
loraDevice.printIrqStatus(); //prints the text of which IRQs set
}
//===========================================================================================//
void printRxPacketOK() {
uint16_t IRQStatus, localCRC;
IRQStatus = loraDevice.readIrqStatus(); //read the LoRa device IRQ status register
RXpacketCount++;
printElapsedTime(); //print elapsed time to Serial Monitor
Serial.println (F(", Received Packet = "));
loraDevice.printASCIIPacket (RXBUFFER, RXPacketL); //print the packet as ASCII characters
Serial.println();
localCRC = loraDevice.CRCCCITT (RXBUFFER, RXPacketL, 0xFFFF); //calculate the CRC, this is the external CRC calculation of the RXBUFFER
Serial.print (F("CRC = 0x")); //contents, not the LoRa device internal CRC
Serial.println (localCRC, HEX);
Serial.print (F("RSSI = "));
Serial.print (PacketRSSI);
Serial.println (F(" dBm"));
Serial.print (F("SNR = "));
Serial.print (PacketSNR);
Serial.println (F(" dB"));
Serial.print (F("Length = "));
Serial.println (RXPacketL);
Serial.print (F("Packets = "));
Serial.println (RXpacketCount);
Serial.print (F("Errors = "));
Serial.println (errors);
Serial.print (F("IRQreg = 0x"));
Serial.print (IRQStatus, HEX);
Serial.println();
}
//===========================================================================================//
void printRxPacketError() {
uint16_t IRQStatus;
IRQStatus = loraDevice.readIrqStatus(); // Read the LoRa device IRQ status register
printElapsedTime(); // Print elapsed time to Serial Monitor
if (IRQStatus & IRQ_RX_TIMEOUT) { // Check for an RX timeout
Serial.println (F("printRxPacketError(): RXTimeout"));
}
else {
errors++;
Serial.println();
Serial.println (F("printRxPacketError(): Status:"));
Serial.print (F("RSSI = "));
Serial.print (PacketRSSI);
Serial.println (F(" dBm"));
Serial.print (F("SNR = "));
Serial.print (PacketSNR);
Serial.println (F(" dB"));
Serial.print (F("Length = "));
Serial.println (loraDevice.readRXPacketL()); // Get the real packet length
Serial.print (F("Packets = "));
Serial.println (RXpacketCount);
Serial.print (F("Errors = "));
Serial.println (errors);
Serial.print (F("IRQreg = 0x"));
Serial.println (IRQStatus, HEX);
Serial.print (F("printRxPacketError(): IRQ Status = "));
loraDevice.printIrqStatus(); // Print the names of the IRQ registers set
Serial.println();
}
delay (250); // Gives a longer buzzer and LED flash for error
}
//===========================================================================================//
void flashLED (uint16_t flashes, uint16_t delaymS) {
uint16_t index;
for (index = 1; index <= flashes; index++) {
digitalWrite (PIN_LED1, HIGH);
delay (delaymS);
digitalWrite (PIN_LED1, LOW);
delay (delaymS);
}
}
//===========================================================================================//
void printElapsedTime() {
float seconds;
seconds = millis() / 1000;
Serial.print (F("Time = "));
Serial.print (seconds, 0);
Serial.print (F("s"));
}
//===========================================================================================//
main.cppFollowing is the output from the Transmitter in the serial monitor. Notice how small the transmit time is. It is less than 1 millisecond. Compared to that, our LoRa transmitter took around 20 ms to transmit the same message.
SX1280 LoRa Test
initLoRaTx(): Starting FLRC transmitter..
initLoRaTx(): FLRC device is found.
SX1280,PACKET_TYPE_FLRC,2444999936hz,BandwidthBitRate_FLRC_BR_1_300_BW_1_2,CodingRate_CR_1_0,BT_1
SX1280,PACKET_TYPE_FLRC,Preamble_32_BITS,SyncWordLength_4,SyncWordMatch_1,VariableLengthPacket,PayloadLength_127,CRC_3_Bytes,Whitening_OFF,LNAgain_HighSensitivity
Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x900 80 0C 7B 02 20 FA C0 00 00 80 00 00 00 00 00 FF
0x910 FF FF 00 00 00 19 00 00 00 19 87 65 43 21 7F FF
0x920 FF FF FF 0C 70 37 0A 50 D0 80 00 C0 5F D2 8F 0A
0x930 00 C0 00 00 00 24 00 21 28 B0 30 09 01 59 70 08
0x940 58 0B 32 0A 14 24 6A 96 00 18 00 00 00 00 00 00
0x950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x960 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF
0x970 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 04
0x980 00 0B 18 70 00 3F 00 4C 00 F0 64 00 00 00 00 00
0x990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9A0 14 06 66 BC 13 C1 E6 66 04 00 00 00 00 00 00 00
0x9B0 14 06 66 BC 13 C1 E6 66 04 00 00 00 00 00 00 00
0x9C0 60 74 00 7F FC 01 FF FF FF FF 5E 4D 25 94 55 01
0x9D0 23 45 67 55 55 55 55 55 55 55 55 55 55 00 00 00
0x9E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
initLoRaTx(): FLRC transmitter is ready.
loraSender(): Sending message with power 0 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 0 mS
Packets Counter = 1
loraSender(): Sending message with power 0 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
CRC = 0xDAAB
Transmit Time = 1 mS
Packets Counter = 2
loraSender(): Sending message with power 0 dBm..
loraSender(): Packet =
Hello World 1234567890*
printTxPacketOK(): Status:
BytesSent = 23
Transmitter Serial MonitorBut unlike LoRa, FLRC will produce more errors if the spectrum (frequency bands) is being heavily used, such as when you have multiple Wi-Fi and Bluetooth devices nearby. When an error occurs, the FLRC Receiver will a message like the following.
loraReceiver(): RSSI = -99, SNR = -14 dB
Time = 60s
printRxPacketError(): Status:
RSSI = -99 dBm
SNR = -14 dB
Length = 32
Packets = 57
Errors = 1
IRQreg = 0x46
printRxPacketError(): IRQ Status = ,IRQ_RX_DONE,IRQ_SYNCWORD_VALID,IRQ_CRC_ERROR
Receiver Serial MonitorFollowing is the normal output from the Receiver device.
SX1280 LoRa Test
initLoRaRx(): Starting FLRC receiver..
initLoRaRx(): FLRC device is found.
SX1280,PACKET_TYPE_FLRC,2444999936hz,BandwidthBitRate_FLRC_BR_1_300_BW_1_2,CodingRate_CR_1_0,BT_1
SX1280,PACKET_TYPE_FLRC,Preamble_32_BITS,SyncWordLength_4,SyncWordMatch_1,VariableLengthPacket,PayloadLength_127,CRC_3_Bytes,Whitening_OFF,LNAgain_HighSensitivity
Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x900 80 0C 7B 02 20 FA C0 00 00 80 00 00 00 00 00 FF
0x910 FF FF 00 00 00 19 00 00 00 19 87 65 43 21 7F FF
0x920 FF FF FF 0C 70 37 0A 50 D0 80 00 C0 5F D2 8F 0A
0x930 00 C0 00 00 00 24 00 21 28 B0 30 09 01 59 70 08
0x940 58 0B 32 0A 14 24 6A 96 00 18 00 00 00 00 00 00
0x950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x960 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF
0x970 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 04
0x980 00 0B 18 70 00 3F 00 4C 00 F0 64 00 00 00 00 00
0x990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9A0 14 06 66 BC 13 C1 E6 66 04 00 00 00 00 00 00 00
0x9B0 14 06 66 BC 13 C1 E6 66 04 00 00 00 00 00 00 00
0x9C0 60 74 00 7F FC 01 FF FF FF FF 5E 4D 25 94 55 01
0x9D0 23 45 67 55 55 55 55 55 55 55 55 55 55 00 00 00
0x9E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
initLoRaRx(): Receiver is ready - RXBUFFER_SIZE 127
loraReceiver(): RSSI = -32, SNR = 16 dB
Time = 2s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -32 dBm
SNR = 16 dB
Length = 23
Packets = 1
Errors = 0
IRQreg = 0x6
loraReceiver(): RSSI = -32, SNR = 16 dB
Time = 3s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -32 dBm
SNR = 16 dB
Length = 23
Packets = 2
Errors = 0
IRQreg = 0x6
loraReceiver(): RSSI = -32, SNR = 16 dB
Time = 4s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -32 dBm
SNR = 16 dB
Length = 23
Packets = 3
Errors = 0
IRQreg = 0x6
loraReceiver(): RSSI = -32, SNR = 16 dB
Time = 5s, Received Packet =
Hello World 1234567890*
CRC = 0xDAAB
RSSI = -32 dBm
SNR = 16 dB
Length = 23
Packets = 4
Errors = 0
IRQreg = 0x6
Receiver Serial MonitorCode Explained
The FLRC code is similar to the LoRa Sender/Receiver but instead of loading LoRa-specific parameters, we load the FLRC-specific parameters with the following line.
// Setup FLRC
loraDevice.setupFLRC (Frequency, Offset, BandwidthBitRate, CodingRate, BT, Syncword);
C++As you can see, we are initializing the FLRC with a minimum number of parameters leaving everything else to the default. You can also individually, specify the parameters. One important thing with the FLRC is the presence of the Syncword
. FLRC required a sync word to properly decode the packet. Both the transmitter and the receiver should use the same sync word in order to communicate successfully. Sending the data is almost similar to the LoRa code. The rest of the code is self-explanatory.
LoRa Ranging
For the LoRa ranging example, we combined the examples 54_Ranging_Master and the 55_Ranging_Slave. The code should be self-explanatory at this point.
//===========================================================================================//
// Includes
#include <SPI.h>
#include <SX128XLT.h>
//===========================================================================================//
// Macros
#define PIN_LORA_CS 15
#define PIN_LORA_BUSY 5
#define PIN_LORA_RESET 2
#define PIN_LORA_DIO1 4
#define PIN_LORA_ANTSEL 9
#define PIN_LED1 16
#define LORA_DEVICE DEVICE_SX1280 // We need to define the device we are using
//===========================================================================================//
// Globals
//LoRa Modem Parameters
const uint32_t Frequency = 2445000000; // Frequency of transmissions in hz
const int32_t Offset = 0; // Offset frequency in hz for calibration purposes
const uint8_t Bandwidth = LORA_BW_0800; // LoRa bandwidth
const uint8_t SpreadingFactor = LORA_SF8; // LoRa spreading factor
const uint8_t CodeRate = LORA_CR_4_5; // LoRa coding rate
const uint16_t Calibration = 11350; // Manual Ranging calibrarion value
const int8_t RangingTXPower = 10; // Transmit power used
const uint32_t RangingAddress = 16; // Must match address in receiver
const uint16_t waittimemS = 10000; // Wait this long in mS for packet before assuming timeout
const uint16_t TXtimeoutmS = 5000; // Ranging TX timeout in mS
const uint16_t packet_delaymS = 0; // Forced extra delay in mS between ranging requests
const uint16_t rangingcount = 5; // Number of times ranging is carried out for each distance measurment
float distance_adjustment = 1.0000; // Adjustment factor to calculated distance
const int8_t TXpower = 10; // Transmit power used
const uint16_t rangingRXTimeoutmS = 0xFFFF; // Ranging RX timeout in mS
uint16_t ranging_errors, rangings_valid, ranging_results;
uint16_t IrqStatus;
uint32_t endwaitmS, startrangingmS, range_result_sum, range_result_average;
float distance, distance_sum, distance_average;
bool ranging_error;
int32_t range_result;
int16_t RangingRSSI;
uint32_t response_sent;
SX128XLT loraDevice; // Create a library class instance called loraDevice
//===========================================================================================//
// Forward Declarations
void initLoRa();
void initInitiator();
void initResponder();
void loraTask();
void responderFunction();
void initiatorFunction();
void flashLED (uint16_t flashes, uint16_t delaymS);
//===========================================================================================//
void setup() {
Serial.begin (115200);
while (!Serial);
Serial.println ("\nSX1280 LoRa Test");
ESP.wdtDisable();
*((volatile uint32_t*) 0x60000900) &= ~(1); // Hardware WDT OFF
// *((volatile uint32_t*) 0x60000900) |= 1; // Hardware WDT ON
SPI.begin();
initLoRa();
}
//===========================================================================================//
void loop() {
loraTask();
}
//===========================================================================================//
void initLoRa() {
#if OPT_IS_INITIATOR == 1
initInitiator();
#elif OPT_IS_INITIATOR == 0
initResponder();
#endif
}
//===========================================================================================//
void initInitiator() {
pinMode (PIN_LED1, OUTPUT);
// pinMode (PIN_LORA_ANTSEL, OUTPUT);
// digitalWrite (PIN_LORA_ANTSEL, LOW); // Setting Antenna Select to onboard one
flashLED (2, 125); // Two quick LED flashes to indicate program start
Serial.println (F("initInitiator(): Starting ranging initiator.."));
if (loraDevice.begin (PIN_LORA_CS, PIN_LORA_RESET, PIN_LORA_BUSY, PIN_LORA_DIO1, -1, -1, LORA_DEVICE)) {
Serial.println (F("initInitiator(): Device is found."));
flashLED (2, 125);
delay (1000);
}
else {
Serial.println (F("initInitiator(): No device is responding."));
while (1) {
flashLED (50, 50); // Long fast flash indicates device error
}
}
loraDevice.setupRanging (Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, RangingAddress, RANGING_MASTER);
// loraDevice.setRangingCalibration (Calibration); // Override automatic lookup of calibration value from library table
Serial.println();
loraDevice.printModemSettings(); // Reads and prints the configured LoRa settings, useful check
Serial.println();
loraDevice.printOperatingSettings(); // Reads and prints the configured operating settings, useful check
Serial.println();
Serial.println();
loraDevice.printRegisters (0x900, 0x9FF); // Print contents of device registers, normally 0x900 to 0x9FF
Serial.println();
Serial.println();
Serial.println (F("initInitiator(): Ranging configuration ="));
Serial.print (F("Address = "));
Serial.println (RangingAddress);
Serial.print (F("Calibration Value = "));
Serial.println (loraDevice.getSetCalibrationValue());
Serial.println (F("initInitiator(): Ranging initiator RAW ready."));
Serial.println();
delay (2000);
}
//===========================================================================================//
void initResponder() {
pinMode (PIN_LED1, OUTPUT); // Setup pin as output for indicator LED
flashLED (2, 125); // Two quick LED flashes to indicate program start
Serial.println (F("initResponder(): Starting ranging responder.."));
if (loraDevice.begin (PIN_LORA_CS, PIN_LORA_RESET, PIN_LORA_BUSY, PIN_LORA_DIO1, -1, -1, LORA_DEVICE)) {
Serial.println (F("initResponder(): Device is found."));
flashLED (2, 125);
delay (1000);
}
else {
Serial.println (F("initResponder(): No device is responding."));
while (1) {
flashLED (50, 50); // Long fast speed flash indicates device error
}
}
//The function call list below shows the complete setup for the LoRa device for ranging using the information
//defined in the Settings.h file.
//The 'Setup LoRa device for Ranging' list below can be replaced with a single function call, note that
//the calibration value will be loaded automatically from the table in the library;
//loraDevice.setupRanging(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, RangingAddress, RangingRole);
loraDevice.setupRanging (Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate, RangingAddress, RANGING_SLAVE);
//***************************************************************************************************
//Setup LoRa device for Ranging Slave
//***************************************************************************************************
/*
loraDevice.setMode (MODE_STDBY_RC);
loraDevice.setPacketType (PACKET_TYPE_RANGING);
loraDevice.setModulationParams (SpreadingFactor, Bandwidth, CodeRate);
loraDevice.setPacketParams (12, LORA_PACKET_VARIABLE_LENGTH, 0, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0);
loraDevice.setRfFrequency (Frequency, Offset);
loraDevice.setTxParams (TXpower, RADIO_RAMP_02_US);
loraDevice.setRangingMasterAddress (RangingAddress);
loraDevice.setRangingSlaveAddress (RangingAddress);
loraDevice.setRangingCalibration (loraDevice.lookupCalibrationValue(SpreadingFactor, Bandwidth));
loraDevice.setRangingRole (RANGING_SLAVE);
loraDevice.writeRegister (REG_RANGING_FILTER_WINDOW_SIZE, 8); //set up window size for ranging averaging
loraDevice.setHighSensitivity();
*/
//***************************************************************************************************
loraDevice.setRangingCalibration (11300); // Override automatic lookup of calibration value from library table
Serial.println();
Serial.println (F("initResponder(): Ranging configuration ="));
Serial.print (F("Address = "));
Serial.println (RangingAddress);
Serial.print (F("Calibration Value = "));
Serial.println (loraDevice.getSetCalibrationValue());
Serial.println (F("initResponder(): Ranging responder is ready."));
Serial.println();
delay (2000);
}
//===========================================================================================//
void loraTask() {
#if OPT_IS_INITIATOR == 1
initiatorFunction();
#elif OPT_IS_INITIATOR == 0
responderFunction();
#endif
}
//===========================================================================================//
void responderFunction() {
loraDevice.receiveRanging (RangingAddress, 0, TXpower, NO_WAIT);
endwaitmS = millis() + rangingRXTimeoutmS;
while (!digitalRead (PIN_LORA_DIO1) && (millis() <= endwaitmS)); // Wait for Ranging valid or timeout
if (millis() >= endwaitmS) {
Serial.println (F("responderFunction(): Ranging receive timeout error."));
flashLED (2, 100); // Single flash to indicate timeout
}
else {
IrqStatus = loraDevice.readIrqStatus();
digitalWrite (PIN_LED1, HIGH);
if (IrqStatus & IRQ_RANGING_SLAVE_RESPONSE_DONE) {
response_sent++;
Serial.print (F("responderFunction(): Response sent = "));
Serial.println (response_sent);
}
else {
Serial.println (F("responderFunction(): Responder error."));
Serial.print ("IRQ = 0x");
Serial.println (IrqStatus, HEX);
loraDevice.printIrqStatus();
}
digitalWrite (PIN_LED1, LOW);
Serial.println();
}
}
//===========================================================================================//
void initiatorFunction() {
uint8_t index;
distance_sum = 0;
range_result_sum = 0;
ranging_results = 0; // Count of valid results in each loop
for (index = 1; index <= rangingcount; index++) {
startrangingmS = millis();
Serial.println (F("initiatorFunction(): Initiating ranging.."));
loraDevice.transmitRanging (RangingAddress, TXtimeoutmS, RangingTXPower, WAIT_TX);
IrqStatus = loraDevice.readIrqStatus();
if (IrqStatus & IRQ_RANGING_MASTER_RESULT_VALID) {
ranging_results++;
rangings_valid++;
digitalWrite (PIN_LED1, HIGH);
Serial.println (F("initiatorFunction(): Ranging result:"));
Serial.print (F("Status = Valid, "));
range_result = loraDevice.getRangingResultRegValue (RANGING_RESULT_RAW);
Serial.print (F("Register = "));
Serial.print (range_result);
if (range_result > 800000) {
range_result = 0;
}
range_result_sum = range_result_sum + range_result;
distance = loraDevice.getRangingDistance (RANGING_RESULT_RAW, range_result, distance_adjustment);
distance_sum = distance_sum + distance;
Serial.print (F(", Distance = "));
Serial.print (distance, 1);
Serial.print (F(", RSSIReg = "));
Serial.print (loraDevice.readRegister (REG_RANGING_RSSI));
RangingRSSI = loraDevice.getRangingRSSI();
Serial.print (F(", RSSI = "));
Serial.print (RangingRSSI);
Serial.print (F(" dBm"));
Serial.println();
digitalWrite (PIN_LED1, LOW);
}
else {
ranging_errors++;
distance = 0;
range_result = 0;
Serial.println (F("initiatorFunction(): Ranging error."));
Serial.print (F("IRQ = 0x"));
Serial.println (IrqStatus, HEX);
Serial.println();
}
delay (packet_delaymS);
if (index == rangingcount) {
range_result_average = (range_result_sum / ranging_results);
if (ranging_results == 0) {
distance_average = 0;
}
else {
distance_average = (distance_sum / ranging_results);
}
Serial.println (F("initiatorFunction(): Ranging average results:"));
Serial.print (F("Total Valid = "));
Serial.print (rangings_valid);
Serial.print (F(", Total Errors = "));
Serial.print (ranging_errors);
Serial.print (F(", Average RAW Result = "));
Serial.print (range_result_average);
Serial.print (F(", Average Distance = "));
Serial.print (distance_average, 1);
Serial.print (F(" m"));
Serial.println();
delay (2000);
}
Serial.println();
}
}
//===========================================================================================//
void flashLED (uint16_t flashes, uint16_t delaymS) {
uint16_t index;
for (index = 1; index <= flashes; index++) {
digitalWrite (PIN_LED1, HIGH);
delay (delaymS);
digitalWrite (PIN_LED1, LOW);
delay (delaymS);
}
}
//===========================================================================================//
main.cppSince we renamed the transmitter and receiver functions to Initiator and Responder, you have to use a new PIO configuration file.
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env]
platform = espressif8266
board = esp8285
framework = arduino
lib_deps =
https://github.com/StuartsProjects/SX12XX-LoRa.git
extra_scripts = extra_upload.py
[env:Initiator]
upload_protocol = esptool
upload_speed = 2000000
upload_port = COM26
build_flags = -D OPT_IS_INITIATOR=1
[env:Responder]
upload_protocol = esptool
upload_speed = 2000000
upload_port = COM13
build_flags = -D OPT_IS_INITIATOR=0
platformio.iniThe extra Python script also changes.
import os
from SCons.Script import DefaultEnvironment
env = DefaultEnvironment()
def after_build (source, target, env):
print ("Uploading to Initiator...")
env.Execute ("pio run -t upload -e Initiator")
print ("Uploading to Responder...")
env.Execute ("pio run -t upload -e Responder")
env.AddPostAction ("buildprog", after_build)
extra_upload.pyAfter uploading the code, the Initiator will start sending ranging requests. If the Responder is also active, it will start to respond to the requests as you can see from the serial monitor outputs below. The Initiator will send 5 requests before calculating the average value of the distance. If the values are not accurate, you will need to modify the calibration value. Stuart has a detailed explanation of the ranging calibration here.
SX1280 LoRa Test
initInitiator(): Starting ranging initiator..
initInitiator(): Device is found.
SX1280,PACKET_TYPE_RANGING,2444999936hz,SF8,BW812500,CR4:5
SX1280,PACKET_TYPE_RANGING,Preamble_12,Explicit,PayloadL_0,CRC_ON,IQ_NORMAL,LNAgain_HighSensitivity
Reg 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x900 80 00 89 41 20 FA BC 13 C1 80 00 00 00 00 00 61
0x910 9C 70 00 00 00 10 00 00 00 10 87 65 43 21 7F FF
0x920 FF FF FF 0C 70 37 0E 50 C0 80 00 C0 2C 56 8F 0A
0x930 00 C0 00 00 00 24 00 21 68 B0 30 0D 01 52 C6 0C
0x940 58 0B 32 0A 10 24 6B 96 00 18 00 00 00 00 00 00
0x950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x960 00 00 00 00 00 00 00 00 00 00 FF FF FF FF FF FF
0x970 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 04
0x980 00 0B 18 70 00 00 00 4C 00 F0 64 00 00 00 00 00
0x990 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9A0 00 08 EC B8 9D 8A E6 66 04 00 00 00 00 00 00 00
0x9B0 00 08 EC B8 9D 8A E6 66 04 00 00 00 00 00 00 00
0x9C0 00 16 00 3F E8 01 FF FF FF FF 5E 4D 25 10 55 55
0x9D0 55 55 55 55 55 55 55 55 55 55 55 55 55 00 00 00
0x9E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x9F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
initInitiator(): Ranging configuration =
Address = 16
Calibration Value = 11350
initInitiator(): Ranging initiator RAW ready.
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 78, Distance = 3.5, RSSIReg = 103, RSSI = -47 dBm
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 85, Distance = 3.8, RSSIReg = 104, RSSI = -46 dBm
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 74, Distance = 3.3, RSSIReg = 103, RSSI = -47 dBm
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 83, Distance = 3.7, RSSIReg = 104, RSSI = -46 dBm
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 84, Distance = 3.8, RSSIReg = 104, RSSI = -46 dBm
initiatorFunction(): Ranging average results:
Total Valid = 5, Total Errors = 0, Average RAW Result = 80, Average Distance = 3.6 m
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 83, Distance = 3.7, RSSIReg = 104, RSSI = -46 dBm
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 97, Distance = 4.4, RSSIReg = 103, RSSI = -47 dBm
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 69, Distance = 3.1, RSSIReg = 104, RSSI = -46 dBm
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 62, Distance = 2.8, RSSIReg = 104, RSSI = -46 dBm
initiatorFunction(): Initiating ranging..
initiatorFunction(): Ranging result:
Status = Valid, Register = 95, Distance = 4.3, RSSIReg = 104, RSSI = -46 dBm
initiatorFunction(): Ranging average results:
Total Valid = 10, Total Errors = 0, Average RAW Result = 81, Average Distance = 3.7 m
Initiator Serial MonitorSX1280 LoRa Test
initResponder(): Starting ranging responder..
initResponder(): Device is found.
initResponder(): Ranging configuration =
Address = 16
Calibration Value = 11300
initResponder(): Ranging responder is ready.
responderFunction(): Response sent = 1
responderFunction(): Response sent = 2
responderFunction(): Response sent = 3
responderFunction(): Response sent = 4
responderFunction(): Response sent = 5
responderFunction(): Response sent = 6
responderFunction(): Response sent = 7
responderFunction(): Response sent = 8
responderFunction(): Response sent = 9
responderFunction(): Response sent = 10
responderFunction(): Response sent = 11
responderFunction(): Response sent = 12
responderFunction(): Response sent = 13
responderFunction(): Response sent = 14
responderFunction(): Response sent = 15
responderFunction(): Response sent = 16
responderFunction(): Response sent = 17
responderFunction(): Response sent = 18
responderFunction(): Response sent = 19
responderFunction(): Response sent = 20
Responder Serial MonitorWith this post, we wanted to introduce you to the new features and capabilities of the SX1280 LoRa chip with the help of the Zerodrag Nexus1 receiver. We hope this tutorial has been resourceful to you. If you run into any issues, or have a question or feedback, please post them in the comments. Happy coding 😉
Links
- Zerodrag Nexus1 – Official Product Page
- Zerodrag Nexus1 ExpressLRS Receiver – Buy from DRK Store
- ExpressLRS – Official Website
- ExpressLRS – GitHub
Short Link
- A short URL to this page – https://www.circuitstate.com/zerdnexus1