GPS, IoT, Blockchain Integration to ERP

I’ve read a number of articles discussing how Blockchain could have a significant impact on Trade & Logistics, especially item tracking. Granted, Blockchain is not a requirement for shipment tracking, but it does deliver a number of benefits through being a shared, secure ledger that, depending on the network, could provide an automatic openness almost immediately. That is a vast improvement over building a custom customer and partner portal to query legacy backend systems.

Of course, there remains the problem of now having to integrate Blockchain into your legacy ERP system, a whole different level of headache. So, in this post I’m going to do a simple POC to simulate how easy, or hard, it would be to build an item tracking service using Ethereum Blockchain, add to that GPS tracking with temperature and humidity monitoring, and get that to your ERP system, in this case Microsoft Dynamics 365. I want to achieve that without modifying the ERP system in any way, by using Microsoft Flow, a PowerApp and Microsoft Common Data Service. The idea is that end users, customers or partners can use the PowerApp to monitor shipments and climate conditions in real-time. Supply-chain visibility every step of the way, basically.

To start, I built a simple IoT monitoring device around the Adafruit Huzzah. I’ll be using WiFi here, making a wild assumption that WiFi is available wherever this device goes. In the real world, GPRS or Loran might be more suitable, but I don’t have that available in my toolkit just yet and besides, this is an experiment only. I’ve added a low-cost GPS, DHT11 temperature and humidity sensor, and an LCD screen to show me what is happening without requiring connecting to my laptop via the serial interface. The basic IoT device is shown below, with GPS and DHT-11 working and transmitting data.

Circuit

The C code for the IoT device is shown below. I do a POST to my Ethereum network of choice with hardcoded addresses, and embed the GPS coordinates and DHT11 state into the data portion of the Ethereum transaction. Addressing and data is entirely up to you; perhaps instead of hardcoding, this can all be read off an SD card.

#include <DHT.h>
#include "TinyGPS++.h"
#include <SoftwareSerial.h>
#include <Adafruit_SSD1306.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"

#define SSID "WiFiSSID" 
#define PASS "mypassword" 
#define OLED_RESET LED_BUILTIN
#define DHTPIN 12
#define DHTTYPE DHT11

TinyGPSPlus gps;
DHT dht(DHTPIN, DHTTYPE);
Adafruit_SSD1306 display(OLED_RESET);
SoftwareSerial mySerial(13, 15);

const char *gpsStream =
  "$GPRMC,045103.000,A,3014.1984,N,09749.2872,W,0.67,161.46,030913,,,A*7C\r\n"
  "$GPGGA,045104.000,3014.1985,N,09749.2873,W,1,09,1.2,211.6,M,-22.5,M,,0000*62\r\n"
  "$GPRMC,045200.000,A,3014.3820,N,09748.9514,W,36.88,65.02,030913,,,A*77\r\n"
  "$GPGGA,045201.000,3014.3864,N,09748.9411,W,1,10,1.2,200.8,M,-22.5,M,,0000*6C\r\n"
  "$GPRMC,045251.000,A,3014.4275,N,09749.0626,W,0.51,217.94,030913,,,A*7D\r\n"
  "$GPGGA,045252.000,3014.4273,N,09749.0628,W,1,09,1.3,206.9,M,-22.5,M,,0000*6F\r\n";

void setup() {
  Serial.begin(9600);
  dht.begin();
  display.setCursor(0,0);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.display();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.println("Connecting");
  display.println("to");
  display.println("WiFi...");
  display.display();

  WiFi.begin(SSID, PASS);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  } 
  display.clearDisplay();
  display.display();
  display.setCursor(0,0);
  display.println("EtherGPS");
  display.println("www.xalentis.com");
  display.display();
  mySerial.begin(38400); // GPS
  delay(5000); // warm up GPS 
  display.clearDisplay();
  display.display();
  display.setCursor(0,0);
  display.println("Scanning...");
  display.display();
}

void loop() 
{
  while (*gpsStream)
    if (gps.encode(*gpsStream++))
      updateInfo();
}

