Node-RED + MQTT + ESP8266: how to drive an articulated PTZ system in WiFi

We continue our series of articles on remote control of the actuator and more particularly the PTZ (Pan-Tilt) articulated systems. In the previous tutorials we saw how to drive a servo from a smartphone using the libraries for IoT Cayenne and Blynk. Today we will learn how to do it using Node-RED. To communicate with the ESP8266 in WiFi (or from the Internet), we will use the MQTT (Mosquitto broker) communication protocol.

How it works ?

Here is a diagram showing how the PTZ articulated system will be piloted. We will use the Node-RED Dashboard UI module to create 2 sliders (Pan, Tilt). At each change Node-RED publishes an MQTT message. For example, servo / pan, 92. The message is transmitted over the WiFi local area network to ESP8266. The PubSubClient library allows you to subscribe to MQTT (Broker) and receive messages. After decoding the message, the respective servomotor is moved to the required angular position.

What do you need ?

You will therefore need a computer with Node-RED installed. If you need to know how to do it, choose the tutorial that suits your environment:

You will also need a MQTT (broker) server. There are several. Here we will use Mosquitto.
Equipment used

esp8266 Wemos D1 mini Wemos D1 Mini
chargeur raspbery pi 3 5v 3000ma 5V/3A micro-usb power supply
mini kit fpv pan tilt sg90 Mini Kit FPV Pan Tilt with 2x SG90
jumper dupont Jumper Dupont
breadboard Breadboard

Circuit

The circuit is identical to the previous tutorials.

node-red+mqtt+esp8266+ptz pan tilt kit

Arduino Code

To receive (or send) messages with the MQTT protocol, we will use the PubSubClient library (official documentation)

Installing libraries in the Arduino IDE

Before going any further, open the library manager and install the following libraries:

ESP8266

ide arduino install servo esp8266 library

servo(esp8266)

 ide arduino install servo esp8266 library

PubSubClient

 ide arduino install pubsubclient esp8266 library

How does the PubSubClient library work?

Here we will not go into the details of the connection to the WiFi network managed by the ESP8266WiFi library. We will only look at how to subscribe to several MQTT Topics and process incoming messages.

The PubSub library needs an object that contains the connection to the WiFi network. We start by creating an object using the WiFiClient class like this

WiFiClient espClient;

Then, we create a client object

PubSubClient client (espClient);

You will find 3 variables at the beginning of the program

  • const char * mqtt_server = “xxx.xxx.xxx.xxx”: this is the IP address of your MQTT (Broker) server
  • const char * topic_pan = “servo/pan”: topic that contains the pan angle
  • const char * topic_tilt = “servo/tilt”: Topic that contains the angle of the servo Tilt

In the setup () function, the following operations are performed

  • Each servo is attached to the corresponding Pin. For example Pan on D5 and Tilt on D6
pan.attach(D5);
tilt.attach(D6);
  • The setupWiFi()  function is used to connect the ESP8266 to the local WiFi network
  • We connect to the MQTT broker on port 1883 (the default port) client.setServer(mqtt_server, 1883)
  • Finally we define a callback function that will be called whenever an MQTT message is receivedclient.setCallback (callback)

Receiving MQTT messages in the Arduino program

The callback function (documentation) allows you to retrieve incoming messages. It contains the topic, the message (payload) and the length of the message. Before you can process the message, you must convert it to string. There is no helper to decode messages that arrive in the form of a byte array. Fortunately, there are many examples on the internet. Here’s how to decode the incoming message.

String string;
for (int i = 0; i < length; i++) {
   string+=((char)payload[i]);  
}

The function servo.write(position) accepts as input in whole, one will have to convert the message (payload)

int pos = string.toInt();

To determine whether to move the Pan or Tilt, we will use the string comparison function strcmp. It returns the number of different characters between the two strings. If there is no difference (return = 0), the two strings are identical. What will give us for example for the Topic Pan:

