Application

The following scripts show you how to build a basic application onto the LoRaWAN network of the NOI Techpark. Please, choose the programming language you are more comfortable with between python or Java.

python

At first, you'll need to set your environment: (you can try to skip one or more steps at your own risk)

  1. install PyCharm or IntelliJ IDEA;
  2. install Python 3.7;
  3. create a python project in you IDE (please, make sure to select the python interpreter as Existing Interpreter);
  4. create a new python file (File → New → Python File → hack.py);
  5. copy and paste the following script into the hach.py file;
  6. replace DEV EUI, USERNAME, PASSWORD, DB NAME inside the script;
  7. run the code through right click on the script and select Run hack.

Now, this basic python web server should be querying the InfluxDB database and sending back a downlink to the node through the LoRaServer API. You can simply modify the last loop in the script to build your own application.

"""Python server consuming LoRaServer and InfluxDB APIs

Copyright (C) 2018  Eurac Research - Center for Sensing Solutions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA"""

import requests
import warnings
import time
import json

# Global variables
loraAPI_url = "http://saocompute.eurac.edu/LoRaApp/api/"
influxAPI_url = "http://saocompute.eurac.edu/sensordb/query"
database = "XXXXXXXXXX" # the database name here
username = "XXXXXXXXXX" # your username here
password = "XXXXXXXXXX" # your password here
dev_EUI = "XXXXXXXXXX" # your device EUI here
TIME_SLEEP = 5

# JWT generation method needed to query the LoRa App Server API
def regenerateJWT(password,username):
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
    }
    url = loraAPI_url + 'internal/login'
    data = {"password": password, "username": username}
    response = postRequest(url, headers, data)
    print(response.content)
    if (response.status_code == 200):
        new_jwt = json.loads(response.content.decode())['jwt']
        return new_jwt
    else:
        print("Problems in generating a new JWT")
        return -1

# Downlink building method
def sendDownlink(jwt, message):
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Grpc-Metadata-Authorization': 'Bearer ' + jwt
    }

    url = loraAPI_url + 'devices/' + dev_EUI + '/queue'
    print(url)
    data = {"deviceQueueItem": {"confirmed": True, "devEUI": dev_EUI,
                                "fCnt": getFrameUplink(jwt), "fPort": 2, "jsonObject": message}}
    response = postRequest(url, headers, data)
    if (response.status_code == 200):
        print("Message sent")
    else:
        print(response.content)
        print("Problems in sending message")

# Uplink-Downlink synchronization method
def getFrameUplink(jwt):
    headers = {
        'Accept': 'application/json',
        'Grpc-Metadata-Authorization': 'Bearer ' + jwt
    }

    url = loraAPI_url + 'devices/' + dev_EUI + '/activation'
    response = getRequest(url, headers)
    frameuplink = json.loads(response.content.decode())['deviceActivation']['fCntUp']

    if (response.status_code == 200):
        #print("Frame uplink retrieved successfully")
        return frameuplink
    else:
        print("Problem in retrieving the frame uplink.")
        return 0

# GET request method
def getRequest(url, headers):
    return requests.get(url, verify=False, headers=headers)


# POST request method
def postRequest(url, headers, data):
    return requests.post(url, verify=False, headers=headers, json=data)

# Main body
warnings.filterwarnings("ignore")
current_jwt = regenerateJWT(password,username)
database = "?db="+database
username = "&u="+username
password = "&p="+password
first_loop = True
old_value = ""

if(current_jwt!=-1):

    while (True):

        warnings.filterwarnings("ignore")

        # Query that retrieves the last value stored into the InfluxDB database
        base_url = influxAPI_url + database + username + password
        query_url = "SELECT device_name, value FROM device_frmpayload_data_message WHERE dev_eui='" + dev_EUI + "' ORDER BY time DESC LIMIT 1"
        final_url = base_url + "&q=" + query_url
        response = requests.get(final_url)
        jsonresult = json.loads(response.content.decode())['results'][0]['series'][0]
        print("DEV_NAME " + jsonresult['values'][0][1])
        value = jsonresult['values'][0][2]
        print("VALUE_STORED " + value)

        if(first_loop):
            old_value = value
            first_loop = False
        if(value != old_value):
            downlink_msg = str('{"message":"'+value+'"}')
            print(downlink_msg)
            sendDownlink(current_jwt, downlink_msg)
            old_value = value
        else:
            print("Same value")

        print("----------------------------------------------------------")

        time.sleep(TIME_SLEEP)
