Settimino : Ethernet library to communicate with S7 Siemens PLC.

Hi all,
I published on Sourceforge the project of a communication library for the S7 Siemens PLC family.

  • S7 200/300/400/1200/1500 and LOGO 0BA7 are managed.
  • Full PLC memory access (E/A/M/DB/T/Z)
  • Helper functions for data conversion (Big Endian --> Little endian)
  • PDU independence : large data transfer in a single call (limited only on the available memory)
  • Three memory models for footprint optimization.
  • Uses standard Arduino Ethernet library i.e. it can coexist with other clients/servers in the same sketch.
  • 3 ms to read one PDU (240 bytes) into the internal buffer.
  • Detailed documentation.

The project is derived from another my project - Snap7 (a 32/64 bit multi-platform library)
Comments/suggestions are warmly welcomed..

I hope you like it.. :wink:

Davide Nardella

(the son of http://snap7.sourceforge.net)

1 Like

Hi Davide. Felititations for this magnific job.

I'm try the example RunStopDemo, and I can' connect to my Logo! OBA7. The logo IP is 192.168.4.50,
and the ethernet shield+UNO is 192.168.4.11.
Could you say me if the modifications are correct?

Reagards,

Jaume Nogués
Vall Hebron Tech School
Barcelona

RunStopDemo.ino (3.67 KB)

Hello I have a Arduino Yun.
Unfortunately I can not connect to the sample project RunStopDemo.
Can you tell me how the project would look at RunStopDemo an Arduino Yun

Hey congrats on the job. I have a problem and communicate well read but not to do to a real number eacribir eg 60.50 as I have to turn to writing

Hello
read variables from the PLC works.
How can I write a Florat in the PLC?

This code does not work?

float f = 123.45;
    byte* bytes = (byte*)&f;
     
  
    
    
    Result=Client.WriteArea(S7AreaDB, // We are requesting DB access
                       307,        // DB Number = 1
                         0,        // Start from byte N.0
                         4,     // We need "Size" bytes
                         bytes);  // Put them into our target (Buffer or PDU)
/*----------------------------------------------------------------------
Data Read Demo

 Created 19 Nov 2013
 by Davide Nardella
 
------------------------------------------------------------------------

----------------------------------------------------------------------*/
#include <SPI.h>
#include <Ethernet.h>
#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, 0xE11 };
  
IPAddress Local(192,168,178,55); // Local Address
IPAddress PLC(192,168,178,30);   // PLC Address

byte MyBuffer[1024];
byte MyWrite[4];

S7Client Client;

//----------------------------------------------------------------------
// Setup : Init Ethernet and Serial port
//----------------------------------------------------------------------
void setup() {
    // Open serial communications and wait for port to open:
    Serial.begin(9600);
     while (!Serial) {
      ; // wait for serial port to connect. Needed for Leonardo only
    }
    // 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(Ethernet.localIP());
}
//----------------------------------------------------------------------
// 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;
}



 //----------------------------------------------------------------------
// Main Loop
//----------------------------------------------------------------------
void loop() 
{
  
  int Size, Result;
  float Pressure;
  unsigned long Encoder;
  int Component;
  byte Bytes;
 
   

  
  // Connection
  while (!Client.Connected)
  {
    if (!Connect())
      delay(500);
  }
  
  Serial.print("Reading "); 

  Result=Client.ReadArea(S7AreaDB, // We are requesting DB access
                         307,        // DB Number = 1
                         0,        // Start from byte N.0
                         11,     // We need "Size" bytes
                         &MyBuffer);  // Put them into our target (Buffer or PDU)
  if (Result==0)
  {
    Pressure = S7.FloatAt(&MyBuffer, 0);
    Serial.print("Pressure=");
    Serial.println(Pressure); 
    Encoder = S7.DWordAt(&MyBuffer, 4);
    Serial.print("Encoder=");
    Serial.println(Encoder);
    Component = S7.IntegerAt(&MyBuffer, 8);
    Serial.print("Component=");
    Serial.println(Component); 
    Bytes = S7.ByteAt(&MyBuffer, 10);
    Serial.print("Bytes=");
    Serial.println(Bytes);  
  }
  else
    CheckError(Result);
    
    float f = 123.45;
    byte* bytes = (byte*)&f;
     
  
    
    
    Result=Client.WriteArea(S7AreaDB, // We are requesting DB access
                       307,        // DB Number = 1
                         0,        // Start from byte N.0
                         4,     // We need "Size" bytes
                         bytes);  // Put them into our target (Buffer or PDU)
    
    
    
  delay(500);  
}

  



