IoT

How to load Maximo meter readings via IBM Watson IoT platform

How to load Maximo meter readings via IBM Watson IoT platform
How to load Maximo meter readings via IBM Watson IoT platform

The best way to approach to the IoT world it’s for fun , the other way to approach to the IoT is to connect devices and build applications that learn from the physical world and analyze data from any source. The IBM Watson IoT platform scaling through cloud-based services and using rich analytics, it provides organizations with new insight for innovation and transformation.

In this articles I’m going to provide the code developed to implement the End-to-End Scenario: how to load Maximo meter readings via IBM Watson IoT platform.

For a theoretical point of view I suggest to read previous posts.

I used the ESP8266, a microcontroller Arduino compatible, as IoT device, and IBM Maximo asset manager as application and industry solution. If you’re just starting out, read Connect an ESP8266 to IBM Watson IoT Platform with Arduino SDK.

There are three stages:

First the IoT device sends data to IBM IoT platform by MQTT protocol. Here is the sketch to connect an ESP8266 to IBM Watson IoT Platform with Arduino SDK, using the WiFiClient library and the MQTT and PubSubClient library

// Sketch to publish distance readings from
// an attached sensor to IBM Watson IoT Platform.
// Mario Noioso - 2016

#include <SPI.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <MQTT.h>
#include <PubSubClient.h>
#include "Ultrasonic.h"

static const uint8_t TRIGGER_PIN = 12;   // pin D6 is the trigger pin
static const uint8_t ECHO_PIN = 14;         // pin D5 is the echo pin

// create a ranging device object
Ultrasonic ultrasonic(TRIGGER_PIN, ECHO_PIN);


int status = WL_IDLE_STATUS;       // WiFi status.

#define MAX_MSG_LENGTH 100
char msg[MAX_MSG_LENGTH];

int MQTT_PORT=1883;                
String server = "YOUR_ORGID.messaging.internetofthings.ibmcloud.com"; // IoT Server
String CLIENT_ID = "d:YOUR_ORGID:YOUR_DEVICE_TYPE:YOUR_DEVICEID";

// registered device
String AUTHMETHOD = "use-token-auth";
String AUTHTOKEN = "YOUR_TOKEN";

String PUBLISH_TOPIC = "iot-2/evt/status/fmt/json";
String SUBSCRIBE_TOPIC = "iot-2/cmd/+/fmt/json";

// variables to remember the measurements
int  lastRange = -1;
int  range = -1;


// Setup WiFi SSID and Password                   
char ssid[] = "YOUR_WIFI_SSID";                      
char pass[] = "YOUR_WIFI_PASSWORD";                

WiFiClient wifiClient;
PubSubClient client(wifiClient, server, MQTT_PORT);

void setup() {
  // initialize the serial port for debugging
  Serial.begin(115200);
  // wait for serial to initialize
  delay(2000);
 
// Initialize the WiFi client library and connect to Wifi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Conn SSID=");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    
    // wait up to 3 seconds for WiFi connection:
    int count = 0;    
    while ((count < 3) && (status != WL_CONNECTED)) {
      delay(1000);
      count++;
      status = wifiClient.connected();
    }
  }
 
  printWifiStatus();
}

void loop() {
  int rc = -1;
  if (WiFi.status() == WL_CONNECTED){
    if (!client.connected()){
      Serial.println("Conn MQTT");
      if (client.connect(MQTT::Connect(CLIENT_ID).set_auth(AUTHMETHOD, AUTHTOKEN)))
        Serial.println("Connected to MQTT server");
    }
  }

  // get the current range from ultrasonic sensor
  range = getRange();
   
  if (range >= 0) {
    // send to the console
    Serial.print(range);
    Serial.println(" CM");
 
    if (client.connected()) {
      // create message in JSON format
      buildMsg(range);

      // publish the message
      if (!client.publish(PUBLISH_TOPIC, msg)) {
        Serial.print("Pub failed rc=");
        Serial.println(rc);
      }
    } else {
      Serial.println("Not connected for publication");
    }
  }
 
  // check if MQTT client is connected
  if (client.connected()){
    client.loop();
  }

  // measure 10 times a second
  delay(100);
}


void printWifiStatus() {
  // print the SSID of the network we are attached to:
  Serial.print("WPA SSID: ");
  Serial.println(WiFi.SSID());
 
  // print the IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print(" IP=");
  Serial.println(ip);
 
  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print(" RSSI=");
  Serial.print(rssi);
}


int getRange() {
  return ultrasonic.Ranging(CM);
}

void buildMsg(int range) {
  // convert integer to string
  char r[7];
  itoa(range, r, 10);
 
  // clear the current msg
  for (int i=0; i<MAX_MSG_LENGTH; i++) {
    msg[i] = 0;
  }
 
  // start to build the message
  strcpy(msg, "{\"Range\": ");
  strcat(msg, r);
  strcat(msg, "}");
}

You have to register the device on IBM Watson IoT platform with your ORGID, DEVICE_TYPE, DEVICEID and TOKEN

Then a nodeRED flow handles the data and updates the Maximo’s meter:

How to load Maximo meter readings via IBM Watson IoT platform
How to load Maximo meter readings via IBM Watson IoT platform

Here is my Node Red code, use the import from clipboard utility to copy and paste this flow into your Node Red environment:

[{"id":"1e99c765.6e6621","type":"ibmiot","z":"3362e4f1.ea6d2c","name":"sensorDistance"},{"id":"4142fb8.9defb04","type":"ibmiot in","z":"3362e4f1.ea6d2c","authentication":"apiKey","apiKey":"1e99c765.6e6621","inputType":"evt","deviceId":"noioso2001","applicationId":"","deviceType":"+","eventType":"+","commandType":"","format":"json","name":"sensorDistance","service":"registered","allDevices":true,"allApplications":"","allDeviceTypes":true,"allEvents":true,"allCommands":"","allFormats":true,"x":303.15000915527344,"y":390.53330993652344,"wires":[["ec1af9de.ac63e","5b06a4f0.1c0ebc","784b2319.f8e3b4","96cc3c87.b80fc8"]]},{"id":"ec1af9de.ac63e","type":"debug","z":"3362e4f1.ea6d2c","name":"sensora json data","active":false,"console":"false","complete":"payload","x":599.1500091552734,"y":397.81666564941406,"wires":[]},{"id":"17d33656.f6a542","type":"function","z":"3362e4f1.ea6d2c","name":"get range value","func":"return {payload:msg.payload.Range};","outputs":1,"noerr":0,"x":135.4499969482422,"y":158.4499969482422,"wires":[["31d7f938.dc9516","c87baeed.868a5"]]},{"id":"31d7f938.dc9516","type":"debug","z":"3362e4f1.ea6d2c","name":"range value debug","active":true,"console":"false","complete":"payload","x":457.45001220703125,"y":146.4499969482422,"wires":[]},{"id":"c87baeed.868a5","type":"function","z":"3362e4f1.ea6d2c","name":"update meterdata sample date","func":"function dateFormat (date, fstr, utc) {\n  utc = utc ? 'getUTC' : 'get';\n  return fstr.replace (/%[YmdHMS]/g, function (m) {\n    switch (m) {\n    case '%Y': return date[utc + 'FullYear'] (); // no leading zeros required\n    case '%m': m = 1 + date[utc + 'Month'] (); break;\n    case '%d': m = date[utc + 'Date'] (); break;\n    case '%H': m = date[utc + 'Hours'] (); break;\n    case '%M': m = date[utc + 'Minutes'] (); break;\n    case '%S': m = date[utc + 'Seconds'] (); break;\n    default: return m.slice (1); // unknown code, remove %\n    }\n    // add leading zero if required\n    return ('0' + m).slice (-2);\n  });\n}\nvar dateNow = dateFormat (new Date (), \"%Y-%m-%dT%H:%M:%S\", true);\n\nvar maximoRest = \"http://MAXIMOIP/maxrest/rest/os/MXMETERDATA?_action=AddChange&location=MYHOME&siteid=SITEID&metername=RANGE&newreading=\";\nmsg.url = maximoRest + msg.payload + \"&newreadingdate=\" + dateNow + \"&_lid=maxadmin&_lpwd=PASSWORD\";\nnode.error(msg.url);\nreturn msg;","outputs":1,"noerr":0,"x":542.4500122070312,"y":245.4499969482422,"wires":[["8c0ee663.762738"]]},{"id":"8c0ee663.762738","type":"http request","z":"3362e4f1.ea6d2c","name":"Maximo Meterdata rest-api","method":"POST","ret":"txt","url":"","x":803.4500122070312,"y":335.45001220703125,"wires":[["757f46f3.8f0778"]]},{"id":"757f46f3.8f0778","type":"debug","z":"3362e4f1.ea6d2c","name":"output maximo meterdata rest-api ","active":true,"console":"false","complete":"payload","x":929.4500122070312,"y":154.4499969482422,"wires":[]},{"id":"96cc3c87.b80fc8","type":"delay","z":"3362e4f1.ea6d2c","name":"range sample rate","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","rateUnits":"minute","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":183.4499969482422,"y":293.45001220703125,"wires":[["17d33656.f6a542"]]}]



You have to change the MAXIMOIP, SITEID and user PASSWORD inside the Maximo rest-API.

In the final stage you have to configure a location with a continuous meter called RANGE:

IBM Maximo asset manager continuos meter readings
IBM Maximo asset manager continuos meter readings

For more details contact me, your feedback is welcome.

 

 

 

Related Articles

3 Comments

  1. Hello dear could you please make a video for all of us to better understanding

  2. Thanks for the code and instructions Mario – really helped and I have my sensor passing meter readings to Maximo 🙂

    I do have a slight issue with dates though – what does this function do in node red? Is it re-formatting the date string from the MQTT stream into something Maximo can accept? I appear to have a date miss-match where Maximo is thinking the entered date is in the future. I tried hard-coding a date and it works fine – but obviously only the once as you can’t have 2 entries with the same date/time.

    function dateFormat (date, fstr, utc) {
    utc = utc ? ‘getUTC’ : ‘get’;
    return fstr.replace (/%[YmdHMS]/g, function (m) {
    switch (m) {
    case ‘%Y’: return date[utc + ‘FullYear’] (); // no leading zeros required
    case ‘%m’: m = 1 + date[utc + ‘Month’] (); break;
    case ‘%d’: m = date[utc + ‘Date’] (); break;
    case ‘%H’: m = date[utc + ‘Hours’] (); break;
    case ‘%M’: m = date[utc + ‘Minutes’] (); break;
    case ‘%S’: m = date[utc + ‘Seconds’] (); break;
    default: return m.slice (1); // unknown code, remove %
    }
    // add leading zero if required
    return (‘0’ + m).slice (-2);
    });
    }
    var dateNow = dateFormat (new Date (), “%Y-%m-%dT%H:%M:%S”, true);

Back to top button