else:
    print("Some problem occurred... exiting the script")

Note

If the script doesn't work, please try to:

  • edit the configuration settings (Run → Edit Configurations). Here, check if the Script path is set to the name of your python script.
  • check if your anti-virus is blocking the python script. In case, make it allowing the script and run the server one more time.

If you are more likely to consume the InfluxDB and the LoRaServer APIs as they are, please go to the section APIs.

Java

Before to start, set your environment as follows: (you can skip one or more steps at your own risk)

  1. install Eclipse or IntelliJ IDEA;
  2. install Java JDK 1.8;
  3. create a new Java project named hack;
  4. download the JSON .jar file and add it to the dependencies of the project;
  5. look at this guide on how to use InfluxDB in a Java environment.

The scripts below pretend to be a basic Java web server. It queries the InfluxDB database and builds the downlinks to be sent back to the node through the LoRaServer API.

Remember to replace DEV EUI, USERNAME, PASSWORD, and DB NAME in the code.

"""Java server consuming LoRaServer and InfluxDB APIs

Copyright (C) 2018  Eurac Research - Center for Sensing Solutions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA"""

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

public class hack_server {

    // Global variables
    public static String influxAPI_url = "http://saocompute.eurac.edu/sensordb/query"; // influx URL API
    private static String database = "XXXXXXXXXX"; // the database name here
    private static String username = "XXXXXXXXXX"; // the username here
    private static String password = "XXXXXXXXXX"; // the password here
    private static String dev_EUI = "XXXXXXXXXX"; // the device EUI goes here

    public static void main(String[] args) throws Exception {
        JSONParser jsonParser = new JSONParser();
        LoRaAPI l = new LoRaAPI(jsonParser, username, password);

        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    }
                }
        };

        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };

        String jwt = l.generateJWT(allHostsValid);
        String old_value = "";
        boolean first_loop = true;

        if(!jwt.equals("no_jwt")) {
            database = "?db="+database;
            username = "&u="+username;
            password = "&p="+password;
            while (true) {
                String base_url = influxAPI_url + database + username + password;
                String query_url = "SELECT device_name, value FROM device_frmpayload_data_message WHERE dev_eui='"+ dev_EUI + "' ORDER BY time DESC LIMIT 1";
                String restUrl = URLEncoder.encode(query_url, "UTF-8");
                URL final_url = new URL(base_url + "&q=" + restUrl);
                HttpURLConnection conn = (HttpURLConnection) final_url.openConnection();
                conn.setRequestMethod("GET");
                Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
                String outputGetRequest = ((BufferedReader) in).readLine();
                JSONObject outputJson = (JSONObject) jsonParser.parse(outputGetRequest);
                JSONObject currentJson = (JSONObject) ((JSONArray) outputJson.get("results")).get(0);
                JSONObject currentSerie = (JSONObject) ((JSONArray) currentJson.get("series")).get(0);
                JSONArray values = (JSONArray) currentSerie.get("values");
                String device_name = ((JSONArray) values.get(0)).get(1).toString();
                String value = ((JSONArray) values.get(0)).get(2).toString();

                System.out.println("DEV_NAME " + device_name);
                System.out.println("VALUE_STORED " + value);

                if(first_loop){
                    old_value = value;
                    first_loop = false;
                }

                if(!value.equals(old_value)) {
                    String downlink_msg = "{\"message\":\""+value+"\"}";
                    System.out.println(downlink_msg);
                    l.sendInstructions(allHostsValid, downlink_msg, dev_EUI);
                    old_value = value;
                } else {
                    System.out.println("Same value");
                }

                System.out.println("----------------------------------------------------------");
                TimeUnit.SECONDS.sleep(5);
            }
        } else {
            System.out.print("Some problem occurs... exiting the script");
        }
    }
}

Here follows the LoRaAPI class which implements the methods to consume the LoRaServer API.

"""Java class consuming LoRaServer and InfluxDB APIs

Copyright (C) 2018  Eurac Research - Center for Sensing Solutions
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA"""

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;

public class LoRaAPI {
    private static String loraAPI_url = "http://saocompute.eurac.edu/LoRaApp/api/";
    private String username;
    private String password;
    private JSONParser jsonParser;
    private String JWT;

    public LoRaAPI(JSONParser jsonParser, String username, String password) {
        this.jsonParser = jsonParser;
        this.username = username;
        this.password = password;
    }

