Add Seeed LoRa-E5 sensor node to Private LoRaWAN Network and Machinechat JEDI Pro IoT Platform

Description

This project sets up a Seeed LoRa-E5 based Arduino sensor node, adds the sensor node to a ChirpStack based private LoRaWAN network, and uses HTTP integration and a custom data collector to pass sensor data into Machinechat’s JEDI Pro IoT platform. The LoRa sensor uses a Seeeduino Xiao, Grove LoRa-E5 board, and Grove DHT11 temperature/humidity sensor. Seeed’s IP67 rated industrial LoRaWAN gateway is used to forward the LoRa sensor packets to a private ChirpStack LoRaWAN network server running on a Seeed Odyssey Blue mini PC (with Ubuntu linux installed). Machinechat’s JEDI Pro IoT platform runs on the same Odyssey Blue mini PC.

Hardware

Software

  • JEDI Pro or JEDI Pro SSE
    Adaptable software for IoT data collection, visualization, monitoring and data storage that can be integrated into IoT solutions. Capabilities include: collect data from sensors, devices and machines; build intuitive real-time and historical data and system view dashboards; create rules to monitor and respond to data conditions automatically; receive alert notifications by email and SMS. JEDI Pro SSE is the Seeed Studio Edition version of JEDI Pro that adds a Data Collector for Seeed’s line of SenseCAP LoRaWAN sensors
  • ChirpStack
    The ChirpStack open-source LoRaWAN Network Server stack provides open-source components for LoRaWAN networks. The modular architecture makes it possible to integrate within existing infrastructures.
  • Arduino
    Arduino is an open-source electronics platform based on easy-to-use hardware and software.

Background

This post is a follow-on project that builds on the related TechForum post Set up a private LoRaWAN Sensor Network with Machinechat and Seeed SenseCAP which details setting up a private LoRaWAN IoT sensor network using off the shelf hardware and software available at Digi-Key. Software used in the related project included Machinechat’s JEDI Pro applications software and ChirpStack’s LoRaWAN network server application. Hardware used in the project included a Seeed Odyssey x86 Mini PC and SenseCAP outdoor LoRaWAN gateway. For this post a Seeeduino Xiao, Grove LoRa-E5 board, and Grove DHT11 temperature/humidity sensor are configured as a Lora sensor and added to the existing private LoRaWAN network.

Implementation

For this project, USB powers the Seeeduino Xiao and it’s 3V3 output powers the Grove LoRa-E5 and Grove DHT11. The Seeeduino UART is connected to the TX/RX pins of the Grove LoRa-E5 and input D2 is connected to the SIG pin of the Grove DHT11 sensor. The Arduino application code runs on the Seeeduino Xiao to send AT commands to the LoRa-E5 and read the DHT11 sensor. The application joins the LoRa network, reads the sensor, encodes the sensor data in CayenneLPP format, sends the data over LoRa, delays and loops back. Below schematic diagram illustrates how the circuit is wired up and implemented (see Scheme-it LoRa-E5 Project).

image

Electrical connections are shown below:

Seeeduino Xiao pin Grove LoRa-E5 connector Grove DHT11 connector
GND GND GND
3V3 VCC VCC
2 SIG
6 RX
7 TX

Set up the Seeeduino Xiao, Grove LoRa-E5 and Grove DHT11 sensor

1 - Set up Arduino on the Seeeduino Xiao. See link Getting started with Seeeduino Xiao

2 - Install libraries needed for application. Add these libraries thru Arduino’s Library Manager:

3 - Code walkthrough (filename: Xiao_LoraE5CayenneLPP_rev2.ino)

Initial setup

// below code is based on Seeed LoRaE5 example code and modified to NOT use the display on the Seeeduino Xiao expansion board (just uses a Xiao connected to DHT11 sensor and LoRaE5 
// eval board0 Seeed example code at https://wiki.seeedstudio.com/Grove_LoRa_E5_New_Version/
//
//note: all Seeed LoRaE5 Grove boards have App key of 2B7E151628AED2A6ABF7158809CF4F3C 
// rev 2 just changes off time to 60 seconds and humidity sensor channel to 2 to test chirpstack/JEDI integration

#include <Arduino.h>
//sr #include <U8x8lib.h>
#include "DHT.h"
#include <CayenneLPP.h>


#define DHTPIN 2     // what pin DHT11 sensor is connected to 
//#define DHTPIN 0 // what pin we're connected to
 
