Go Down

Topic: Settimino : Ethernet library to communicate with S7 Siemens PLC. (Read 24078 times) previous topic - next topic

davenardella

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.. ;)

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


jnogues

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

emilio20

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

Llaner

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

emilio20

#4
May 08, 2014, 05:29 pm Last Edit: May 08, 2014, 05:53 pm by emilio20 Reason: 1
Hello
read variables from the PLC works.
How can I write a Florat in the PLC?

This code does not work?

Code: [Select]

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)



Code: [Select]

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






davenardella

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

emilio20

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.

Code: [Select]

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)



davenardella

Hi Emilio,
you can use these two functions:

Code: [Select]

//----------------------------------------------------------------------
// 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:

Code: [Select]

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


robtillaart


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

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 ;)
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

andrit

#9
Oct 17, 2014, 04:48 pm Last Edit: Oct 18, 2014, 09:09 am by andrit Reason: 1
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.


Code: [Select]

/*----------------------------------------------------------------------
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.

davenardella

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)

manfred-65

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

chg1988

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.

manfred-65

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

chg1988

Hallo Manfred, 
schau mal in deinen privaten Nachrichten!!;)

Go Up