PLC S7 WinAC RTX 4- (Arduino)

Comunicando ARDUINO a través de la red LAN con el PLC WinAC RTX

En esta entrada, muestro paso a paso como establecer una comunicación de datos entre una placa Arduino Uno y el PLC.

Esta es la cuarta entrada relacionada con el mismo tema, pero es posiblemente la más importante ya que es la que permitirá poder tener entradas y salidas físicas basadas en Hardware libre.

Manos a la obra….

Objetivo:        

-Disponer de un hardware Arduino UNO comandado desde el PLC.

Recursos necesarios:

-PC-PLC ya instalado. Ver entrada PLC S7 WinAC RTX 1- (Configuración) y PLC S7 WinAC RTX 2- (Conexión LAN)

-Una placa Arduino UNO + el escudo oficial de Ethernet o placa Wemos ESP8266.

-Tener el entorno de programación de Arduino instalado.

-Disponer de la librería Settimino de (Davide Nardella).

Finalidad:

-Poder fabricar nuestras propias tarjetas de entradas y salidas, tanto digitales como analógicas.

INTRODUCCION

Para poder hacer realidad este proyecto, utilizamos una librería libre (Settimino), la cual incorpora el protocolo de comunicación de Siemens a nuestro proyecto Arduino.

Recomiendo visitar la página del autor Davide Nardella (http://settimino.sourceforge.net/), el cual tuve la oportunidad de conocer hace años en un proyecto que desarrolló para la empresa donde trabajo. Aquí se puede descargar la librería y un completo y detallado manual de uso.

Nuestro esquema es:

Como se puede ver podemos comunicar por cable o bien por Wifi.

Después de varias pruebas, llego a la conclusión que el wifi solo es factible en casos que podamos permitir perdida de información.

Las comunicaciones por cable no dan ningún tipo de problemas, siendo fiables al 100%.

Ejecutamos el entorno de programación Arduino V1.8.7.  

Una vez dentro del entorno seleccionamos uno de los ejemplos de la librería Settimino, y modificamos a nuestro gusto:

/*-------------------------------------------------------------------
Data Read Demo

Probado por Cable Ethernet en placa (Arduino Uno) mas Shield Wiznet
 Funciona perfecto sin ningún fallo de comunicación.
 Compilado con Arduino IDE 1.8.7
 JColl Sep.2018

 Created 12 Dec 2016
 by Davide Nardella
---------------------------------------------------------------------

This demo shows how to read data from the PLC.
A DB with at least 1024 byte into the PLC is needed.
Specify its number into DBNum variable


- Both small and large data transfer are performed (see DO_IT_SMALL)
- During the loop, try to disconnect the ethernet cable.
  The system will report the error and will reconnect automatically
  when you re-plug the cable.
- For safety, this demo *doesn't write* data into the PLC, try
  yourself to change ReadArea with WriteArea.
- This demo uses ConnectTo() with Rack=0 and Slot=2 (S7300) 
    Client.ConnectTo(<IP>, <Rack>, <Slot>);
    with the couple
    Client.SetConnectionParams(<IP>, <LocalTSAP>, <Remote TSAP>);
    Client.Connect();

NodeMCU 1.0 ESP-12E ESP8266 supported
-------------------------------------------------------------------*/

 
// Restarts program from beginning but
// does not reset the peripherals and registers

void software_Reset(){   asm volatile ("  jmp 0");  }
void(* resetFunc) (void) = 0; //declare reset function at address 0

// Wifi  -> #define S7WIFI
// Cable -> #define S7WIRED
#define S7WIRED  //Comunicación cableada

#include <SPI.h>
#include <Ethernet.h>
#ifdef S7WIFI
  #include <ESP8266WiFi.h>
#endif

#include "Settimino.h"

// Uncomment next line to perform small and fast data access
#define DO_IT_SMALL

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0x90, 0xA2, 0xDA, 0x0F, 0x08, 0xE1 };

IPAddress Local(192,168,1,45); // Local Address
IPAddress PLC(192,168,1,51);   // PLC Address

// Following constants are needed if you are connecting via WIFI
// The ssid is the name of my WIFI network 
char ssid[] = "MIWIFI_2G_x";    // Your network SSID (name)
char pass[] = "xxxxxxxxxx";  // Your network password (if any)
IPAddress Gateway(192,168,1,1);
IPAddress Subnet(255,255,255,0);
int    intentos =5; //Intentos de comunicar antes de hacer reset.
int DBNum = 1; // This DB must be present in your PLC
byte Buffer[8];
 
#ifdef S7WIFI
// S7Client will create a WiFiClient as TCP Client
S7Client Client(_S7WIFI);
#else
// S7Client will create an EthernetClient as TCP Client
S7Client Client(_S7WIRED);
#endif