// Uncomment whatever type you're using!
#define DHTTYPE DHT11 // DHT 11
// #define DHTTYPE DHT22   // DHT 22  (AM2302)
//#define DHTTYPE DHT21   // DHT 21 (AM2301)
 
DHT dht(DHTPIN, DHTTYPE);

CayenneLPP lpp(51);   //setup Cayenne LPP (low power payload) buffer
 
//sr U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/U8X8_PIN_NONE);
// U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);   // OLEDs without Reset of the Display
 
static char recv_buf[512];
static bool is_exist = false;
static bool is_join = false;
static int led = 0;

int buf_size;  //Cayenne LPP buffer payload size
int Pointer;   //pointer used in Cayenne LPP buffer
int Offset = 12;    //offset to where Cayenne LPP payload data starts
int Loop1;
int Loop2;

 
static int at_send_check_response(char *p_ack, int timeout_ms, char *p_cmd, ...)
{
    int ch;
    int num = 0;
    int index = 0;
    int startMillis = 0;
    va_list args;
    memset(recv_buf, 0, sizeof(recv_buf));
    va_start(args, p_cmd);
    Serial1.printf(p_cmd, args);
    Serial.printf(p_cmd, args);
    va_end(args);
    delay(200);
    startMillis = millis();
 
    if (p_ack == NULL)
    {
        return 0;
    }
 
    do
    {
        while (Serial1.available() > 0)
        {
            ch = Serial1.read();
            recv_buf[index++] = ch;
            Serial.print((char)ch);
            delay(2);
        }
 
        if (strstr(recv_buf, p_ack) != NULL)
        {
            return 1;
        }
 
    } while (millis() - startMillis < timeout_ms);
    return 0;
}
 
static void recv_prase(char *p_msg)
{  
    if (p_msg == NULL)
    {
        return;
    }
    char *p_start = NULL;
    int data = 0;
    int rssi = 0;
    int snr = 0;
 
    p_start = strstr(p_msg, "RX");
    if (p_start && (1 == sscanf(p_start, "RX: \"%d\"\r\n", &data)))
    {
        Serial.println(data);
// sr        u8x8.setCursor(2, 4);
// sr        u8x8.print("led :");
        led = !!data;
// sr        u8x8.print(led);
        if (led)
        {
            digitalWrite(LED_BUILTIN, LOW);
        }
        else
        {
            digitalWrite(LED_BUILTIN, HIGH);
        }
    }
 
    p_start = strstr(p_msg, "RSSI");
    if (p_start && (1 == sscanf(p_start, "RSSI %d,", &rssi)))
    {
        /*
        u8x8.setCursor(0, 6);
        u8x8.print("                ");
        u8x8.setCursor(2, 6);
        u8x8.print("rssi:");
        u8x8.print(rssi);
        */
        Serial.println(rssi);
    }
    p_start = strstr(p_msg, "SNR");
    if (p_start && (1 == sscanf(p_start, "SNR %d", &snr)))
    {
        /*
        u8x8.setCursor(0, 7);
        u8x8.print("                ");
        u8x8.setCursor(2, 7);
        u8x8.print("snr :");
        u8x8.print(snr);
        */
        Serial.println(snr);
    }
}
 
void setup(void)
{
//sr    u8x8.begin();
//sr    u8x8.setFlipMode(1);
//sr    u8x8.setFont(u8x8_font_chroma48medium8_r);
 
    Serial.begin(115200);
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);
 
    Serial1.begin(9600);                  //UART serial1 connection to LoRaE5
    Serial.print("E5 LORAWAN TEST\r\n");
//sr    u8x8.setCursor(0, 0);
 
    if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
    {
        is_exist = true;
        at_send_check_response("+ID: AppEui", 1000, "AT+ID\r\n");
        at_send_check_response("+MODE: LWOTAA", 1000, "AT+MODE=LWOTAA\r\n");
        //at_send_check_response("+DR: EU868", 1000, "AT+DR=EU868\r\n");  //original
        at_send_check_response("+DR: US915", 1000, "AT+DR=US915\r\n"); 
        //at_send_check_response("+CH: NUM", 1000, "AT+CH=NUM,0-2\r\n");  //original
        at_send_check_response("+CH: NUM", 1000, "AT+CH=NUM,8-15,65\r\n"); // configure channels to match chirpstack
        at_send_check_response("+KEY: APPKEY", 1000, "AT+KEY=APPKEY,\"2B7E151628AED2A6ABF7158809CF4F3C\"\r\n");
        at_send_check_response("+CLASS: C", 1000, "AT+CLASS=A\r\n");
        at_send_check_response("+PORT: 8", 1000, "AT+PORT=8\r\n");
        delay(200);
//sr        u8x8.setCursor(5, 0);
//sr        u8x8.print("LoRaWAN");
        is_join = true;
    }
    else
    {
        is_exist = false;
        Serial.print("No E5 module found.\r\n");
//sr        u8x8.setCursor(0, 1);
//sr        u8x8.print("unfound E5 !");
    }
 
    dht.begin();
}
 