//----------------------------------------------------------------------
// 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(); 
  }
}

Hi,
Siemens PLC data are Big-Endian, Arduino data are Little-Endian, you need to convert the float to Big-Endian in order to transfer it into the CPU.
Basically, every var greater then a byte needs to be converted..

Regards
Davide Nardella

Hello
how can I convert the data? Can you write me an example?

I try to Wandlen Florat in bytes. Sorry, no figures were displayed in the PLC.

float f = 123.45;
    byte* bytes = (byte*)&f;
     
  
    
    
    Result=Client.WriteArea(S7AreaDB, // We are requesting DB access
                       307,        // DB Number = 1
                         0,        // Start from byte N.0
                         4,     // We need "Size" bytes
                         bytes);  // Put them into our target (Buffer or PDU)

Hi Emilio,
you can use these two functions:

//----------------------------------------------------------------------
// Reverse float/dword/dint
//----------------------------------------------------------------------
void Reverse4(void *ptr)
{
  byte *pb;
  byte tmp;
  pb=(byte*)(ptr);
  // Swap byte 4 with byte 1
  tmp=*(pb+3);
  *(pb+3)=*pb;
  *pb=tmp;
  // Swap byte 3 with byte 2
  tmp=*(pb+2);
  *(pb+2)=*(pb+1);
  *(pb+1)=tmp;
 }
//----------------------------------------------------------------------
// Reverse word/int
//----------------------------------------------------------------------
void Reverse2(void *ptr)
{
  byte *pb;
  byte tmp;
  pb=(byte*)(ptr);
  // Swap byte 2 with byte 1
  tmp=*(pb+1);
  *(pb+1)=*pb;
  *pb=tmp;
 }

Reverse4 exchanges (Big<->Little endian) the bytes inside a float/dword/dint (4 bytes variable)
Reverse2 exchanges (Big<->Little endian) the bytes inside a word/int (2 bytes variable)

This is the usage in your case:

float f = 123.45;
word w=0x1234;

Reverse4(&f);
Result=Client.WriteArea(S7AreaDB, 307, 0, sizeof(float), &f);
Reverse2(&w);
Result=Client.WriteArea(S7AreaDB, 307, 4, sizeof(word), &w);

Open a VAT (Variable table) into your project and display:
DB307.DBD 0 (change Display format as floating-point)
DB307.DBW 4 (change Display format as Hexadecimal)

It's only an example, I suggest you to fully "adjust" a buffer in order to transfer it rather then transferring single fields.

Regards
Davide

swapping 2 bytes for endianess can be done ~20% faster - if you are in a need for speed (or have large arrays)

void reverse2(uint16_t *n)
{
  *n = (*n >> 8 ) | (*n << 8);
}

void reverse2(int16_t *n)
{
  *n = ((*n >> 8 ) & 0xFF)| (*n << 8);
}

