Project tutorial
SCADA Using Cellular IoT with MKR GSM 1400

SCADA Using Cellular IoT with MKR GSM 1400 © GPL3+

Applying IoT technologies to revive an old SCADA telemetry system.

  • 4,760 views
  • 2 comments
  • 12 respects

Components and supplies

Abx00018 featured wi0j4nqatf
Arduino MKR GSM 1400
×1
MAX485 TTL to RS485 Converter
×1
MT-102 GSM RTU from Inventia
×1

Apps and online services

About this project

PART 1

I took up a challenge from an oil/gas client to revive a GSM based SCADA system that they had inherited, and to do so at very low cost. The existing RTUs at the remote sites used GPRS which is no longer available. 3G, however, is available at all sites. Fortunately, the existing RTUs have the ability to be programmed as Modbus RS485 slave devices. So the solution is to use the Arduino MKR 1400 to interface with the existing RTU devices and then have them connect to a cloud service and send MQTT updates. At the base, Node-Red will be used to connect to the cloud service and subscribe to the data points and expose them to the HMI which is a very old version of Wonderware Intouch. The diagram below shows the proposed set up.

The video below gives more details of the proposed solution:

PART 2

In this part, I set up the hardware and write the Arduino code to enable the MKR 1400 to read a single Input Register via Modbus from the MT-102 slave device. The Arduino code is attached below as "ModbusReader01.ino".

Here is the wiring diagram.

The video below gives a deeper explanation of what I am doing.

PART 3

In this part, I expand the Arduino code. The MKR 1400 connects to the 2G/3G data network and publishes the single data value to a mqtt topic in the free mosquitto eclipse mqtt broker. I then use Node-Red to subscribe to the topic to verify that the data is reaching where it is supposed to. The video below gives a deeper explanation of what I did and the Arduino code for this part is attached below as "ModbusMQTT01.ino".

PART 4 - FINAL!

In this last part, I write a small.NET forms application that reads the single topic from the cloud MQTT broker and exposes the value via a Modbus TCP Server so that a very old version of Wonderware Intouch would be able to read it. To create the.NET application, I used two libraries as follows:

EasymodbusTCP... http://easymodbustcp.net/en/

M2Mqtt... https://www.nuget.org/packages/M2Mqtt/

Code

ModbusReader01Arduino
Reads a single Input Register value and displays it on the serial monitor.
// ---------------------------------------------------------------------------
// Include the base required libraries
// ---------------------------------------------------------------------------
#include <Arduino.h>
#include <SensorModbusMaster.h>

// ---------------------------------------------------------------------------
// Set up the MT-102 Modbus slave information
// ---------------------------------------------------------------------------

// Define the sensor's modbus address
byte modbusAddress = 0x01;    // The sensor's modbus address, or SlaveID
long modbusBaudRate = 9600;   // The baud rate the sensor uses

// Define pin number variables
const int DEREPin = 7;       // The pin controlling Recieve Enable and Driver Enable

// Serial1 is the serial port used on the MKR 1400
HardwareSerial* modbusSerial = &Serial1;

// Construct the modbus instance
modbusMaster modbus;

// ---------------------------------------------------------------------------
// Main setup function
// ---------------------------------------------------------------------------
void setup()
{
    pinMode(DEREPin, OUTPUT);
    
    // Turn on the "main" serial port for debugging via USB Serial Monitor
    Serial.begin(9600);

    // Turn on your modbus serial port
    Serial1.begin(modbusBaudRate, SERIAL_8N1);

    // Turn on debugging, if desired
    // modbus.setDebugStream(&Serial);

    // Start the modbus instance
    modbus.begin(modbusAddress, modbusSerial, DEREPin);

}

// ---------------------------------------------------------------------------
// Main setup function
// ---------------------------------------------------------------------------
void loop()
{
    // Get data values from read-only input registers (0x04)
    uint16_t analog01 = 0;

    analog01 = modbus.int16FromRegister(0x04, 0x04, bigEndian);

    // Print results
    Serial.print("Analog_Input_01: ");
    Serial.println(analog01);

    delay(3000);
}
ModbusMQTT01Arduino
// ---------------------------------------------------------------------------
// Include the base required libraries
// ---------------------------------------------------------------------------
#include <ArduinoMqttClient.h>
#include <MKRGSM.h>
#include <Arduino.h>
#include <SensorModbusMaster.h>
#include "arduino_secrets.h" 