if ( strcmp(topic, topic_pan) == 0 ) {
  Serial.print("Move Pan to ");
  Serial.println(pos);
  pan.write(pos);  
}

Complete Arduino Code

Here is the complete Arduino code. Create a new sketch and modify the parameters in the code before uploading:

  • SSID, Password
  • MQTT IP broker address
  • Servo spindle Pan and tilt
/*
 * Node-RED + MQTT + ESP8266 + PTZ servo kit (Pan-Tilt)
 * How to move pan-tilt system with Node-RED - MQTT and ESP8266
 * - Use Node-RED UI Dashboard to move Pan and Tilt servos
 * - Node-RED sequencer : scan Pan and tilt 
 * - Node-RED reset position to 90°/90°
 * 
 * Comment déplacer le système pan-tilt avec Node-RED - MQTT et ESP8266
&nbsp;* - Utilisez le tableau de bord Node-RED UI pour déplacer les servos Pan et Tilt
&nbsp;* - Séquenceur Node-RED: balayage panoramique (Pan) et inclinaison (Tilt)
&nbsp;* - Reset des positions depuis Node-RED (90°/90°)
 * - Tutoriel complet
 * 
 * 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, see <http://www.gnu.org/licenses/>.
 * 
 * Licence : MIT
 * Copyright (C) 2017 : www.projetsdiy.fr and www.diyprojects.io
 */
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Servo.h> 
Servo pan; 
Servo tilt;