unsigned long Elapsed; // To calc the execution time
//-------------------------------------------------------------------
// Setup : Init Ethernet and Serial port
//-------------------------------------------------------------------
void setup() {
//Open serial communications and wait for port to open:
    Serial.begin(115200);
     while (!Serial) {
      ; // wait for serial port to connect. Needed for Leonardo only
    }

#ifdef S7WIFI
//----------------------------------- ESP8266 Initialization   
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);
    WiFi.begin(ssid, pass);
    WiFi.config(Local, Gateway, Subnet);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        Serial.print(".");
    }
    Serial.println("");
    Serial.println("WiFi connected"); 
    Serial.print("Local IP address : ");
    Serial.println(WiFi.localIP());
#else

//-------------------------Wired Ethernet Shield Initialization   
    // Start the Ethernet Library
    Ethernet.begin(mac, Local);
    // Setup Time, someone said me to leave 2000 because some
    // rubbish compatible boards are a bit deaf.
    delay(2000);
    Serial.println("");
    Serial.println("Cable connected"); 
    Serial.print("Local IP address : ");
    Serial.println(Ethernet.localIP());
#endif  
}
//----------------------------------------------------------------
// Connects to the PLC
//----------------------------------------------------------------
bool Connect()
{
    int Result=Client.ConnectTo(PLC,
                                  0,  // Rack (see the doc.)
                                  2); // Slot (see the doc.)
    Serial.print("Connecting to ");Serial.println(PLC); 
    if (Result==0)
    {
      Serial.print("Connected ! PDU Length = ");Serial.println(Client.GetPDULength());
    }
    else
      Serial.println("Connection error");
    return Result==0;
}
//------------------------------------------------------------------
// Dumps a buffer (a very rough routine)
//-----------------------------------------------------------------
void Dump(void *Buffer, int Length)
{
  int i, cnt=0;
  pbyte buf;
  if (Buffer!=NULL)
    buf = pbyte(Buffer);
  else 
    buf = pbyte(&PDU.DATA[0]);
  Serial.print("[ Dumping ");Serial.print(Length);
  Serial.println(" bytes ]===========================");
  for (i=0; i<Length; i++)
  {
    cnt++;
    if (buf[i]<0x10)
      Serial.print("0");
    Serial.print(buf[i], HEX);
    Serial.print(" ");
    if (cnt==16)
    {
      cnt=0;
      Serial.println();
    }
  } 
  Serial.println("===============================================");
}
//-----------------------------------------------------------------
// Prints the Error number
//-----------------------------------------------------------------
void CheckError(int ErrNo)
{
  Serial.print("Error No. 0x");
  Serial.println(ErrNo, HEX);
 
  // Checks if it's a Severe Error => we need to disconnect
  if (ErrNo & 0x00FF)
  {
    Serial.println("SEVERE ERROR, disconnecting.");
    Client.Disconnect();
  }
}
//------------------------------------------------------------------
// Profiling routines
//------------------------------------------------------------------
void MarkTime()
{
  Elapsed=millis();
}
//------------------------------------------------------------------
void ShowTime()
{
  // Calcs the time
  Elapsed=millis()-Elapsed;
  Serial.print("Job time (ms) : ");
  Serial.println(Elapsed);  
}
//------------------------------------------------------------------
// Main Loop
//------------------------------------------------------------------
void loop()
{
  int Size, Result;
  void *Target;
  
#ifdef DO_IT_SMALL
  Size=64;
  Target = NULL; // Uses the internal Buffer (PDU.DATA[])
#else
  Size=1024;
  Target = &Buffer; // Uses a larger buffer
#endif
 
  // Connection
  while (!Client.Connected)
  {
     if (!Connect()){
      delay(150);
      if (intentos<1) resetFunc(); //software_Reset();
      intentos--; //Intentos de comunicar antes de hacer reset.
    }
  }
 
  Serial.print("Reading ");Serial.print(Size);
Serial.print(" bytes from DB");Serial.println(DBNum);
  // Get the current tick
  MarkTime();
  Result=Client.ReadArea(S7AreaDB, // We are requesting DB access
                         DBNum,    // DB Number
                         0,        // Start from byte N.0
                         Size,     // We need "Size" bytes
                         Target);  // Put them into our target 
  if (Result==0)
  {
    ShowTime();
    Dump(Target, Size);
  }
  else
    CheckError(Result);
  delay(50); //Pruebas de tiempo cada 50ms. por cable va perfecto.
}

 

Como se puede observar introducimos los datos de nuestra red:

Trabajaremos con Arduino UNO y cable ethernet.

Para trabajar por Wifi, simplemente substituir S7WIRED por S7WIFI.

Introducimos la IP local y la del PLC WinAc RTX

Configuramos los parámetros del wifi (Solo necesario si trabajamos con Wifi)

Seleccionamos bastidor 0 Slot 2

Seleccionamos el tipo de placa que estamos utilizando.

Conectamos la placa al PC por cable USB y pulsamos el icono Compilar y transferir.

La transferencia se inicia inmediatamente después de la compilación.

Una vez cargado podemos visualizar los datos leídos del PLC desde el Monitor Serie:

Este ejemplo, lee el contenido del DB1 del PLC, pero el área a leer o escribir se puede configurar según nuestra necesidad.

Ya tenemos una placa remota, accediendo a la información del PLC a través de la red LAN.

A partir de aquí podemos desarrollar nuestro hardware IO personalizado.

Fin del procedimiento….