// ---------------------------------------------------------------------------
// Set up the MT-102 Modbus slave information
// ---------------------------------------------------------------------------

// Define the RTU's modbus address
byte modbusAddress = 0x01;    // The RTU's modbus address, or SlaveID
long modbusBaudRate = 9600;   // The baud rate the sensor uses

// Define pin number variables
const int DEREPin = 7;       // The pin controlling Recieve Enable and Driver Enable

// Serial1 is the serial port used on the MKR 1400
HardwareSerial* modbusSerial = &Serial1;

// Construct the modbus instance
modbusMaster modbus;

// ---------------------------------------------------------------------------
// Set up the information for 2G/3G data connection
// ---------------------------------------------------------------------------

// Please enter your sensitive data in the Secret tab or arduino_secrets.h
// PIN Number
const char PINNUMBER[]     = SECRET_PINNUMBER;
// APN data
const char GPRS_APN[]      = SECRET_GPRS_APN;
const char GPRS_LOGIN[]    = SECRET_GPRS_LOGIN;
const char GPRS_PASSWORD[] = SECRET_GPRS_PASSWORD;

// To connect with SSL/TLS:
// 1) Change GSMClient to GSMSSLClient.
// 2) Change port value from 1883 to 8883.
// 3) Change broker value to a server with a known SSL/TLS root certificate 
//    flashed in the WiFi module.

GPRS gprs;
GSM gsmAccess;
GSMClient gsmClient;
MqttClient mqttClient(gsmClient);

const char broker[] = "mqtt.eclipse.org";
int        port     = 1883;
const char topic[]  = "eackbarali/to";
const char publishTopic[]  = "eackbarali/analog";

// ---------------------------------------------------------------------------
// Main setup function
// ---------------------------------------------------------------------------

void setup() {
  
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // attempt to connect to GSM and GPRS:
  Serial.print("Attempting to connect to GSM and GPRS");
   // connection state
  bool connected = false;

  // After starting the modem with GSM.begin()
  // attach the shield to the GPRS network with the APN, login and password
  while (!connected) {
    if ((gsmAccess.begin(PINNUMBER) == GSM_READY) &&
        (gprs.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY)) {
      connected = true;
    } else {
      Serial.println("Not connected");
      delay(1000);
    }
  }

  Serial.println("You're connected to the network");
  Serial.println();

  Serial.print("Attempting to connect to the MQTT broker: ");
  Serial.println(broker);

  if (!mqttClient.connect(broker, port)) {
    Serial.print("MQTT connection failed! Error code = ");
    Serial.println(mqttClient.connectError());

    while (1);
  }

  Serial.println("You're connected to the MQTT broker!");
  Serial.println();

  pinMode(DEREPin, OUTPUT);

  // Turn on your modbus serial port
  Serial1.begin(modbusBaudRate, SERIAL_8N1);

  // Start the modbus instance
  modbus.begin(modbusAddress, modbusSerial, DEREPin);

}

