Arduino Uno and Ignition SCADA over Ethernet

Hello! I have a task I would like to accomplish with Arduino Uno and the Ignition SCADA platform I was wondering if you would be able to please provide some guidance.

---Task: Send one bit of data from the Ignition Designer to an Arduino Uno board over an Ethernet shield, and have Arduino be able to receive and respond to this data. (The data can be either serial data or a "high" input; it doesn't matter.)

I have verified that the Arduino board was successfully connected to Ignition with its IP address using this code: https://github.com/andresarmento/modbus-arduino/tree/master/libraries/ModbusIP/examples/Switch

However, I have been unsuccessful in creating a holding register or coil within the Arduino's code that allows it to understand inputs from Ignition. Are you able to please provide guidance in this area?

Below are my specs and devices used.

-Arduino Board: Arduino Uno R3
---Connection: W5100 Ethernet Shield
-Software: Ignition Gateway version 8.1
---local host (http://localhost:8088/web/home?12)

Any help you can provide would be greatly appreciated.

What would that look like? What is it?

Why not post the code, according to the advice here: How to get the best out of this forum - Using Arduino / Installation & Troubleshooting - Arduino Forum

Here is the code that worked for me:

/*
  Modbus-Arduino Example - Switch (Modbus IP)
  Copyright by André Sarmento Barbosa
  http://github.com/andresarmento/modbus-arduino
*/
 
#include <SPI.h>
#include <Ethernet.h>
#include <Modbus.h>
#include <ModbusIP.h>

//Modbus Registers Offsets (0-9999)
const int SWITCH_ISTS = 100; 
//Used Pins
const int switchPin = 3;

//ModbusIP object
ModbusIP mb;

void setup() {
    // The media access control (ethernet hardware) address for the shield
    byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  
    // The IP address for the shield
    byte ip[] = { 192, 168, 1, 120 };   
    //Config Modbus IP 
    mb.config(mac, ip);
    //Set ledPin mode
    pinMode(switchPin, INPUT);
    // Add SWITCH_ISTS register - Use addIsts() for digital inputs 
    mb.addIsts(SWITCH_ISTS);
}

void loop() {
   //Call once inside loop() - all magic here
   mb.task();
   
   //Attach switchPin to SWITCH_ISTS register     
   mb.Ists(SWITCH_ISTS, digitalRead(switchPin));
}

In terms of a "holding register" or "coil," these are the terms Ignition uses to determine what data bit to connect to, typically to a PLC (programmable logic controller). This is the part that I don't know how to address in Arduino. I believe there would be some code in the ethernet library that would allow the Arduino to read incoming signals and print to serial, but I have not yet found any examples that have worked for Ignition for me.

Attached is a screenshot of what data Ignition can see within the Arduino. I believe that if the correct code is put into the Arduino, a new bit of data will show up here that Ignition can write values to.

I won't post the full code as it's much too long, but hopefully these bits will help. This is communications between a mega 2560 and an AB 1769-L18ER.

Oops, just realized you're using Modbus. Might be similar though.

byte InBuffer[200];
byte OutBuffer[200];
  EIPlen = ethClient.available();

 // Read all data from the buffer to keep it from overflowing
  while (EIPlen >= 200) {
    if (EIPlen > 200) {
      Serial.print(F("EIPlen: "));
      Serial.println(EIPlen);
    }
    ethClient.read(InBuffer, 200);
    EIPlen = ethClient.available();
  }
  if (ethClient.connected()) {
    Connection_LED_Green();
    PLCHeartBeat();

    if (InBuffer[0] == 1) {
      digitalWrite(LED_BUILTIN, HIGH);
    } else {
      digitalWrite(LED_BUILTIN, LOW);
    }

    if (InBuffer[38] == 0) {
      LINBaud = 19200;
    } else {
      LINBaud = InBuffer[38];
    }
    if (bitRead(InBuffer[45], 0) == 0) SRstate = 0; // Prevent the sequential read from running over
    if ((bitRead(InBuffer[45], 0) == 1) && (SRstate == 1)) { // If the finished bit doesn't clear
      bitSet(OutBuffer[45], 1);
    } else {
      bitClear(OutBuffer[45], 1);
    }
  ethClient.flush(); // Wait for any writes to complete
  ethClient.write(OutBuffer, 200); // Send your bytes to the server

You use therminologe used in rather special fields, not in the Arduino sfere.
Now: What is "ignition"?

HMI and SCADA interface for production environments typically.

Human Machine Interface
Supervisory control and data acquisition

Thank you!

Ignition is a web-based platform designed for connecting all industrial devices within a process to a single network, regardless of the devices' brand or type. Here is the official website: Powerful Control & Data-Visualization Software | Ignition SCADA

Thank you very much! I will test this later today.

I think SWITCH_ISTS from line 38 might be your outgoing values. Arduino to Ignition.

MBAP seems to be your incoming values. Ignition to Arduino.

I'm too hung over to make any more sense of it at the moment. Sorry.

Okay, thank you. Comparing this back and forth with Ignition's training videos, I believe the piece I am missing is how to allow the Arduino to recognize holding registers. I'll keep looking, but if you happen to know anything about this, I would really appreciate any advice you can give.

I did some testing with Modbus trying to talk to an AB PLC, but Rockwell has their own special flavor of Modbus and I never got it working. At this point I'd try taking a look at other Modbus libraries that aren't as convoluted.

I understand, thanks for testing. Unfortunately, this library, although it is confusing and not explained, it's the only library that has worked for me to get Ignition to recognize my Arduino and say "Connected." I will keep looking for more libraries, but I have tried many to no avail.

Well, here is what I was able to do dig up trying to make a mega 2560 work. I don't even remember which code worked so I'm going to throw a bunch of chunks at you and hopefully some of it helps. This is copy pasted from four different code revisions so sorry. I'm also attaching the library I believe I used for it.

ArduinoModbus-master.zip (76.6 KB)

#include <SPI.h>
#include <Ethernet.h>
#include <ArduinoRS485.h> // ArduinoEIP depends on the ArduinoRS485 library
#include <ArduinoModbus.h>
byte mac[] = {0xA8, 0x61, 0x0A, 0xAE, 0x96, 0x4C};
IPAddress ip(192, 168, 2, 8);

// EIP client connection information
int EIP_Client_Port = 10001;
int EIP_Server_Port = 81; // Specify the target port to connect to
EthernetClient ethClient;
//ModbusTCPClient modbusTCPClient(ethClient);
IPAddress server(192, 168, 2, 1); // IP Address of your EIP server
// EIP Server information
EthernetServer ethServer = EthernetServer(EIP_Server_Port);
ethServer.begin();

At one point I tried using the arduino as the master, that may have been where this function was intended.

  ethServer.accept();
  EthernetClient ServerClient = ethServer.available();
  if (ServerClient == true) {
    Serial.println(ServerClient.read());
  } // else Serial.println(ServerClient);

I think I had to add additional conditions to the .accept() function.

      if (!ethClient.connect(server, EIP_Client_Port)) ethClient.connect(server, EIP_Client_Port);
      byte test001 = 64;
      ethClient.write(test001);
      delay(100);
      if ( ethClient.available() ) {
        Serial.println("EIP data available");
        char IncEIPdata = ethClient.read();
        Serial.println(IncEIPdata);
      }

Write a numeric value. Read out incoming data over serial to verify if anything is being received.

    if (ethClient.connect(server, EIP_Port)) {
    Serial.println("EIP connected");
    Serial.print("Remote IP address: ");
    Serial.println(ethClient.remoteIP());
    } else {
      Serial.println("connection failed");
    }

Check that you're connected to the proper server over the correct port.

  EthernetClient ServerClient = ethServer.available();
  

  if (ServerClient == true) {
    Serial.println(ServerClient.read());
  } // else Serial.println(ServerClient);
  
  
  if (ethClient.connected()) {    
  EIPcomms();
  } else {
    // Serial.println("Attempting to reconnect");
    ethClient.connect(server, EIP_Port);
  if (!modbusTCPClient.connected()) {
    // client not connected, start the EIP TCP client
    Serial.println("Attempting to connect to EIP TCP server");

    if (!modbusTCPClient.begin(server, 502)) {
      Serial.println("EIP TCP Client failed to connect!");
    } else {
      Serial.println("EIP TCP Client connected");
    }
  }
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      // Serial.println("Blink");
      Serial.println(HeartBeat);
        if (HeartBeat == 0) {
          HeartBeat = 1;
            if (!modbusTCPClient.coilWrite(0x00, 0x01)) {
              Serial.print("Failed to write coil high! ");
              Serial.println(modbusTCPClient.lastError());
              LinkStatusError = 1;
              } else {
              LinkStatusError = 0;
                }
                } else {
          HeartBeat = 0;
            if (!modbusTCPClient.coilWrite(0x00, 0x00)) {
              Serial.print("Failed to write coil low! ");
              Serial.println(modbusTCPClient.lastError());
              LinkStatusError = 1;
          } else {
            LinkStatusError = 0;
          }
      }
    }

Pulse heartbeat using a coilWrite (setting a bit).

Thank you very much for the detail! I will try this out.

After much trial and error, I was able to complete the task! The Ignition Gateway can now energize or de-energize one of the Arduino's outputs at will. Thank you for your help!

/*
  Modbus-Arduino Example - Lamp (Modbus IP)
  Copyright by André Sarmento Barbosa
  http://github.com/andresarmento/modbus-arduino 

  Revisions by Elias on 3/25/2023
*/

// SCADA - Using the Ignition software, users can send data to Arduino over the W5100 Ethernet Shield
#include <SPI.h>
#include <Ethernet.h>
#include <Modbus.h>
#include <ModbusIP.h>
// Modbus Registers Offsets can range from 0-9999.  In this case, we have selected Address 100.
const int LAMP1_COIL = 100; // In the Modbus software, send data to Address 100 to activate the Lamp Coil.  The equivalent Modbus address, if needed, is 101.
// The Modbus library requires data be sent directly to an Arduino's output.
// To read this as data, SCADA inputs can be outputted to a pin, and then Arduino can read that pin with a separate variable.
int ModbusPin = 13; // This is the Arduino pin that outputs SCADA's values.
int valModbus; // This spot in Arduino's memory reads SCADA's values.
ModbusIP mb; // Declare the ModbusIP object.

void setup() {
  byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // The media access control (MAC address) ethernet hardware address for the Arduino.
  byte ip[] = {192, 168, 0, 72}; // This is the IP Address for the Arduino; it can be changed by the user.  This IP Address was used in Millersville University Osburn Hall Automation Lab Station 5.  
  mb.config(mac, ip); // Configure the Modbus IP.
  pinMode(ModbusPin, OUTPUT); // Set the spot in memory for SCADA's inputs.
  mb.addCoil(LAMP1_COIL); // Add LAMP1_COIL register - Use addCoil() for digital outputs
  Serial.begin(9600); // Begin the Serial Monitor
}

void loop() {
mb.task(); // Enable Modbus Library - "all magic here"
   digitalWrite(ModbusPin, mb.Coil(LAMP1_COIL)); // Send Modbus data to the ModbusPin.
   valModbus = digitalRead(ModbusPin); // Save the ModbusPin's value as data.
   Serial.print("Modbus = ");
   Serial.println(valModbus); // Print the Modbus's value to the Arduino's serial monitor.
   delay(100); // Delay 0.1 seconds.
}

Excellent!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.