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:
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:
For more details contact me, your feedback is welcome.
Hello dear could you please make a video for all of us to better understanding
Much appreciated 🙂
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);