void loop() {

  uint16_t analog01 = 0;
  
  // call poll() regularly to allow the library to receive MQTT messages and
  // send MQTT keep alives which avoids being disconnected by the broker
  mqttClient.poll();

  // read the first Analog pin
  //int sensorVal = analogRead(0);
  analog01 = modbus.int16FromRegister(0x04, 0x04, bigEndian);
  
  // Publish our sensor value
  mqttClient.beginMessage(publishTopic);
  mqttClient.print(analog01);
  mqttClient.endMessage();

  // Print results
  Serial.print("Analog_Input_01: ");
  Serial.println(analog01);

  // Set the rate at which we send our values
  delay(5000);
  
}
MQTT to Modbus TCP .NET Forms Application CodeVB.NET
Reads a single topic from a cloud MQTT broker and exposes the value via a Modbus TCP Server
Public Class Form1

    Dim client As MqttClient
    Dim Msg As String
    Dim modbusServer = New EasyModbus.ModbusServer

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If (TextBox1.Text.Length <> 0) Then


            Try
                client = New MqttClient(TextBox1.Text)

                Dim clientId As String = Guid.NewGuid().ToString()

                AddHandler client.MqttMsgPublishReceived, AddressOf Client_MqttMsgPublishReceived
                AddHandler client.ConnectionClosed, AddressOf Client_Disconnect

                client.Connect(clientId)

                If client.IsConnected Then
                    ComboBox1.SelectedIndex = 0
                    'ComboBox2.SelectedIndex = 0

                    ToolStripStatusLabel1.Text = "Connected to " + "'" + TextBox1.Text + "'"
                Else
                    ToolStripStatusLabel1.Text = "Disconnected"
                End If

            Catch ex As Exception

                ToolStripStatusLabel1.Text = "Error"
                MsgBox(ex.Message(), MsgBoxStyle.Critical)
            End Try
        Else
            ToolStripStatusLabel1.Text = "Please enter a valid Broker address "

        End If
    End Sub

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
        If (client IsNot Nothing AndAlso client.IsConnected()) Then
            client.Disconnect()
        Else
            ToolStripStatusLabel1.Text = "Error"
        End If
    End Sub

    Private Sub Client_Disconnect(sender As Object, e As EventArgs)
        ToolStripStatusLabel1.Text = "Connection Lost"
    End Sub

    Private Sub Client_MqttMsgPublishReceived(ByVal sender As Object, ByVal e As MqttMsgPublishEventArgs)

        Msg = Encoding.Default.GetString(e.Message)
        SetText(Msg.ToString)

    End Sub

    Delegate Sub SetTextCallback(newString As String)
    Private Sub SetText(ByVal newString As String)
        ' Calling from another thread? -> Use delegate
        If Me.TextBox2.InvokeRequired Then
            Dim d As New SetTextCallback(AddressOf SetText)
            ' Execute delegate in the UI thread, pass args as an array
            Me.Invoke(d, New Object() {newString})
        Else ' Same thread, assign string to the textbox
            'Me.RichTextBox2.Text = newString
            Me.TextBox2.Text = newString
            modbusServer.InputRegisters(1) = CInt(newString)
        End If
    End Sub

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        If (client IsNot Nothing AndAlso client.IsConnected()) Then
            If (TextBox3.Text.Length <> 0) Then

                Try
                    Dim Topic() As String = {TextBox3.Text}
                    Dim Qos() As Byte = {ComboBox1.SelectedIndex}
                    client.Subscribe(Topic, Qos)
                    ToolStripStatusLabel1.Text = "Subscribe to " + "{" + TextBox3.Text + "}"

                Catch ex As Exception
                    ToolStripStatusLabel1.Text = "Error"
                    MsgBox(ex.Message, MsgBoxStyle.Critical)

                End Try


            Else
                ToolStripStatusLabel1.Text = "Please enter a valid topic "
            End If


        Else
            ToolStripStatusLabel1.Text = "Disconnected !! subscription procedure is not valid"
        End If


    End Sub

    Public Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load

        modbusServer.Listen()

    End Sub

End Class

Comments

Similar projects you might like

Securely Connecting a MKR GSM 1400 to Google Cloud IoT Core

Project tutorial by Arduino_Genuino

  • 20,140 views
  • 25 comments
  • 41 respects

Arduino MKR GSM 1400: Cellular IoT Data Visualisation

Project tutorial by Alexis Susset

  • 9,500 views
  • 2 comments
  • 17 respects

IoT Pressure Sensor: MKR GSM + Arduino Cloud + Google Sheets

Project tutorial by wahltharvey

  • 16,976 views
  • 9 comments
  • 48 respects

Arduino MKR GSM 1400 and DTMF

by Arduino_Genuino

  • 23,705 views
  • 2 comments
  • 35 respects
Add projectSign up / Login