Moving on with this series of basic tutorials for people who are new to any of these three worlds -or all of them-: RAKwireless WisBlock, LoRa®, and IoT. This tutorial has a brief introduction to these terms, so take something to drink and pay attention to what's to come!
WisBlock, LoRa®, and IoT have one thing in common: making sure everything is connected. Communication is a fundamental part of any technology today, you've already experienced it with Bluetooth and WiFi.
But what about LoRa®? Why is it so important? In this tutorial, we will explain why, and obviously, we want you to experiment with making a Peer-to-Peer communication using WisBlock Starter Kit.
Why LoRa® instead of WiFi o Bluetooth?If WiFi and Bluetooth already exist, it might seem that LoRa® is superfluous, right? The truth is that LoRa® is a complementary technology to both. WiFi and Bluetooth are special for home applications, such as Google Home or Alexa, but these technologies are not enough, here are some reasons why:
- Range: Perhaps the biggest weakness of WiFi and Bluetooth is their transmission range. You can only enjoy a few meters, which you can check when you leave your house and the WiFi signal of your phone dies after walking a few steps. Imagine if you want to develop an application on farms of several hectares. LoRa® has a great range or at least that's what it says, so let's put it to the test.
- Consumption: This feature applies in particular to WiFi. Sending data over WiFi or LTE consume much more power than an LPWAN network. WisBlock has long battery life devices, but we don't want you just take our word for it, we will put it to the test in later tutorials.
- Infrastructure and Privacy: To transmit data via WiFi, you will always have to hire a service provider. Using LoRa®, a public infrastructure of the people for the people is being developed, also based on Blockchain. Here you can learn about the Helium project.
As you can see, they are all benefits, but then, why is it only a complementary technology? The answer is the transmission capacity. and Bluetooth can transmit large amounts of data, audio, video, streaming, among others, while LoRa® transmits a small amount of data. Of course, you don't need a lot of information to know if your crops are lacking water, just a sensor that sends a few data a day would be enough.
Broadcasting vs Point-to-Point communicationsIf you've been developing hardware for some time, you've already raised your hands to your face saying “I already know that!”. But, hey, we want to do a basic tutorial for dummies, so here we go!
Let's keep it short. Broadcasting is used when a signal is sent to all devices that are within range. For example, information from your router is broadcast to all devices connected to it, but it's only accessible through the devices that request it. Peer-to-peer communication is exclusive between two devices only. Do you want an analogy? Broadcasting: a conference; peer-to-peer: a romantic dinner. So enough talking and let's go to our romantic dinner… We mean, let’s go to program our Peer-to-peer communication with WisBlock Starter Kit.
What do you need?Or you can buy them separately:
These items are also available in:
Step by StepAt this point we are going to assume that you already know about the WisBlock modular system, that you have installed a Hardware programming Software such as Arduino or PlatformIO, and if not, don't worry, here is a tutorial where you can learn all of these topics.
Hardware Configuration
Programming
- Receiver
- Code Review and Explanation
- Transmitter
- Code Review and Explanation
Measure your communication Range
- Traveler
- Home
- Range Measurement
PlatformIO implementation (Optional)
What’s Next…?
Hardware ConfigurationTo complete this tutorial you will need two units of each of the following elements: WisBlock Core RAK4631, WisBlock Base RAK5005-O, LoRa Antenna (generally included with the WisBlock Core). You can see the elements in Figure 1.
The next step, assemble these elements as seen in Figure 2.
Don't forget to install antennas.
Finally, you will have two WisBlock fully functional and ready to work:
Programming is easy, you are simply going to download the libraries that RAKwireless has already developed for you. Furthermore, we are going to give you a brief introduction to LoRa® and the SX126x transceiver. Let's start!
Receiver
If you haven't configured your Arduino IDE with the WisBlock tools, we leave you a tutorial here. In the home interface go to File > Examples > RAK WisBlock examples > RAK4631 > Communications > LoRa > LoRaP2P > LoRaP2P_RX.
A new window with the code will appear. Notice that in the first lines of code the SX126x-RAK4631 library appears. This element makes it possible to use LoRa® on WisBlock, so click on the link that appears after it to install them.
Make sure to install the two libraries that appear in the dialog.
Now, you are ready to compile your first program, obtaining something similar to Figure 8.
Code Review and Explanation
We are going to break the code into parts to make it more readable and understandable. Let's go with the first part:
// Function declarations
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr);
void OnRxTimeout(void);
void OnRxError(void);
#ifdef NRF52_SERIES
#define LED_BUILTIN 35
#endif
// Define LoRa parameters
#define RF_FREQUENCY 868300000 // Hz
#define TX_OUTPUT_POWER 22 // dBm
#define LORA_BANDWIDTH 0 // [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 8 // 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 RX_TIMEOUT_VALUE 3000
#define TX_TIMEOUT_VALUE 3000
static RadioEvents_t RadioEvents;
static uint8_t RcvBuffer[64];
The first four lines are declarations of the three functions to establish a data reception. then, we declare the pin for a LED. The next lines are the most important as they are the LoRa® parameters, given specifically for the Semtech SX1262 transceiver. You can review the datasheet of this for more details.
Some recommendations about these parameters are: Look at the operating frequency, to communicate with a LoRa® network you must make sure that it has the value given for your region, commonly 433MHz, 868MHz, or 915MHz. In this case, you must make sure that this frequency is the same as the LoRa antenna which comes with your WisBlock Kit, these antennas have a label as can be seen in Figure 4.
The rest of the data is given to have an optimal performance in Point-to-Point communications, you can leave them as they are or you can explore the results by changing these parameters. Just make sure that they are the same in the RX algorithm as in the TX algorithm.
Finally, the last two lines initialize a data structure inherited from the library you installed earlier, and the buffer where the data will be received, usually 64 bits.
void setup()
{
// Initialize LoRa chip.
lora_rak4630_init();
// Initialize Serial for debug output
time_t timeout = millis();
Serial.begin(115200);
while (!Serial)
{
if ((millis() - timeout) < 5000)
{
delay(100);
}
else
{
break;
}
}
Serial.println("=====================================");
Serial.println("LoRaP2P Rx Test");
Serial.println("=====================================");
// Initialize the Radio callbacks
RadioEvents.TxDone = NULL;
RadioEvents.RxDone = OnRxDone;
RadioEvents.TxTimeout = NULL;
RadioEvents.RxTimeout = OnRxTimeout;
RadioEvents.RxError = OnRxError;
RadioEvents.CadDone = NULL;
// Initialize the Radio
Radio.Init(&RadioEvents);
// Set Radio channel
Radio.SetChannel(RF_FREQUENCY);
// Set Radio RX configuration
Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
// Start LoRa
Serial.println("Starting Radio.Rx");
Radio.Rx(RX_TIMEOUT_VALUE);
}
In this part of the code, if you already know Arduino IDE, you know that all the variables and elements that we are going to use are initialized. Then, the LoRa® chip is initialized. In the next step, the serial port is initialized, in case you didn’t notice, unlike other cards the serial port is always initialized at 115200 baud. A brief delay of 5 seconds is left to wait for the TX initialization. Finally, some messages per serial port for debugging.
The lines of the event functions that will direct the reception are assigned, if this concept is not clear, don't worry, we will see it in the lines below.
Communication is initialized, starting the device and assigning it a frequency by which it will be connected. Set the LoRa® communication parameters that we declared previously. If you want to learn more in-depth why these values are set, we recommend you go to the datasheet.
Finally, a serial port communication message and the start and TimeOut signal for the receiver.
Note:
Although we recommend going to the datasheet to consult more information, we want to make the use of the TimeOut clear: This is a value that starts in three seconds, every time data is sent or received, both the sender and the receiver start a counter when this counter reaches the given value (3 seconds for this particular case) it closes the channel so that it remains available for the reception or transmission of new data.
void loop()
{
// Put your application tasks here, like reading of sensors,
// Controlling actuators and/or other functions.
}
Again, if you're used to programming on Arduino IDE, this probably seems pointless, there's no program running, right? The truth is when you are programming WisBlock for LoRa® communications, it works only by events, that is, the program does not repeat a loop but only executes when the radio has to send or receive data.
This is great because it not only allows the least power consumption in the use of communications but also allows the microcontroller to execute tasks in parallel. Here are the three events you need to receive data using WisBlock:
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
Serial.println("OnRxDone");
delay(10);
memcpy(RcvBuffer, payload, size);
Serial.printf("RssiValue=%d dBm, SnrValue=%d\n", rssi, snr);
for (int idx = 0; idx < size; idx++)
{
Serial.printf("%02X ", RcvBuffer[idx]);
}
Serial.println("");
Radio.Rx(RX_TIMEOUT_VALUE);
}
/**@brief Function to be executed on Radio Rx Timeout event
*/
void OnRxTimeout(void)
{
Serial.println("OnRxTimeout");
Radio.Rx(RX_TIMEOUT_VALUE);
}
/**@brief Function to be executed on Radio Rx Error event
*/
void OnRxError(void)
{
Serial.println("OnRxError");
Radio.Rx(RX_TIMEOUT_VALUE);
}
The first function of the code is the most important. It is in charge of reading the received buffer and printing it via the serial port. The first input is a pointer to the buffer address, the buffer size, and the RSSI and SNR values.
Note
The RSSI (Received Signal Strength Indicator) and SNR (Signal to Noise Ratio) values are measures that in telecommunications indicate how good the received signal is, commonly expressed in dBm or simply without a unit. A more negative RSSI indicates signal loss, a higher SNR indicates better signal quality.
The next lines report a new package is ready, a delay, and the information is stored in the created variables. Finally, the SNR and RSSI data and buffer information are printed. A line break is performed in the serial monitor and the TimeOut is reset.
The second function is set to start a timeout that will determine when a new packet arrives completely. And finally, gives notice of a packet that arrived badly. Either due to interference or low signal power, in this case, the timeout is reset and a new packet is expected to be sent.
Transmitter
The TX code is also available in the RAKwireless examples, for this go to: File > Examples > RAK WisBlock examples > RAK4631 > Communications > LoRa > LoRaP2P > LoRaP2P_TX.
A new window will open with the code. You can see this in Figure 10. Note that we again have the option to download the SX126x-RAK4630 libraries. This step is no longer necessary since they were already installed previously.
Now, you are ready to compile your first program, obtaining something similar to Figure 11.
Code
The TX code is quite similar to the RX code, so we will only clarify the differences:
// Function declarations
void OnTxDone(void);
void OnTxTimeout(void);
#ifdef NRF52_SERIES
#define LED_BUILTIN 35
#endif
// Define LoRa parameters
#define RF_FREQUENCY 868300000 // Hz
#define TX_OUTPUT_POWER 22 // dBm
#define LORA_BANDWIDTH 0 // [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 8 // 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 RX_TIMEOUT_VALUE 3000
#define TX_TIMEOUT_VALUE 3000
static RadioEvents_t RadioEvents;
static uint8_t TxdBuffer[64];
In TX only two functions are declared to send the data, we will see the implementation later. The rest of the code is the same, except for the naming conventions from RX to TX.
void setup()
{
// Initialize LoRa chip.
lora_rak4630_init();
// Initialize Serial for debug output
time_t timeout = millis();
Serial.begin(115200);
while (!Serial)
{
if ((millis() - timeout) < 5000)
{
delay(100);
}
else
{
break;
}
}
Serial.println("=====================================");
Serial.println("LoRap2p Tx Test");
Serial.println("=====================================");
// Initialize the Radio callbacks
RadioEvents.TxDone = OnTxDone;
RadioEvents.RxDone = NULL;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxTimeout = NULL;
RadioEvents.RxError = NULL;
RadioEvents.CadDone = NULL;
// Initialize the Radio
Radio.Init(&RadioEvents);
// Set Radio channel
Radio.SetChannel(RF_FREQUENCY);
// Set Radio TX configuration
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, TX_TIMEOUT_VALUE);
send();
}
Be careful with some differences with the RX code: In the events, only the two TX functions are declared, the rest are left with a NULL value. The function on line 40 has different input attributes, such as TX_OUTPUT_POWER which determines the transmit power, the rest are the RX parameters.
Finally, the send() function is used. This is a system function and we will see its implementation later.
void OnTxDone(void)
{
Serial.println("OnTxDone");
delay(5000);
send();
}
/**@brief Function to be executed on Radio Tx Timeout event
*/
void OnTxTimeout(void)
{
Serial.println("OnTxTimeout");
}
void send()
{
TxdBuffer[0] = 'H';
TxdBuffer[1] = 'e';
TxdBuffer[2] = 'l';
TxdBuffer[3] = 'l';
TxdBuffer[4] = 'o';
Radio.Send(TxdBuffer, 5);
}
The OnTxDone and OnTxTimeOut functions are simple, and some of the processes they perform have already been explained in the Receiver section. In the send() function, a buffer is declared and then sent. The Radio.Send() function receives two attributes, the indicated buffer and its size.
With this, you can now upload your TX program to one WisBlock Starter Kit and the RX program to the other, as seen in Figures 12 and 13.
Finally, you can open the Serial Monitor to see the RX and TX output.
In this section of the tutorial, we are going to modify the code, so that each WisBlock module can behave as a transmitter and receiver. What is the challenge? Leave one module in a fixed place (Home) and the other (Traveler) takes it out for a walk, so you can measure the maximum distance you can reach. Spoiler alert: You can achieve greater coverage than WiFi and Bluetooth with just the antennas that your WisBlock Starter Kit comes with, so let's get started.
Download the complete codes available on our GitHub repository, in this tutorial we will only see some lines that change.
Traveler
The previous LoRaP2P_TX code is going to be modified to be our Traveler, with which data is sent to the other module to measure the distance.
// Function declarations
void OnTxDone(void);
void OnTxTimeout(void);
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr);
void OnRxTimeout(void);
void OnRxError(void);
void OnCadDone(bool cadResult);
In the declaration of the functions, note that in the LoRaP2P_TX file there were only the functions of lines 2 and 3, now we add 4 more. The three are defined in the LoRaP2P_RX algorithm and a function called OnCadDone, later we will see what it consists of.
LoRa® parameter definitions are left the same, make sure they are the same in Home and Traveler.
static RadioEvents_t RadioEvents;
static uint8_t TxdBuffer[64];
static uint8_t RcvBuffer[64];
In the definition of the variables, the reception buffer is added where the received data will be stored.
void setup()
{
// Initialize LoRa chip.
lora_rak4630_init();
// Initialize LEDs.
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_BLUE, LOW);
pinMode(LED_GREEN, OUTPUT);
digitalWrite(LED_GREEN, LOW);
// Initialize Serial for debug output
Serial.begin(115200);
Serial.println("=====================================");
Serial.println("Traveler saying Hello!");
Serial.println("=====================================");
// Initialize the Radio callbacks
RadioEvents.TxDone = OnTxDone;
RadioEvents.RxDone = OnRxDone;
RadioEvents.TxTimeout = OnTxTimeout;
RadioEvents.RxTimeout = OnRxTimeout;
RadioEvents.RxError = OnRxError;
RadioEvents.CadDone = OnCadDone;
// Initialize the Radio
Radio.Init(&RadioEvents);
// Set Radio channel
Radio.SetChannel(RF_FREQUENCY);
// Set Radio TX configuration
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, TX_TIMEOUT_VALUE);
// Set Radio RX configuration
Radio.SetRxConfig(MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true);
send();
}
The Setup() function initializes all the elements that we are going to use: The LoRa® chip, the blue and green LEDs that we will use as transmit and receive indicators, and the serial port for debugging.
Lines 20 to 25 initialize all the events that LoRa® works with, making sure that both the transmit and receive events have a function associated with all of them. We will see what each of these functions does later.
Start the Radio and define the frequency, make sure that this frequency matches the frequency of the antennas that you have connected to your WisBlock and this corresponds to the frequency of your region. You can check the frequency of each country here. Next, start the TX and RX configuration.
Finally, a test transmission is started.
void OnTxDone(void)
{
digitalWrite(LED_BLUE, HIGH);
Serial.println("OnTxDone");
Radio.Rx(RX_TIMEOUT_VALUE);
delay(100);
digitalWrite(LED_BLUE, LOW);
delay(5000);
}
The OnTxDone function changes, in this case, it will only send a confirmation through the serial monitor and turn on the blue LED.
void OnRxDone(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr)
{
digitalWrite(LED_GREEN, HIGH);
Serial.println("OnRxDone");
delay(100);
digitalWrite(LED_GREEN, LOW);
memcpy(RcvBuffer, payload, size);
Serial.printf("RssiValue=%d dBm, SnrValue=%d\n", rssi, snr);
for (int idx = 0; idx < size; idx++)
{
Serial.printf("%02X ", RcvBuffer[idx]);
}
Serial.println("");
delay(100);
Radio.Standby();
Radio.SetCadParams(LORA_CAD_08_SYMBOL, LORA_SPREADING_FACTOR + 13, 10, LORA_CAD_ONLY, 0);
Radio.StartCad();
}
The changes in the OnRxDone function are: we turn on the green led to indicate that data has arrived. Configure the WisBlock to stop and check if there are packets in the channel. The Radio.SetCadParameters function receives 5 attributes which are the number of symbols to use for CAD (Channel Activity Detection) operations, Limit for detection of SNR peak used in the CAD, Minimum symbol recognition for CAD, Operation to be done at the end of CAD action, and the timeout to abort CAD activity. Finally, StartCad() starts the detection.
void OnCadDone(bool cadResult){
if(cadResult){
Serial.println("Busy Buffer");
delay(100);
Radio.Standby();
Radio.SetCadParams(LORA_CAD_08_SYMBOL, LORA_SPREADING_FACTOR + 13, 10, LORA_CAD_ONLY, 0);
Radio.StartCad();
}
else{
Serial.println("Channel Free, I am sending data");
delay (100);
send();
}
}
The OnCadDone function is presented. It is responsible for reviewing the channel. If it is busy, it reports this event do a little delay and try again; if not, it sends data using the Send function implemented above.
Home
If you notice, the functions in Home are the same as in Traveler, with just a few changes:
void OnTxDone(void){
digitalWrite(LED_GREEN, HIGH);
Serial.println("OnTxDone");
Radio.Rx(RX_TIMEOUT_VALUE);
delay(100);
digitalWrite(LED_GREEN, LOW);
}
In the OnTxDone function, a 5-second delay is not performed, because this function is specific to the code for Traveler to send data every 5 seconds.
void send()
{
TxdBuffer[0] = 'H';
TxdBuffer[1] = 'i';
TxdBuffer[2] = '!';
Radio.Send(TxdBuffer, 3);
}
Finally, in the send() function, we use "Hi!" instead of "Hello" to identify the RX buffer of TX buffers. and that's it. We're going to try it.
First, each of the Modules must be programmed, as shown in figures 16 to 19.
Congratulations! You already have two point-to-point transceivers working. You can see them in the transmission (Green) and reception (Blue) LED indicators of the two WisBlock modules.
Finally, we can see the responses on the serial monitor.
Figures 20 and 21 show the reception data: RSSI, SNR, and the message in hexadecimal, which can differentiate which is the Traveler and which is the Home.
Range Measurement
The distance was measured from the 12th floor in a residential building with an exterior view. Two tests were carried out, indoor and outdoor.
The indoor test consisted of measuring the maximum vertical and horizontal distance that could be achieved between the two WisBlocks. The result was satisfactory. The entire floor was covered, approximately 40 meters of range. In the vertical test, a range of 3 floors was achieved, approximately 8.1 meters away. The building where the tests were implemented has very thick walls in each of the tests.
The outdoor test was executed leaving the Home device in a closed place (one wall away from the outside) and surrounding the building trying to get a line of sight. The greatest range that was achieved was 250 meters, unfortunately, the test was carried out in a fairly populated city (Bogotá - Colombia) the line-of-sight tests could not be carried out.
We invite you to make the tests in the place where you are and to leave us your comments, telling us how far you were able to reach communication with your WisBlock Starter Kit. And tell us under what conditions and in which part of the world you carried out your tests. We will like to read and get in touch with you.
PlatformIO (Optional)To build this project using PlatformIO, you need to go to the Home page and click on Import Arduino project.
Next, select the card with the WisCore RAK4631 Board. Select Use libraries installed by Arduino IDE, and choose the file from the directory where it is saved. Do the same procedure with both projects, Home and Traveler.
Before compiling, make sure to add these two lines to the project in the platformio.ini file, particularly, if you have two WisBlocks connected to your PC. Select the port name according to your operating system (Mac, Linux, or Windows). Make sure to do it for each of the projects with their respective port.
With this done, select the default project and proceed to Build and Upload.
You now have your program ready and the LED indicators working as shown in Figure 20. Now let's see the response in VS code's serial monitor.
You can see in Figures 26 and 27, the data from the Traveler and the Home, received via the serial port. Remember to select the device in the lower blue bar, in the Default button.
What’s next?In the next tutorials, we are going to start working with all the sensors that RAKwireless WisBlock has in amazing projects. Leave us ideas of topics you'd like to see in the comments and join our RAKstar community on Hackster by following the official RAKwireless profile.
Comments