Main loop - read DHT11 sensor

void loop(void)
{
    float temp = 0;
    float humi = 0;
 
    temp = dht.readTemperature();
    humi = dht.readHumidity();
    Serial.print("Humidity: ");
    Serial.print(humi);
    Serial.print(" %\t");
    Serial.print("Temperature: ");
    Serial.print(temp);
    Serial.println(" *C");

Main loop - encode sensor data in CayenneLPP

//***************** start of Cayenne LPP code **************************
    lpp.reset();
    lpp.addTemperature(1, temp);
    lpp.addRelativeHumidity(2, humi);
    //lpp.addBarometricPressure(3, 1002.4);  //just use dummy value

    buf_size = lpp.getSize();
    Serial.print("Cayenne LPP buffer size = ");
    Serial.println(buf_size);

    uint8_t *payload = lpp.getBuffer();
    char cmd[128];
    
    // Lmsg is just test code that probably can be deleted?
    char Lmsg[4];
    for (unsigned char i = 0; i < lpp.getSize(); i++)
    {
      sprintf(Lmsg, "%02X", (char)payload[i]);
      Serial.print(Lmsg);
    }

    Serial.println(" payload"); 


//**************end of payload builder code**************************


Main Loop - LoRa communication

    // start of LoRa communication code 
    if (is_exist)
    {
        int ret = 0;
        if (is_join)
        {
 
            ret = at_send_check_response("+JOIN: Network joined", 12000, "AT+JOIN\r\n");
            if (ret)
            {
                is_join = false;   //resets join check?
            }
            else
            {
                at_send_check_response("+ID: AppEui", 1000, "AT+ID\r\n");
                Serial.print("JOIN failed!\r\n\r\n");
                delay(5000);
            }
        }
        else
        {
            char cmd[128]; 
            //sprintf(cmd, "AT+CMSGHEX=\"%04X%04X\"\r\n", (int)temp, (int)humi); //original
            //****** add in CayenneLPP ecoding ******
            sprintf(cmd, "AT+CMSGHEX=\"%02X\"\r\n", (char)payload[0]);  //first part of message
            // add data payload to LoRa message by looping thru Cayenne LPP data buffer
            char TestMsg[2];   
            for (Loop1 = 0; Loop1 < buf_size; Loop1++)
              {
                Pointer = (Loop1*2) + Offset;
                sprintf(TestMsg, "%02X", (char)payload[Loop1]);
                // write data buffer character to LoRa message
                for (Loop2 = 0; Loop2 < 2; Loop2++)
                  {
                     cmd[Loop2 + Pointer] = TestMsg[Loop2];
                  }
              }            
            // create end of message characters for LoRa message (need ",return,null characters)
            char EndMsg[20];
            sprintf(EndMsg, "test\"%02X\"\r\n", (char)payload[0]);
            // add ", return and null characters to end of LoRa message
            for (unsigned char i = 2; i < 7; i++)
              {
                cmd[Pointer + i] = EndMsg[5 + i];
              }            
            //******** end of CayenneLPP encodeding *********
            ret = at_send_check_response("Done", 5000, cmd);                     //sends cmd command over LoRa
            if (ret)
            {
                recv_prase(recv_buf);
            }
            else
            {
                Serial.print("Send failed!\r\n\r\n");
            }
            delay(5000); 
        }
    Serial.println(" in main loop checking LoRa then wait 60 seconds");   
    delay(60000); //main delay 60 seconds 
    }
    else
    {
        delay(1000);
    }
}

Latest source code for the WioTerminalMQTTDisplay2topicOAQrev2.ino application is on github at below link:

Determine Grove LoRa-E5 board device EUI

In Arduino, compile and upload Xiao_LoraE5CayenneLPP_rev2.ino code into Seeeduino Xiao and enable Serial Monitor. Review Serial Monitor output to determine Grove LoRa-E5 device EUI.
image

Add the LoRa-E5 based sensor node to the ChirpStack LoRaWAN network server

