Sending Sensor Data over LoRa
Introduction
We are on mission here at SparkFun to demonstrate the simplest ways to send sensor data using different wireless solutions. So far together we have explored sending sensor data over wifi, but what about longer range projects? Sure, we could create a mesh network of lots of boards talking to one another and it would be super rad, but for long range, why not use LoRa! In this tutorial we’re going to check out what it takes to send sensor data and output that data to a screen by using two SparkFun LoRa Thing Plus - expLoRaBLE development boards, and a couple of really nifty antennas; let us away!
Hardware not to scale
Required Materials
Below you can find all of the parts you’ll need to follow along with this tutorial; you can add what you need or if you don’t have any of these parts already, you can click to the wishlist provided and purchase it all in one go! I included two USB-C cables with this list, as I found it really helpful to have them to monitor both the transmitter and receiver, but it would work fine with just one (with the receiver powered by the battery and monitored from the OLED screen). Additionally, the LoRa Fiberglass Antenna does come with an interface N to RP-SMA cable included, but I went ahead and added the part to the wishlist for reference.
Hardware Hookup
This project was put together to be as simple as possible and requires two separate hardware hookups; one being the transmitter and the other the receiver. There is no soldering needed since we are utilizing our Qwiic Connect System for the sensor and the OLED screen to output the data. In the photos below, we are using a USB-C connector for power but depending on your needs you can also connect a battery to the on-board JST-PH connector.
Hooking up the LoRa Transmitter
First thing to do here is to simply connect one end of your Qwiic cable to your transmitter expLoRaBLE board, and the other to the atmospheric sensor. For the antenna you’ll first need to attach the U.FL to SMA interface cables to your expLoRaBLE’s U.FL connection. This connection can be a bit tricky to hook up so if this is your first time using U.FL you may want to reference Three Quick Tips About Using U.FL. I highly recommend that you run this cable through the hole on the board (as shown below) if you aren’t using it for mounting. This will add a lot of stability to your connection to the antenna and greatly reduce the risk of damaging the U.FL connectors.
Very simple hardware hookup for the data transmitter.
Once you feel confident that your interface cable isn’t going anywhere, you’ll be able to screw on the second interface cable, which will then attach to the end of the fiberglass antenna.
Hooking up the LoRa Receiver
In a very similar fashion to the transmitter, you'll start by connecting one end of your Qwiic cable to your receiver expLoRaBLE board and the other end to the Micro OLED Breakout. The only difference of hooking up this antenna is that it attaches directly to the U.FL interface cable (as shown below).
That's it! About as easy as it gets.
Software Setup and Programming
To run this project, you’ll need to install the following libraries:
- RadioLib Arduino library
- SparkFun Micro OLED library
- SparkFun BME280 Library
Aside from the above libraries you'll need to install the SparkFun Apollo3 Boards board definitions: If you need help, we've put together a quick video to show how this is done.
Note: If you don't see the SparkFun Apollo3 Boards when searching in the boards manager, below is the .json
file link for the SparkFun Ambiq Apollo3 Arduino Core referenced in the video above.
https://raw.githubusercontent.com/sparkfun/Arduino_Apollo3/master/package_sparkfun_apollo3_index.json
Transmit/Receive Code
Once the libraries are installed, go ahead and open up two separate blank sketches in Arduino. One sketch will house the transmitter program, while the other will be for our receiver:
// Peer to Peer: Monitor Sensor Data using LoRa - Transmitter
// SparkFun Electronics, Mariah Kelly, November 2022
// Original transmit file can be found here: https://cdn.sparkfun.com/assets/learn_tutorials/1/4/9/4/Transmit-v3.ino
// Include necessary libraries
#include <RadioLib.h> // Transmit & receive - https://github.com/jgromes/RadioLib/archive/refs/heads/master.zip
#include "SparkFunBME280.h" // Qwiic Atmospheric Sensor library - https://github.com/sparkfun/SparkFun_BME280_Arduino_Library/archive/master.zip
// SX1262 has the following connections:
// NSS pin: D36
// DIO1 pin: D40
// NRST pin: D44
// BUSY pin: D39
SX1262 radio = new Module(D36, D40, D44, D39, SPI1);
BME280 mySensor;
void setup() {
Serial.begin(115200); // Remember to set this value in your serial monitor as well!
Serial.println("Reading basic values from BME280");
Wire.begin(); // Begin I2C communication
if (mySensor.beginI2C() == false) {
Serial.println("The sensor did not respond. Please check wiring.");
while (1); //Freeze
}
// Initialize SX1262 with default settings
Serial.print(F("[SX1262] Initializing ... "));
int state = radio.begin(915.0, 250.0, 7, 5, 0x34, 20, 10, 0, false);;
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
}
void loop() {
Serial.println(F("[SX1262] Transmitting packet ... "));
// Retrieve environmental data and declare as integers
int temp = (int)mySensor.readTempF();
int humidity = (int)mySensor.readFloatHumidity();
int alt = (int)mySensor.readFloatAltitudeFeet();
// Print to serial monitor (use this to double check that your sent data is correct and that your sensor is taking readings)
Serial.print(temp);
Serial.print(" Degrees ");
Serial.print(humidity);
Serial.print("% ");
Serial.print(alt);
Serial.println(" Feet");
// Create a String to send (data must be sent as a string, so we'll tell our computer friend that we want our integers to be set as strings)
String myData = String(temp) + " Degrees" + String(humidity) + "% Humid " + String(alt) + " Feet"; // I added some units for our data here as well
// Transmit data and units
int state = radio.transmit(myData);
if (state == RADIOLIB_ERR_NONE) {
// The packet was successfully transmitted
Serial.println(F("Success!"));
// Print measured data rate
Serial.print(F("[SX1262] Datarate:\t"));
Serial.print(radio.getDataRate());
Serial.println(F(" bps"));
} else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) {
// The supplied packet was longer than 256 bytes
Serial.println(F("Packet size too long!"));
} else if (state == RADIOLIB_ERR_TX_TIMEOUT) {
// Timeout occurred while transmitting packet
Serial.println(F("Timeout!"));
} else {
// Some other error occurred
Serial.print(F("Error"));
Serial.println(state);
}
// Wait for a second before transmitting again
delay(1000);
}
Once your transmitter is up and running, and you’re getting readings from your atmospheric sensor, we can move on to the receiver!
// Peer to Peer: Monitor Sensor Data using LoRa - Receiver
// SparkFun Electronics, Mariah Kelly, November 2022
// Original receive file can be found here: https://cdn.sparkfun.com/assets/learn_tutorials/1/4/9/4/Receive-v3.ino
// Include necessary libraries
#include <RadioLib.h>
#include <Wire.h>
#include <SFE_MicroOLED.h> // Include the SFE_MicroOLED library
#define PIN_RESET 9
#define DC_JUMPER 1
MicroOLED oled(PIN_RESET, DC_JUMPER); // I2C declaration
// SX1262 has the following connections:
// NSS pin: 10
// DIO1 pin: 2
// NRST pin: 3
// BUSY pin: 9
//SX1262 radio = new Module(10, 2, 3, 9);
SX1262 radio = new Module(D36, D40, D44, D39, SPI1);
void setup() {
Serial.begin(9600); // Remember to set this value in your serial monitor as well!
Wire.begin(); // Begin I2C communication
oled.begin(); // Initialize the OLED
oled.clear(ALL); // Clear the display's internal memory
oled.display(); // Display what's in the buffer (splashscreen)
delay(1000); // Delay 1000 ms
oled.setFontType(0);
oled.clear(PAGE); // Clear the buffer.
randomSeed(analogRead(A0) + analogRead(A1));
// initialize SX1262 with default settings
Serial.print(F("[SX1262] Initializing ... "));
int state = radio.begin(915.0, 250.0, 7, 5, 0x34, 20, 10, 0, false);
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
}
void loop() {
Serial.print(F("[SX1262] Waiting for incoming transmission ... "));
String str; // Declare a data type to receive (this must be a string)
int state = radio.receive(str); // Receive data from transmitter
if (state == RADIOLIB_ERR_NONE) {
// The packet was successfully received
Serial.println(F("Success!"));
// Print that the receiver was successful to the OLED screen (the back and forth of the "Success" text and data will act as a signifier that data is still being received!)
oled.clear(PAGE);
oled.setCursor(0, 0);
oled.print("Success!");
oled.display();
delay(1000);
// Print the data of the packet to the serial monitor
Serial.print(F("[SX1262] Data:\t\t"));
Serial.println(str);
// Print the data of the packet to the OLED screen
oled.clear(PAGE);
oled.setCursor(0, 0);
oled.print(str);
oled.display();
delay(2000);
oled.clear(PAGE);
// Print the RSSI (Received Signal Strength Indicator) of the last received packet
Serial.print(F("[SX1262] RSSI:\t\t"));
Serial.print(radio.getRSSI());
Serial.println(F(" dBm"));
// Print the SNR (Signal-to-Noise Ratio) of the last received packet
Serial.print(F("[SX1262] SNR:\t\t"));
Serial.print(radio.getSNR());
Serial.println(F(" dB"));
} else if (state == RADIOLIB_ERR_RX_TIMEOUT) {
// Timeout occurred while waiting for a packet
Serial.println(F("Timeout!")); // We won't print this to the screen since the Success/data print will freeze on its own if a timeout occurs
} else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
// The packet was received, but is malformed
Serial.println(F("CRC error!"));
// Print CRC Error message to OLED screen
oled.clear(PAGE);
oled.setCursor(0, 0);
oled.print("CRC Error!");
oled.display();
delay(1000);
oled.clear(PAGE);
} else {
// Some other error occurred
Serial.print(F("Error"));
Serial.println(state);
// Print Error message to OLED screen
oled.clear(PAGE);
oled.setCursor(0, 0);
oled.print("Error! ");
oled.print(state);
oled.display();
delay(1000);
oled.clear(PAGE);
}
}
Now that our transmitter and receiver are chatting with each other, we can go out and test how far the signal will reach! Feel free to play around with your settings a bit, just remember to change them in both the transmitter and receiver settings AND to check your regional LoRa Frequency Band parameters (for the U.S. this range is 902 - 928MHz).
We managed to get 0.3 miles away before we had to turn around because of the highway, but were still getting a decent signal from there! Let us know how far you can go!
Troubleshooting
Getting a CRC Error?
As you explore the range of your device, you may get a “CRC Error” on your screen, meaning the packet was corrupted in some way and was not received and/or sent properly. It doesn’t necessarily mean that you are out of range of your transmitter and you may still be able to receive data after seeing this error. Give it a second or two to update again to know for sure if you are out of range!
Resources and Going Further
There were lots of links included in this tutorial, so let’s make a quick list all in one place to keep track of them!
Necessary Libraries:
- RadioLib Library
- SparkFun Micro OLED Library
- SparkFun BME280 Library
Board Definitions:
- SparkFun Apollo3 Boards (Installing Board Definitions in the Arduino IDE)
Helpful Information:
Interesting in logging data to an IoT platform?
We've tried to make it as easy as possible by combining the automatic data-logging of the SparkFun OpenLog Artemis with the simplicity of Machinechat. Check out the resources below for more information.
Getting Started with Machinechat
October 7, 2022