Connecting the ESP32 Based Norvi IOT Device to Ubidots Over MQTT Protocol and Controlling Multiple Relay Outputs

by NORVI Controllers in Circuits > Microcontrollers

1114 Views, 1 Favorites, 0 Comments

Connecting the ESP32 Based Norvi IOT Device to Ubidots Over MQTT Protocol and Controlling Multiple Relay Outputs

main.PNG

Norvi IIOT devices are ESP32 embedded industrial controllers. These devices come with variety of features with different models which make them ideal for industrial automation and IoT solutions.

In this instructable, we'll be looking at how to connect a Norvi Device to Ubidots over MQTT protocol.

Ubidots is an IoT platform for managing IoT devices and collecting, storing, visualizing, and analyzing data from those devices. Here, we'll be using the Ubidots dashboard to control the relay outputs of the Norvi device.

So what do you need to get started?

Check this instructable to understand more about the device - Getting started with Norvi devices

Understanding the MQTT Protocol​

  • MQTT is a messaging protocol that was developed to establish a reliable standard for communication between machine and machine. It's a publish-and-subscribe protocol, where client devices and applications publish and subscribe to topics handled by a broker instead of communicating with a server.

  • MQTT Messages - These are the information that you want to exchange between your devices. It can be data or commands.
  • MQTT Topics - Through Topics, you register your interest in incoming messages or you specify where you want to publish the message. These topics are managed by MQTT brokers
  • MQTT Client - Anything that runs an MQTT library and links to a broker over a network will essentially become an MQTT client (from a microcontroller to a large server). Clients do not explicitly send messages to and from each other, but instead, connect with MQTT broker-managed Topics.

  • MQTT Broker - The duty of the broker to accept all messages, sort the messages, determine who is interested in them and then publish the message to all subscribing customers according to the topics.

  • MQTT Subscribe & Publish - A device may post a message on a topic through a publish or it can subscribe to the desired topic to accept messages.

Here, as we are going to control the inbuilt relays of the Norvi device, only subscribing to the topics is considered to achieve our goal.

Creating an Ubidot Account and Adding Dashboards & Widgets to Control the Relays.

1.PNG
2.PNG
3.PNG
4.PNG
5.PNG
7.PNG
6.PNG
8.PNG
9.PNG
10.PNG
11.PNG

After creating an Ubidot account, follow the below steps in order to set up the Norvi Device with the Ubidot account and to add the dashboards & widgets to control the relays.

  1. After creating the account, select "Setup My First Device".
  2. Then click "Add Device" and name your device (In our case, it's named ESP32)
  3. Next, choose the created device.
  4. Using the "Add Variable" option, create 6 variables inside the device and name them accordingly. (In our example, the variables are named as RELAY1-RELAY7)
  5. Then select the data button which is at the top middle of our account screen and create a dashboard.
  6. Select the "Add new widget" option available in the top right corner and choose the Switch widget.
  7. Click the add variable option.
  8. Select our device " ESP32"
  9. Then choose the variable RELAY1.
  10. Ensure that the right variable option selected and save the widget.
  11. After these steps, you'll see an Indicator widget is created which corresponds to the variable RELAY1. Repeat the above steps and create 6 such Switch widgets.

Programming the Device

  • First, install the PubSubClient library and upload the below sketch to the Norvi device.
<pre>#include <WiFi.h>
#include <PubSubClient.h>


#define WIFISSID "" // Put your WifiSSID here
#define PASSWORD "" // Put your wifi password here
#define TOKEN "" // Put your Ubidots' TOKEN
#define MQTT_CLIENT_NAME "" // MQTT client Name, please enter your own 8-12 alphanumeric character ASCII string; 
                                           //it should be a random and unique ascii string and different from all other devices

#define VARIABLE_LABEL_SUBSCRIBE1 "relay1" 
#define VARIABLE_LABEL_SUBSCRIBE2 "relay2" 
#define VARIABLE_LABEL_SUBSCRIBE3 "relay3"
#define VARIABLE_LABEL_SUBSCRIBE4 "relay4"
#define VARIABLE_LABEL_SUBSCRIBE5 "relay5"
#define VARIABLE_LABEL_SUBSCRIBE6 "relay6"

#define DEVICE_LABEL "esp32" // 

#define RELAY1 14
#define RELAY2 12
#define RELAY3 13
#define RELAY4 15
#define RELAY5 2
#define RELAY6 33

char mqttBroker[]  = "industrial.api.ubidots.com";
char payload[100];
char topic[150];


WiFiClient ubidots;
PubSubClient client(ubidots);


void callback(char* topic, byte* payload, unsigned int length)
{
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  
  for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
  }
  if (strcmp(topic,"/v1.6/devices/esp32/relay1/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY1, HIGH);
    } else {
        digitalWrite(RELAY1, LOW);
    }
  }
   if (strcmp(topic,"/v1.6/devices/esp32/relay2/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY2, HIGH);
    } else {
        digitalWrite(RELAY2, LOW);
    }
  }
  if (strcmp(topic,"/v1.6/devices/esp32/relay3/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY3, HIGH);
    } else {
        digitalWrite(RELAY3, LOW);
    }
  }
if (strcmp(topic,"/v1.6/devices/esp32/relay4/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY4, HIGH);
    } else {
        digitalWrite(RELAY4, LOW);
    }
  }
  if (strcmp(topic,"/v1.6/devices/esp32/relay5/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY5, HIGH);
    } else {
        digitalWrite(RELAY5, LOW);
    }
  }
  if (strcmp(topic,"/v1.6/devices/esp32/relay6/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY6, HIGH);
    } else {
        digitalWrite(RELAY6, LOW);
    }
  }
  Serial.write(payload, length);
  Serial.println();
}