    public String getJWT() {
        return JWT;
    }

    public void setJWT(String JWT) {
        this.JWT = JWT;
    }

    // Method to generate the JWT needed to connect to the LoRaServer API
    public String generateJWT(HostnameVerifier allHostsValid) throws Exception{
        JSONObject loginData = new JSONObject();
        loginData.put("password", password);
        loginData.put("username", username);
        URL url = new URL(loraAPI_url+"internal/login");
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        HttpURLConnection conn = this.postRequest(url,loginData.toString(),allHostsValid);
        int resp = conn.getResponseCode();
        if(resp==200){
            Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            String outputGetRequest = ((BufferedReader) in).readLine();
            JSONObject currentJson = (JSONObject) jsonParser.parse(outputGetRequest);
            String newJWT = (String) currentJson.get("jwt");
            setJWT(newJWT);
            return newJWT;
        } else {
            System.out.println("There's a problem generating JWT");
            return "no_jwt";
        }
    }

    // Method to send an acknowledgment back to the node
    public void sendInstructions(HostnameVerifier allHostsValid, String instruction, String dev_EUI) throws Exception {
        String payload = formatDeviceInstructions(dev_EUI,allHostsValid, instruction).toString();
        URL url = new URL(loraAPI_url + "devices/" + dev_EUI + "/queue");
        HttpURLConnection conn = this.postRequest(url,payload,allHostsValid);
        int resp = conn.getResponseCode();
        if(resp==200){
            System.out.println("Message sent");
            Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
        } else {
            System.out.println(conn.getErrorStream());
            System.out.println("There's a problem sending the message");
        }
    }

    // Method to create the JSONObject with the instructions to be forwarded to the node
    public JSONObject formatDeviceInstructions(String dev_EUI, HostnameVerifier allHostsValid, String instruction) throws Exception {
        JSONObject devQueue = new JSONObject();
        JSONObject devQueueItem = new JSONObject();
        devQueueItem.put("confirmed",true);
        devQueueItem.put("devEUI", dev_EUI);
        devQueueItem.put("fCnt", getFrameUplink(dev_EUI,allHostsValid));
        devQueueItem.put("fPort", 1);
        devQueueItem.put("jsonObject", instruction);
        devQueue.put("deviceQueueItem",devQueueItem);
        return devQueue;
    }

    // Method to get the counter of uplink frames of the node (needed to synchronize the uplink/downlink handshake)
    public int getFrameUplink(String dev_EUI, HostnameVerifier allHostsValid) throws Exception {
        URL url = new URL(loraAPI_url+ "devices/" + dev_EUI + "/activation");
        HttpURLConnection conn = this.getRequest(url,allHostsValid);
        int resp = conn.getResponseCode();
        if(resp==200){
            Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
            String outputGetRequest = ((BufferedReader) in).readLine();
            JSONObject currentJson = (JSONObject) jsonParser.parse(outputGetRequest);
            JSONObject singleObj = (JSONObject) currentJson.get("deviceActivation");
            int fCntUp = Integer.parseInt(singleObj.get("fCntUp").toString());
            return fCntUp;
        } else {
            System.out.println("There's a problem retrieving the frame uplink");
            return -1;
        }
    }

    // Method to do a POST request to the LoRaServer API
    public HttpURLConnection postRequest(URL url, String payload, HostnameVerifier allHostsValid) throws Exception {
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setRequestProperty("Accept", "application/json");
        conn.setRequestProperty("Authorization", "Bearer " + getJWT());
        conn.setDoOutput(true);
        conn.getOutputStream().write(payload.getBytes("UTF-8"));
        return conn;
    }

    // Method to do a GET request to the LoRaServer API
    public HttpURLConnection getRequest(URL url, HostnameVerifier allHostsValid) throws Exception {
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Accept", "application/json");
        conn.setRequestProperty("Authorization", "Bearer " + getJWT());
        return conn;
    }
}

If you are more likely to consume the InfluxDB and the LoRaServer APIs as they are, please look at the section APIs.

APIs

InfluxDB API

Here, the link to the official documentation of the InfluxDB API.

LoRa App Server API

Proceed on this link if you want directly access the LoRa App Server API.

Note

  • Some methods won't be available according to your permissions.
  • Remember that you are not allowed to send uplinks more often than every 10 seconds.

Creative Commons Licence
This work is licensed under a Creative Commons Attribution 4.0 International License.