(note: this project and below steps assume that a ChirpStack based private LoRaWAN network is active and in range of the LoRa-E5 sensor node, if not refer to TechForum post Set up a private LoRaWAN Sensor Network with Machinechat and Seeed SenseCAP)

1 - In ChirpStack, select Device-profile and Create. Name Device-profile “Seeed LoRaE5”, Select “1.0.2” for LoRaWAN MAC version, Select “A” for LoRaWAN Regional Parameters version, Select “Default ADR algorithm” for ADR algorithm, and enter “3600” for Uplink interval. In JOIN(OTAA/ABP) tab, check box for “Device supports OTAA”. In CODEC tab, select “Cayenne LPP” in CODEC dropdown list.

2 - In ChirpStack, select Applications, then select “FarmTest” and then select Create. Enter “SeeedLoRaE5sensor” for Device name, enter “description” for Device description, enter Device EUI for Grove LoRa-E5 board (determined from above step “Determine Grove LoRa-E5 board device EUI”), enter “STM32WL Sensors” for Device-profile, and select CREATE DEVICE. (note: for initial testing and demonstration you may want to check Disable frame-counter validation box)

3 - Add Application key for Device. Enter Application key “2B7E151628AED2A6ABF7158809CF4F3C” (note: this is default key in LoRa-E5, to change refer to KEY section of LoRa-E5 AT Command Specification) and select SET DEVICE-KEYS.

Set up and test ChirpStack HTTP Integration with JEDI Pro Generic LoRaWAN Custom Data Collector

ChirpStack is modified to add HTTP integration for forwarding LoRaWAN meta and sensor data to a specified IP address. Machinechat’s Generic LoRaWAN Custom Data Collector Plug-in is used for listening to the specified IP address and parsing the LoRaWAN data for review (when debug enabled) and use in the JEDI Pro platform.
(note: The Custom Data Collector consists of two files, lorawan-linux.bin and config.yml that are available from Machinechat at: https://support.machinechat.io/hc/en-us/articles/6046199010327-Generic-LoRaWAN-Custom-Data-Collector-Beta-for-JEDI-PRO-Linux )

1 - Enable HTTP Integration in ChirpStack.
Select “Add” in ChirpStack Integrations screen.

2 - Configure HTTP Integration
Select “JSON” for Payload marshaler , add IP address (use same IP as in config.yml file) for Endpoint URL , and select ADD INTEGRATION

3 - Copy lorawan-linux.bin and config.yml files to the ~/jedi/plugins directory where JEDI Pro is installed on the Ubuntu Linux Mini-PC. Modify config.yml file to enable debug and specify IP listening address.

4 - Run Custom Plug-In on command line using “./lorawan-linux.bin ./config.yml” on terminal in Ubuntu Linux Mini-PC and monitor output data. Data should look similar to below (note: remember to make lorawan-linux.bin file executable):

5 - Edit config.yml file to map LoRaWAN data to JEDI Pro data parameters and disable debug. For this project example, edit propertyNames so LoRaWAN cSproperty: “barometer.0” is mapped to mcProperty: “barometer”, LoRaWAN cSproperty: “temperatureSensor.1” is mapped to mcProperty: “temperature”, and LoRaWAN cSproperty: “humiditySensor.2” is mapped to mcProperty: “humidityr”. Disable debugging by setting “setDebug:” to false and save file.

Set up JEDI Pro Custom Data Collector and Data Dashboard

In JEDI Pro, select “Settings” tab, then select “Data Collectors” and select “Add Collector”.

Configure Collector as shown below. Name Data Collector “LoRaWAN” (or whatever you prefer), select Collector Type as “Custom Plug-In”, select “lorawan-linux.bin” as Plug-In Executable file, enter location of config.yml file (example: “/home/scottr/jedi/plugins/config.yml” for Plug-in Options, select check box for “Run As Background Process and Monitor” , then select “VALIDATE PLUG-IN” to verifiy functionality.

In JEDI Pro, select “Data Dashboards”, then select “+” to add a new chart.

Configure data chart and select “Add” to include in your Data Dashboard(see example below)

image

Conclusion

The combination of Arduino, Seeeduino Xiao, LoRa-E5 radio, and sensor provides a flexible modular platform to develop a LoRa sensor node. ChirpStack’s HTTP Integration and Machinechat’s Generic LoRaWAN Custom Data Collector are then configured to bring the LoRaWAN End Node sensor data into JEDI Pro for IoT data collection, visualization, monitoring and data storage. The example code can be easily modified for other sensors as needed.

References