void reconnect() 
{
 // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("Attempting MQTT connection...");
    
    // Attemp to connect
    if (client.connect(MQTT_CLIENT_NAME, TOKEN, "")) {
      Serial.println("Connected");
    } else {
      Serial.print("Failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 2 seconds");
      // Wait 2 seconds before retrying
      delay(2000);
    }
  }
}

void setup() 
{
  Serial.begin(115200);
  WiFi.begin(WIFISSID, PASSWORD);
  // Assign the pin as OUTPUT 
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);
  pinMode(RELAY5, OUTPUT);  
  pinMode(RELAY6, OUTPUT);
  
  Serial.println();
  Serial.print("Wait for WiFi...");
  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  
  Serial.println("");
  Serial.println("WiFi Connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  client.setServer(mqttBroker, 1883);
  client.setCallback(callback);
}

void loop() 
{
  if(WiFi.status() != WL_CONNECTED) {
    Serial.print("wifi disconnected\n");
  
    WiFi.begin(WIFISSID, PASSWORD);
  }
  
 if (!client.connected()) {
  reconnect();
  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE1);
  client.subscribe(topic);
 
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE2);
  client.subscribe(topic);
  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE3);
  client.subscribe(topic);

  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE4);
  client.subscribe(topic);

  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE5);
  client.subscribe(topic);

  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE6);
  client.subscribe(topic);
  }

  client.loop();
  delay(1000);
}<br>

(For installing the ESP32 board and booting up the device, check the steps 1 and 2 of our previous instructable -Getting Started With Norvi Devices )

Understanding the Code

13.PNG
14.PNG
api label.PNG
relay label.PNG
  • We begin by adding the required libraries. The PubSubClient will operate to allow an MQTT connection. The WiFi library is required to establish the WiFi connection.

#include <WiFi.h>
#include <PubSubClient.h>
  • Then give your WiFi name and password.
#define WIFISSID "" 
#define PASSWORD "" 
  • Next, insert your Ubidots token here. Follow the below steps to find your unique token.

  1. Select your profile
  2. Select "API Credentials"
  3. Under Token, copy the "default token"
#define TOKEN ""
  • MQTT_CLIENT_NAME is the ID with which your device will be identified by the broker. If your device tries to connect with the same ID that has already been taken by another device, the connection will be refused. So a unique client name should be provided.

#define MQTT_CLIENT_NAME ""
  • Include the variable label names that have to be subscribed and the device label name in order to operate the relays.
#define VARIABLE_LABEL_SUBSCRIBE1 "relay1" 
#define VARIABLE_LABEL_SUBSCRIBE2 "relay2" 
#define VARIABLE_LABEL_SUBSCRIBE3 "relay3"
#define VARIABLE_LABEL_SUBSCRIBE4 "relay4"
#define VARIABLE_LABEL_SUBSCRIBE5 "relay5"
#define VARIABLE_LABEL_SUBSCRIBE6 "relay6"

#define DEVICE_LABEL "esp32" // 
  • Next, the GPIOs of the relays are defined.
#define RELAY1 14
#define RELAY2 12
#define RELAY3 13
#define RELAY4 15
#define RELAY5 2
#define RELAY6 33
  • In the below lines the necessary char arrays are defined. The MQTT broker variable contains the Ubidots broker. The payload reserves memory space for data to be sent later in the routine. Additional memory storage and prompt data transfer are made possible by the topic array.

char mqttBroker[]  = "industrial.api.ubidots.com";
char payload[100];
char topic[150];
  • Then we will initialize the wifi client (ubidots) that will be pass as the parameter to the PubSubClient constructor.