void updateInfo()
{
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  if (gps.location.isValid())
  {
    display.clearDisplay();
    display.display();
    display.setCursor(0,0);
    display.println("Temp:" + String(t));
    display.println("Hum:" + String(h));
    display.println("Lat:" + String(gps.location.lat()));
    display.println("Lon:" + String(gps.location.lng()));
    display.display();

    String data = "0x";
    String deviceSerial = "3132333435"; // 12345 in HEX
    data = data + deviceSerial + "2c"; // comma
    
    String temp = String(t);
    String hum = String(h);
    String lat = String(gps.location.lat());
    String lon = String(gps.location.lng());
    byte buffer[255]={0};
    
    //temp
    temp.getBytes(buffer, 255, 0);
    for (int i=0;i<=temp.length()-1;i++)
    {
      data = data + String((int)buffer[i], HEX);
    }
    data = data + deviceSerial + "2c"; // comma

    //hum
    hum.getBytes(buffer, 255, 0);
    for (int i=0;i<=hum.length()-1;i++)
    {
      data = data + String((int)buffer[i], HEX);
    }
    data = data + deviceSerial + "2c"; // comma

    //latitude
    lat.getBytes(buffer, 255, 0);
    for (int i=0;i<=lat.length()-1;i++)
    {
      data = data + String((int)buffer[i], HEX);
    }
    data = data + deviceSerial + "2c"; // comma

    //longitude
    lon.getBytes(buffer, 255, 0);
    for (int i=0;i<=lon.length()-1;i++)
    {
      data = data + String((int)buffer[i], HEX);
    }

    // build up our Ethereum transaction
    StaticJsonBuffer<1000> JSONbufferTwo;  
    JsonObject& uploadJSON = JSONbufferTwo.createObject(); 
    uploadJSON["jsonrpc"] = "2.0";
    uploadJSON["method"] = "personal_sendTransaction";      
    JsonArray&  uploadQueryParams = uploadJSON.createNestedArray("params");
    JsonObject& callTxParams = JSONbufferTwo.createObject();
    callTxParams["from"] = "0x27f6f763ae5c52721db57c4423c298a78de1f22a";
    callTxParams["to"] = "0xcaade3aa018d57d808fceb16824c47dfd206484c";
    callTxParams["value"] = "0x6FC23AC00"; //hex value 30 Gwei 
    callTxParams["gas"] = "0x30D40"; //hex value for 200000 -high gas limit for good measure          
    callTxParams["gasPrice"] = "0x6FC23AC00"; //hex value 30 Gwei gasprice 21gwei is typical
    callTxParams["data"] = data; // device,tem,hum,lat,long
    uploadQueryParams.add(callTxParams);
    uploadQueryParams.add("myetherpassword");
    uploadJSON["id"] = 1;
    String uploadString;
    uploadJSON.printTo(uploadString);
    callGeth(uploadString); // send for mining
  }
}

String callGeth(String inputJSON) 
{
  HTTPClient http;
  http.begin("http://13.72.73.21:8545/");
  http.addHeader("Content-Type", "application/json");
  int httpCode = http.POST(inputJSON);
  String JSONResult = http.getString(); // contains Txn
  http.end();
  return JSONResult;
}

At this point the transactions are flowing to the Blockchain network and that is great, but we need to be able to monitor the Blockchain for transactions we are interested in, so we can pull that off the Blockchain and into an ERP system, right?

The easiest way to do that is to use Xalentis Fusion. Sign up for a trial account at www.xalentis.com or grab it via Microsoft AppSource. Once signed-up, and logged-in, you’ll end up at the main dashboard as shown below.

xalgps1

Follow the Getting Started tutorial which takes about 10 minutes to create a pair of accounts and top them up with credit as required. The Ethereum network being used is a canned version of Microsoft’s Project Bletchley, so it’s not on the main or test Ethereum networks and can be used without spending any real Ether. You can deploy your own network and use that within the Fusion platform as well, by creating transaction filters pointing to your own deployed RPC node. Make sure your RPC node is visibly outside your firewall, obviously.

The following image shows us having created a transaction filter to monitor the default RPC node at http://xaleth4kq.eastus.cloudapp.azure.com, for any transactions made from the address 0x27f6f763ae5c52721db57c4423c298a78de1f22a. Filters can be created to match any transaction, from any address, or even containing a specific string value in the data portion. This is useful when the address(es) constantly change, while a specific identifier is passed within the data portion, perhaps a Device ID, Company ID or Serial Number of sorts – anything static.

xalgps2

Filters execute rules containing a simple compiled script, and this is where actions are performed on matching transactions. The script below has been added as a rule for our filter.

xalgps3

The rule simply extracts whatever is in the transaction data field, parses that and constructs a JSON packet. This packet will be passed to a Microsoft Flow we will be creating.

We’ll need a place to store our data. Using Flow, we could push directly into Dynamics 365, but since we don’t want to directly modify our ERP by adding a new table, I’ve chosen to use Microsoft Common Data Service as a temporary store instead. The image below shows the new Entity we’ve created with fields for Device, Temperature, Humidity, Longitude and Latitude.

GPS_CDS

Using Microsoft Flow, we’ll first use the Request action to accept an incoming POST request (from our Rule). Next, we’ll take the body of the POST request, parse it, and store the fields into our new CDS Entity. The Flow design is shown below.

GPS_Flow

Use the generated URL from the Flow to update the Rule – the final line calling Flow requires that URL.

Run the Flow, and then power up the IoT device to start submitting GPS and climate information into the Blockchain network. As transactions are mined into new blocks, Fusion will detect the transaction matching our Filter, and execute the associated Rule. The Rule in turn will parse the transaction data field, parse the content, construct it as JSON, and call our Flow with that body content. When the Flow executes, the JSON will be parsed and the data elements inserted as a new record into the CDS, as shown below.

GPS_DataCDS

We can use the data now stored in the CDS to create a PowerApp that displays that information on a Google Map. The PowerApp shown is fairly basic, but with enough time, patience and data this can be turned into something much more interactive, and it is real-time, a vast improvement over building a customer tracking portal from scratch, getting updates only when items are scanned with a barcode or RFID reader.

GPS_Map

Apart from our Rule script, we’ve used virtually no coding, and we’ve not modified our production ERP system in any way. As a bonus, we also have a mobile app that customers and partners can use!

Advertisements