// Paramètre WiFi, MQTT - WiFi, MQTT parameters
const char* ssid = "yourSSID";                // WiFi SSID
const char* password = "yourPASSWORD";        // WiFi Password
const char* mqtt_server = "xxx.xxx.xxx.xxx";  // IP Broker MQTT
const char* topic_pan = "servo/pan";          // Topic MQTT pour servo Pan - Topic MQTT for Pan servor
const char* topic_tilt = "servo/tilt";        // Topic MQTT pour servo Tilt - Topic MQTT for Tilt servor
 
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {

  Serial.begin(115200);
  pan.attach(D5);
  tilt.attach(D6);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  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.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length) {
 String string;
 // Affiche le topic entrant - display incoming Topic
 Serial.print("Message arrived [");
 Serial.print(topic);
 Serial.print("] ");
 // décode le message - decode payload message
 for (int i = 0; i < length; i++) {
 string+=((char)payload[i]); 
 }
 // Affiche le message entrant - display incoming message
 Serial.print(string);
 Serial.print(" toInt ");
 // Conversion de la position en entier - convert position as an Integer
 int pos = string.toInt(); 
 Serial.println(pos);

 // Détermine quel servo doit être bougé - Determines which servo should be moved
 if ( strcmp(topic, topic_pan) == 0 ) {
 Serial.print("Move Pan to ");
 Serial.println(pos);
 pan.write(pos); 
 }
 if ( strcmp(topic, topic_tilt) == 0 ) {
 Serial.print("Move Tilt to ");
 Serial.println(pos);
 tilt.write(pos); 
 }
 delay(15); 
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      client.subscribe(topic_pan); 
      client.subscribe(topic_tilt); 
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
void loop() {   
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  delay(100);
}

Flow Node-RED

For this flow Nod-RED, you will need to install several modules. Open the palette manager (full tutorial) and install the following modules

Import this Node-RED code directly onto a flow

[{"id":"34545562.076d7a","type":"mqtt out","z":"2555341e.a5485c","name":"","topic":"servo/pan","qos":"1","retain":"false","broker":"ceed5755.206c58","x":680,"y":220,"wires":[]},{"id":"f9a85217.59893","type":"ui_slider","z":"2555341e.a5485c","name":"","label":"Pan","group":"9c59c1e2.8235f","order":4,"width":"6","height":"1","passthru":true,"topic":"","min":0,"max":"180","step":1,"x":510,"y":240,"wires":[["34545562.076d7a","d604bc1e.67691"]]},{"id":"d604bc1e.67691","type":"ui_gauge","z":"2555341e.a5485c","name":"","group":"9c59c1e2.8235f","order":5,"width":"6","height":"6","gtype":"donut","title":"Pan servo position ","label":"position ","format":"{{value}}","min":0,"max":"180","colors":["#00b500","#e6e600","#ca3838"],"x":680,"y":280,"wires":[]},{"id":"3a393dcd.afa272","type":"comment","z":"2555341e.a5485c","name":"ESP8266 MQTT Pan Tilt PTZ Control Servo","info":"","x":210,"y":680,"wires":[]},{"id":"4316c9.3c84a938","type":"mqtt out","z":"2555341e.a5485c","name":"","topic":"servo/tilt","qos":"1","retain":"false","broker":"ceed5755.206c58","x":680,"y":440,"wires":[]},{"id":"9087f6a0.b7e938","type":"ui_slider","z":"2555341e.a5485c","name":"","label":"Tilt","group":"9c59c1e2.8235f","order":4,"width":"6","height":"1","passthru":true,"topic":"","min":0,"max":"180","step":1,"x":410,"y":480,"wires":[["4316c9.3c84a938","584bfb6e.86a1d4"]]},{"id":"584bfb6e.86a1d4","type":"ui_gauge","z":"2555341e.a5485c","name":"","group":"9c59c1e2.8235f","order":5,"width":"6","height":"6","gtype":"donut","title":"Tilt servo position ","label":"position ","format":"{{value}}","min":0,"max":"180","colors":["#00b500","#e6e600","#ca3838"],"x":680,"y":500,"wires":[]},{"id":"6a8f2c2b.64bdb4","type":"ui_button","z":"2555341e.a5485c","name":"","group":"9c59c1e2.8235f","order":0,"width":0,"height":0,"label":"Reset","color":"","icon":"fa-undo","payload":"","payloadType":"str","topic":"","x":270,"y":620,"wires":[["743cd12b.d31b5"]]},{"id":"798e642.411779c","type":"mqtt out","z":"2555341e.a5485c","name":"","topic":"servo/tilt","qos":"1","retain":"false","broker":"ceed5755.206c58","x":640,"y":600,"wires":[]},{"id":"743cd12b.d31b5","type":"function","z":"2555341e.a5485c","name":"Pan (90°) - Tilt(90°)","func":"msg.payload = 90;\nreturn msg;","outputs":1,"noerr":0,"x":430,"y":620,"wires":[["d604bc1e.67691","4336088.add08f8","798e642.411779c","d2553ca9.80fdd","584bfb6e.86a1d4"]]},{"id":"d2553ca9.80fdd","type":"mqtt out","z":"2555341e.a5485c","name":"","topic":"servo/pan","qos":"1","retain":"false","broker":"ceed5755.206c58","x":640,"y":660,"wires":[]},{"id":"3ba0147.632dcec","type":"link in","z":"2555341e.a5485c","name":"","links":["4336088.add08f8","56f0b2a7.b1c81c"],"x":415,"y":240,"wires":[["f9a85217.59893"]]},{"id":"4336088.add08f8","type":"link out","z":"2555341e.a5485c","name":"","links":["3ba0147.632dcec","8117306f.30c94"],"x":595,"y":720,"wires":[]},{"id":"8117306f.30c94","type":"link in","z":"2555341e.a5485c","name":"","links":["4336088.add08f8","56f0b2a7.b1c81c"],"x":315,"y":480,"wires":[["9087f6a0.b7e938"]]},{"id":"89f37cd6.f1a11","type":"comment","z":"2555341e.a5485c","name":"www.projetsdiy.fr | www.diyprojects.io","info":"# Version française \nwww.projetsdiy.fr\n\n# English version\nwww.diyprojects.io","x":190,"y":720,"wires":[]},{"id":"e5657f97.89ffd","type":"repeat","z":"2555341e.a5485c","name":"","repetitions":"18","elseOutput":false,"outputs":1,"x":460,"y":80,"wires":[["68869228.30d7fc"]]},{"id":"d4ee4d5e.0ffcd","type":"ui_button","z":"2555341e.a5485c","name":"","group":"9c59c1e2.8235f","order":0,"width":"6","height":"1","label":"Scan Pan","color":"","icon":"","payload":"-10","payloadType":"num","topic":"","x":180,"y":160,"wires":[["68869228.30d7fc"]]},{"id":"68869228.30d7fc","type":"function","z":"2555341e.a5485c","name":"+10°","func":"var pos = msg.payload;\npos += 10;\nmsg.payload = pos;\nreturn msg;","outputs":1,"noerr":0,"x":370,"y":160,"wires":[["18dca8a0.64c977","f9a85217.59893"]]},{"id":"18dca8a0.64c977","type":"delay","z":"2555341e.a5485c","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":540,"y":160,"wires":[["e5657f97.89ffd"]]},{"id":"549400d6.d1279","type":"repeat","z":"2555341e.a5485c","name":"","repetitions":"18","elseOutput":false,"outputs":1,"x":360,"y":320,"wires":[["593953a.80dfbac"]]},{"id":"fe1eebc1.8d83c8","type":"ui_button","z":"2555341e.a5485c","name":"","group":"9c59c1e2.8235f","order":0,"width":"6","height":"1","label":"Scan Tilt","color":"","icon":"","payload":"-10","payloadType":"num","topic":"","x":100,"y":400,"wires":[["593953a.80dfbac"]]},{"id":"593953a.80dfbac","type":"function","z":"2555341e.a5485c","name":"+10°","func":"var pos = msg.payload;\npos += 10;\nmsg.payload = pos;\nreturn msg;","outputs":1,"noerr":0,"x":270,"y":400,"wires":[["4cfcf34c.3c7d3c","9087f6a0.b7e938"]]},{"id":"4cfcf34c.3c7d3c","type":"delay","z":"2555341e.a5485c","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":420,"y":400,"wires":[["549400d6.d1279"]]},{"id":"ceed5755.206c58","type":"mqtt-broker","z":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"willTopic":"","willQos":"0","willPayload":"","birthTopic":"","birthQos":"0","birthPayload":""},{"id":"9c59c1e2.8235f","type":"ui_group","z":"","name":"PTZ Servo Control","tab":"84e11c13.a96e9","order":2,"disp":true,"width":"12"},{"id":"84e11c13.a96e9","type":"ui_tab","z":"","name":"ESP8266","icon":"dashboard","order":2}]

node-red ptz servo control esp8266 wemos d1 mini flow

What does this flow do?

There are 2 sliders to control the position of the Pan servo and Tilt servo. A Reset button is used to reposition the 2 servos at 90 °. Finally, a repeat loop allows a scanning with a step of 10 ° in each direction (Pan and Tilt).

node-red ptz servo control esp8266 wemos d1 mini

Receiving MQTT messages

You can listen to messages that pass through the MQTT network using a client. For example, you can use MQTT.fx (available for Linux, MacOS and Windows, the download page). Start MQTT.fx and log in to the Broker. Subscribe to the topic servo / #. The # allows you to retrieve all messages from the tree. Here pan and tilt.

node-red mqtt mqttfx

Testing in operation

After uploading the program to ESP8266 and deploying flow Node-RED, open the dashboard from any web browser. You can also control the articulated system from the Internet browser of a tablet or a smartphone. Open the page at

http://IP_NODERED:1880/ui

Here is a short video demonstration to complete the article.

Subscribe to the weekly newsletter

No spam and no other use will be made of your email. You can unsubscribe anytime.

  • Diego Moreira

    Hello, I need your help! Please.

    • Ed Darby

      Hey, what’s up?

  • Ed Darby

    Great project, thanks, merci, got it working in no time at all.

    https://youtu.be/RiTL7d-TgyQ

DIY Projects