WiFiClient ubidots;
PubSubClient client(ubidots);
  • Next, we need to define a role for a callback. This function is very important because controls the modifications of the variables in Ubidots and it is unique to the PubSubClient library. Below, the arguments for this function are clarified.

  1. char* topic: The topic is the endpoint of your variable, according to the API it should be /v1.6/devices/{LABEL_DEVICE} for publishing and /v1.6/devices/{LABEL_DEVICE}/{LABEL_VARIABLE}/lv for subscribing.

  2. byte* payload - It is the response obtained directly from the broker once a change in one of your subscribed variables has taken place.

  3. unsigned int length -This refers to the length of the payload.

void callback(char* topic, byte* payload, unsigned int length)
  • Then the conditions are defined to operate the relays for every variable topic subscribed.
  if (strcmp(topic,"/v1.6/devices/esp32/relay1/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY1, HIGH);
    } else {
        digitalWrite(RELAY1, LOW);
    }
  }
   if (strcmp(topic,"/v1.6/devices/esp32/relay2/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY2, HIGH);
    } else {
        digitalWrite(RELAY2, LOW);
    }
  }
  if (strcmp(topic,"/v1.6/devices/esp32/relay3/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY3, HIGH);
    } else {
        digitalWrite(RELAY3, LOW);
    }
  }
if (strcmp(topic,"/v1.6/devices/esp32/relay4/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY4, HIGH);
    } else {
        digitalWrite(RELAY4, LOW);
    }
  }
  if (strcmp(topic,"/v1.6/devices/esp32/relay5/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY5, HIGH);
    } else {
        digitalWrite(RELAY5, LOW);
    }
  }
  if (strcmp(topic,"/v1.6/devices/esp32/relay6/lv")==0){
   if ( (char)payload[0] == '1') {
        digitalWrite(RELAY6, HIGH);
    } else {
        digitalWrite(RELAY6, LOW);
    }<br>
  • An additional function for reconnecting (in case the MQTT goes down) is written.

Void reconnect() 
{
 // Loop until we're reconnected
  while (!client.connected()) {
    Serial.println("Attempting MQTT connection...");
    
    // Attemp to connect
    if (client.connect(MQTT_CLIENT_NAME, TOKEN, "")) {
      Serial.println("Connected");
    } else {
      Serial.print("Failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 2 seconds");
      // Wait 2 seconds before retrying
      delay(2000);
    }
  }
}
  • In the setup() function, we initialize the serial port baud rate and set the defined pins as the output of the Norvi device to control the relays. We then connect to the wifi access point and print some debug messages.
    The function, client.setServer() is a method of PubSubClient to set the broker URL and the port to begin communications. And the client.setCallBack() makes sure that callback() function defined previously is available.

void setup() 
{
  Serial.begin(115200);
  WiFi.begin(WIFISSID, PASSWORD);
  // Assign the pin as OUTPUT 
  pinMode(RELAY1, OUTPUT);
  pinMode(RELAY2, OUTPUT);
  pinMode(RELAY3, OUTPUT);
  pinMode(RELAY4, OUTPUT);
  pinMode(RELAY5, OUTPUT);  
  pinMode(RELAY6, OUTPUT);
  
  Serial.println();
  Serial.print("Wait for WiFi...");
  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  
  Serial.println("");
  Serial.println("WiFi Connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  client.setServer(mqttBroker, 1883);
  client.setCallback(callback);
}
  • Inside the void loop() function, first, we verify that the device is connected; if not, the function reconnect() is called to establish the connection. Once connected, we subscribe to the variable topics in Ubidots for retrieving data and to control the relays of the Norvi device.

  • The client.loop() function is a built-in function that will read the receive and send buffers, and process any messages it finds. It looks at the messages on the receiving side, and it will trigger the callback function depending on the message type.

void loop() 
{
  if(WiFi.status() != WL_CONNECTED) {
    Serial.print("wifi disconnected\n");
  
    WiFi.begin(WIFISSID, PASSWORD);
  }
  
 if (!client.connected()) {
  reconnect();
  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE1);
  client.subscribe(topic);
 
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE2);
  client.subscribe(topic);
  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE3);
  client.subscribe(topic);

  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE4);
  client.subscribe(topic);

  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE5);
  client.subscribe(topic);

  
  sprintf(topic, "/v1.6/devices/%s/%s/lv", DEVICE_LABEL, VARIABLE_LABEL_SUBSCRIBE6);
  client.subscribe(topic);
  }

  client.loop();
  delay(1000);
}

To check more about the Norvi Lineup - www.norvi.lk