Category Archives: Industrial automation
Playing with Raspberry Pi, Arduino, NodeMcu and MQTT
These days I’m playing with IoT. Today I want to use MQTT protocol to comunicate between different devices. First I’ve start a mqtt broker in my Laptop. For testing I’ll use mosquitto server. In production we can use RabbitMQ or even a 3party server such as iot.eclipse.org or even Amazon’s IoT service.
The idea is emit one value with one device, and listen this value whit the rest of devices and perform one action depending on that value. For example I will use one potentiometer connected to on NodeMcu micro controller.
This controller will connect to the mqtt broker and will emit the value of the potentiometer (reading the analog input) into one topic (called “potentiometer”). We can code our NodeMcu with Lua but I’m more confortable with C++ and Arduino IDE. First I need to connect to my Wifi and then connect to broker and start emmiting potentiometer’s values
#include <PubSubClient.h> #include <ESP8266WiFi.h> // Wifi configuration const char* ssid = "MY_WIFI_SSID"; const char* password = "my_wifi_password"; // mqtt configuration const char* server = "192.168.1.104"; const char* topic = "potentiometer"; const char* clientName = "com.gonzalo123.nodemcu"; int value; int percent; String payload; WiFiClient wifiClient; PubSubClient client(wifiClient); void wifiConnect() { Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("WiFi connected."); Serial.print("IP address: "); Serial.println(WiFi.localIP()); if (client.connect(clientName)) { Serial.print("Connected to MQTT broker at "); Serial.print(server); Serial.print(" as "); Serial.println(clientName); Serial.print("Topic is: "); Serial.println(topic); } else { Serial.println("MQTT connect failed"); Serial.println("Will reset and try again..."); abort(); } } void mqttReConnect() { while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect(clientName)) { Serial.println("connected"); client.subscribe(topic); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } void setup() { Serial.begin(9600); client.setServer(server, 1883); wifiConnect(); delay(10); } void loop() { value = analogRead(A0); percent = (int) ((value * 100) / 1010); payload = (String) percent; if (client.connected()) { if (client.publish(topic, (char*) payload.c_str())) { Serial.print("Publish ok ("); Serial.print(payload); Serial.println(")"); } else { Serial.println("Publish failed"); } } else { mqttReConnect(); } delay(200); }
Now we will use another Arduino (with a ethernet shield).
We’ll move one servomotor depending to NodeMcu’s potentiomenter value. This Arduino only needs to listen to MQTT’s topic and move the servo.
#include <SPI.h> #include <Servo.h> #include <Ethernet.h> #include <PubSubClient.h> #define SERVO_CONTROL 9 byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; Servo servo; EthernetClient ethClient; // mqtt configuration const char* server = "192.168.1.104"; const char* topic = "potentiometer"; const char* clientName = "com.gonzalo123.arduino"; PubSubClient client(ethClient); void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] angle:"); String data; for (int i = 0; i < length; i++) { data += (char)payload[i]; } double angle = ((data.toInt() * 180) / 100); constrain(angle, 0, 180); servo.write((int) angle); Serial.println((int) angle); } void mqttReConnect() { while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Attempt to connect if (client.connect(clientName)) { Serial.println("connected"); client.subscribe(topic); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } void setup() { Serial.begin(9600); client.setServer(server, 1883); client.setCallback(callback); servo.attach(SERVO_CONTROL); if (Ethernet.begin(mac) == 0) { Serial.println("Failed to configure Ethernet using DHCP"); } delay(1500); // Allow the hardware to sort itself out } void loop() { if (!client.connected()) { mqttReConnect(); } client.loop(); }
Finally we’ll use one Raspberry Pi with a Sense Hat and we’ll display with its led matrix different colors and dots, depending on the NodeMcu’s value. In the same way than the Arduino script here we only need to listen to the broker’s topic and perform the actions with the sense hat. Now with Python
import paho.mqtt.client as mqtt from sense_hat import SenseHat sense = SenseHat() sense.clear() mqttServer = "192.168.1.104" red = [255, 0, 0] green = [0, 255, 0] yellow = [255, 255, 0] black = [0, 0, 0] def on_connect(client, userdata, rc): print("Connected!") client.subscribe("potentiometer") def on_message(client, userdata, msg): value = (64 * int(msg.payload)) / 100 O = black if value < 21: X = red elif value < 42: X = yellow else: X = green sense.set_pixels(([X] * value) + ([O] * (64 - value))) client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message client.connect(mqttServer, 1883, 60) client.loop_forever()
The hardware:
- 1 Arduino Uno
- 1 NodeMCU (V3)
- 1 potentiometer
- 1 Servo (SG90)
- 1 Raspberry Pi 3 (with a Sense Hat)
Source code is available in my github.
Reading Modbus devices with Python from a PHP/Silex Application via Gearman worker
Yes. I know. I never know how to write a good tittle to my posts. Let me show one integration example that I’ve been working with this days. Let’s start.
In industrial automation there’re several standard protocols. Modbus is one of them. Maybe isn’t the coolest or the newest one (like OPC or OPC/UA), but we can speak Modbus with a huge number of devices.
I need to read from one of them, and show a couple of variables in a Web frontend. Imagine the following fake Modbus server (it emulates my real Modbus device)
#!/usr/bin/env python ## # Fake modbus server # - exposes "Energy" 66706 = [1, 1170] # - exposes "Power" 132242 = [2, 1170] ## from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext from pymodbus.datastore import ModbusSequentialDataBlock from pymodbus.server.async import StartTcpServer import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) hrData = [1, 1170, 2, 1170] store = ModbusSlaveContext(hr=ModbusSequentialDataBlock(2, hrData)) context = ModbusServerContext(slaves=store, single=True) StartTcpServer(context)
This server exposes two variables “Energy” and “Power”. This is a fake server and it will returns always 66706 for energy and 132242 for power. Mobus is a binary protocol so 66706 = [1, 1170] and 132242 = [2, 1170]
I can read Modbus from PHP, but normally use Python for this kind of logic. I’m not going to re-write an existing logic to PHP. I’m not crazy enough. Furthermore my real Modbus device only accepts one active socket to retrieve information. That’s means if two clients uses the frontend at the same time, it will crash. In this situations Queues are our friends.
I’ll use a Gearman worker (written in Python) to read Modbus information.
from pyModbusTCP.client import ModbusClient from gearman import GearmanWorker import json def reader(worker, job): c = ModbusClient(host="localhost", port=502) if not c.is_open() and not c.open(): print("unable to connect to host") if c.is_open(): holdingRegisters = c.read_holding_registers(1, 4) # Imagine we've "energy" value in position 1 with two words energy = (holdingRegisters[0] << 16) | holdingRegisters[1] # Imagine we've "power" value in position 3 with two words power = (holdingRegisters[2] << 16) | holdingRegisters[3] out = {"energy": energy, "power": power} return json.dumps(out) return None worker = GearmanWorker(['127.0.0.1']) worker.register_task('modbusReader', reader) print 'working...' worker.work()
Our backend is ready. Now we’ll work with the frontend. In this example I’ll use PHP and Silex.
<?php include __DIR__ . '/../vendor/autoload.php'; use Silex\Application; $app = new Application(['debug' => true]); $app->register(new Silex\Provider\TwigServiceProvider(), array( 'twig.path' => __DIR__.'/../views', )); $app['modbusReader'] = $app->protect(function() { $client = new \GearmanClient(); $client->addServer(); $handle = $client->doNormal('modbusReader', 'modbusReader'); $returnCode = $client->returnCode(); if ($returnCode != \GEARMAN_SUCCESS) { throw new \Exception($this->client->error(), $returnCode); } else { return json_decode($handle, true); } }); $app->get("/", function(Application $app) { return $app['twig']->render('home.twig', $app['modbusReader']()); }); $app->run();
As we can see the frontend is a simple Gearman client. It uses our Python worker to read information from Modbus and render a simple html with a Twig template
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Demo</title> </head> <body> Energy: {{ energy }} Power: {{ power }} </body> </html>
And that’s all. You can see the full example in my github account