No optimization found for the 4 byte swap (yet :wink:

Hi,

although I get data in the PLC but not written correctly
what have i done wrong?:

I get displayed in the PLC positive and negative numbers, but they are not in connection with the variables in the Arduino.

/*----------------------------------------------------------------------
 DB Get Demo

 Created 19 Nov 2013
 by Davide Nardella
 
------------------------------------------------------------------------
This demo shows how to upload an entire DB from the PLC without
knowing its size in advance.
A DB1 with not more than 1024 byte into the PLC is needed.

- 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)
  - If you want to connect to S71200/S71500 change them to Rack=0, Slot=0.
  - If you want to connect to S7400 see your hardware configuration.
  - If you want to work with a LOGO 0BA7 or S7200 please refer to the
    documentation and change 
    Client.ConnectTo(<IP>, <Rack>, <Slot>);
    with the couple
    Client.SetConnectionParams(<IP>, <LocalTSAP>, <Remote TSAP>);
    Client.Connect();
----------------------------------------------------------------------*/
#include <SPI.h>
#include <Ethernet.h>
#include "Settimino.h"

// 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, 0xE11 };
  
IPAddress Local(192,168,0,70); // Local Address
IPAddress PLC(192,168,0,1);   // PLC Address

byte Buffer[1024];

byte MyWrite[2];


S7Client Client;
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(9600);
     while (!Serial) {
      ; // wait for serial port to connect. Needed for Leonardo only
    }
    // 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(Ethernet.localIP());
}
//----------------------------------------------------------------------
// 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() 
{
  
  uint16_t Size;
  int Result;
  Size = sizeof(Buffer);
  
  // Connection
  while (!Client.Connected)
  {
    if (!Connect())
      delay(500);
  }
  
  Serial.println("Uploading DB100"); 
  // Get the current tick
  MarkTime();
  Result=Client.DBGet(100,       // DB Number = 1
                      &Buffer, // Our Buffer
                      &Size);  // In input contains our buffer size
                               // As output contains the data read
                               
//////////////////////////////////////////////////////////////

 
int VAR1 = 32000;

      byte* bytes = (byte*)&VAR1;
      
      
    Result=Client.WriteArea(S7AreaDB, // We are requesting DB access
                       200,           // DB Number = 1
                         0,           // Start from byte N.0
                         2,           // We need "Size" bytes
                         bytes);      // Put them into our target (Buffer or PDU)
                        
                         

/////////////////////////////////////////////////////////////
  if (Result==0)
  {
    ShowTime();
    Dump(&Buffer, Size);
  }
  else
    CheckError(Result);
    
  delay(500);  
}

Thanks.

Hi,
As just said, Siemens PLC data are Big-Endian, Arduino data are Little-Endian.
You neet to swap the byte order of words and floats before writing them into the PLC.

Use reverse2 (for int/words) and reverse4 (for double word/large int/floats)

Hi
I need help. I'd like to read with my Arduino D18b20 a temperature sensor and the serial number and then Themperaturwert S7 in an example DB1 transferred.
How do I have the SPS parametiereren (via Ethernet) as seen from the program for the Arduino. I'm from the examples that I find not smart in the network. Can any one help me?
thank chine times and L.G. Mani

Hello davenardella,

I'm going to transfer a temperature from arduino to S7, too. It would be interesting how that works. The connection between S7 and Arduino already works. Its easy to send and read "byte"-data into/of a DB, but I despair in sending floats/words/ints. An example would be very nice :-).

Thanks in advance.

Hello chg1988
I work with automatic translator, therefore, apologize for bad English.
How did you set up the connection to the S7?
via Ethernet?
I have a 151-8 CPU I can also combine this with the Arduino? If so, how. An example would be good.
L.G. Mani

Hallo Manfred,
schau mal in deinen privaten Nachrichten!!:wink:

Hi,

Im trying to connect a Galileo Gen 2 Board with a S7 1200 or WinAC, but the ConnectTo function retuns error 3 errTCPDataRecvTout. The function RecvISOPacket waits for 4 byte but only 1 byte is arriving. Does anyone know whether this is a galileo related problem? Or is there a problems with my settings?

thanks for any help!

Oh! YES. Its great, wonderful. Many thank you for a job!
I tried to read data from my PLC in the company LAN to ARDUINO UNO, all is OK!

But from one CPU 314+Cp343 error 0x3!
And if I disconnect MPI cabel to the TP270 from this CPU, reading starting with not often errors.
In Simatic Manager all data monitorin good, HMI CC/CCFlex/other scada - dataexchahge no problemm.

In HWconfig - communication i check allowed connection(PG/OP/S7) all is correct.

I have not others idea. Can somebody halp me? Please!!!!

Hi, congratulations on the settimino software.

I'm testing the RunStopDemo example, but it seems that only detects the status of my S7 1200 PLC (CPU 1214C AC / DC / RLY, firmware v4.0), but not start or stop it.

It detects when it is running but can not pararala.
It detects when stopped, but can not implement it.

The IP address (arduino one) is: 192.168.1.99
The IP address (PLC) is: 192.168.1.100

Access Level: Full unprotected
PUT / GET: Allowed

Can anyone tell me what could happen.

Thank you.

Hello,

I am trying to connect Arduino Ethernet Shield to Pc with Snap7 server demo software act as PLC.I am not able to connect it,getting error like "connection error".
can u please help me?

Thank you.

Hello,

Very good work!
I try the lib on the ESP8266 to run.

But compile error on the error.

Has anyone ever adapted?

Need Help!!!

Norbert