Profinet ve pic/atmega/stm haberleşmesi

Başlatan gokhangokcen, 06 Temmuz 2021, 13:46:44

gokhangokcen

Merhabalar, şu aralar Siemens plc ile x bir işlemci ile haberleşme yapmaya çalışıyorum. Elimde CPU1510 serisi plc var. Denemelerimi yaptım. Elimin altında hazır kurulu donanım olmadığı için ilk etapta arduino ve ethernet shieldi ile denemeler yapıyorum. Herkesin bildiği gibi internetteki hazır kod olan http://settimino.sourceforge.net/ sitesindeki kodları yükledim. Fakat buradaki sorunum şu;
Kodlar ya PLC'deki DB bloklarına yazıyor ya da DB bloklarından okuyor. PLC ile arduino üzerindeki verileri çekemiyorum. Arduino kartını slave gibi kullanamıyorum. Bu konuda fikri olan ya da izlenmesi gereken bir yol var mıdır? Kodlarım aşağıdaki gibi;

main;
#include <SPI.h>
#include <Ethernet.h>
#include "Settimino.h"

//#define DO_IT_SMALL

byte mac[] = {0x250x630x360x8E0x760xE3};

IPAddress Local(1010169);
IPAddress PLC(1010170);
IPAddress Gateway(101011);
IPAddress Subnet(2552552550);

int DBNum 1;
int Buffer[1024];
int16_t Data[1024];
int abc;

typedef union
{
  
uint8_t l2int[2];
  
uint16_t veri;
}
longToInt;

S7Client Client;



unsigned long Elapsed;


void setup() {
#define sdcs          4
#define ethcs         10

  
pinMode(ethcsOUTPUT);                                                                     //WIZ5100 cs pini çıkış olarak atanır
  
pinMode(sdcsOUTPUT);                                                                      //SD kart cs pini çıkış olarak atanır

  
digitalWrite(sdcsHIGH);                                                                   //SD kart pini pasif yapılır
  
digitalWrite(ethcsLOW);
  
// put your setup code here, to run once:
  
Serial.begin(9600);

  while (!
Serial) {

  }

  
Ethernet.begin(macLocal);

  
delay(2000);
  
Serial.println("");
  
Serial.println("Cable connected");
  
Serial.println("Local IP address : ");
  
Serial.println(Ethernet.localIP());

}


bool Connect()
{
  
int Result Client.ConnectTo(PLC,
                                
0,  //Rack
                                
1); //Slot

  
Serial.print("Connecting to ");
  
Serial.println(PLC);
  if (
Result == 0)
  {
    
Serial.print("Connection ! PDU Length = ");
    
Serial.println(Client.GetPDULength());
  }
  else
    
Serial.println("Connection error");
  return 
Result;
}

void CheckError(int ErrNo)
{
  
Serial.print("Error No. 0x");
  
Serial.println(ErrNoHEX);

  if (
ErrNo 0x00FF)
  {
    
Serial.println("Server Error, disconnectiong.");
    
Client.Disconnect();
  }
}


void MarkTime() {
  
Elapsed millis();
}


void ShowTime() {
  
Elapsed millis() - Elapsed;
  
Serial.print("Job time (ms) :");
  
Serial.println(Elapsed);
}



void loop() {
  
// put your main code here, to run repeatedly:

  
int SizeResultStatus;

  if (
Serial.available() > 0) {
    
String okunan Serial.readString();
    
abc okunan.toInt();
  }

  while (!
Client.Connected) {
    if (!
Connect())delay(500);
  }
  
//**************************
  /* Result = Client.GetPlcStatus(&Status);
    if (Result==0)
    {
    if (Status==S7CpuStatusRun)
    {
     Serial.println("Running THE CPU");
     //Client.PlcStop();
    }
    else
    {
     Serial.println("Stopping THE CPU");
     //Client.PlcStart();
    }
    }
    else
    CheckError(Result);
    //*************************/
  
MarkTime();

  
//SetDIntAt(void *Buffer, int index, dint value)

  
S7.SetIntAt(&Buffer0abc);
  
abc++;

  
Serial.print("PDU Data0");
  
Serial.println(PDU.DATA[0],DEC);



 
// Buffer[0] = abc;

  //Client.WriteArea(S7AreaDB, 1, 0, 2, &Buffer);
   //Client.WriteArea(S7AreaDB,1,0,9,&Buffer);
  
Serial.print("Buffer[0]: ");
  
Serial.println(Buffer[0] , DEC);
  
delay(500);
  
  
//
//  ReadArea(int Area, uint16_t DBNumber, uint16_t Start, uint16_t Amount, void *ptrData);
   /*Client.ReadArea(S7AreaDB,1,0,4,&Data);
   Serial.print("Data0: ");
   Serial.println(Data[0] ,DEC);
   Serial.print("Data1: ");
   Serial.println(Data[1], DEC);*/

}

platform.h
/*=============================================================================|
|  PROJECT SETTIMINO                                                     2.0.0 |
|==============================================================================|
|  Copyright (C) 2013, 2019 Davide Nardella                                    |
|  All rights reserved.                                                        |
|==============================================================================|
|  SETTIMINO is free software: you can redistribute it and/or modify           |
|  it under the terms of the Lesser GNU General Public License as published by |
|  the Free Software Foundation, either version 3 of the License, or           |
|  (at your option) any later version.                                         |
|                                                                              |
|  It means that you can distribute your commercial software linked with       |
|  SETTIMINO without the requirement to distribute the source code of your     |
|  application and without the requirement that your application be itself     |
|  distributed under LGPL.                                                     |
|                                                                              |
|  SETTIMINO is distributed in the hope that it will be useful,                |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of              |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
|  Lesser GNU General Public License for more details.                         |
|                                                                              |
|  You should have received a copy of the GNU General Public License and a     |
|  copy of Lesser GNU General Public License along with Snap7.                 |
|  If not, see  http://www.gnu.org/licenses/                                   |
|------------------------------------------------------------------------------|
|                                                                              |
|  1.1.0 Added support for ESP8266 (Thanks to Geoffrey Hayward Piggot)         |
|  2.0.0 Added new hardware support                                            |
|        Added Read/Write consistent bit into the CPU                          |
|        Added new 18 helper functions                                         |
|        Small bugfixes (Thanks to Daniel Förstmann and Schöneberg Swen)       |
|                                                                              |
|=============================================================================*/
#ifndef PLATFORM_H
#define PLATFORM_H

//*********************************************************************
//  PLATFORM AND MEDIA DEFINITION
//
//  Chose your platform and uncomment the related define
//  *ONLY* one line must be uncommented
//*********************************************************************
//  ARDUINO_LAN
//      Arduino UNO R3 + Ethernet Shield R3 or Ethernet Shield 2
//      Arduino MEGA 2560 R3 + Ethernet Shield R3 or Ethernet Shield 2
//  ESP8266_FAMILY (WiFi)
//      NodeMCU
//      Wemos
//      Lolin V3
//      (Other 7 modules based onto ESP8266 chip)
//  ESP32_WIFI
//      ESP32-WROOM-32 / 32D
//      ESP32 DevModule
//  M5STACK_WIFI (only core)
//      Basic, Gray, Red
//  M5STACK_LAN
//      Core + LAN MODULE (W5500)
//
//*********************************************************************
//  Notes of external libraries needed.
//  If you already use that boards, you should have already installed
//*********************************************************************
//  ESP8266_FAMILY
//  
	
https://github.com/esp8266/Arduino
//  ESP32
//  
	
https://github.com/espressif/arduino-esp32
//  M5STACK (*)
//      https://docs.m5stack.com/#/en/quick_start/m5core/m5stack_core_get_started_Arduino_Windows
//  
	
https://github.com/m5stack/M5Stack
//
//  (*) Need also to install Ethernet2 library to use the LAN MODULE.
//      Use the Arduino IDE Library Manager. 
//
//*********************************************************************

#define ARDUINO_LAN    
//#define ESP8266_FAMILY  
//#define ESP32_WIFI
//#define M5STACK_WIFI
//#define M5STACK_LAN

#include <SPI.h>

// Platforms 

#ifdef ARDUINO_LAN
  #include <Ethernet.h>
  #define S7WIRED
#endif

#ifdef ESP8266_FAMILY
  #include <ESP8266WiFi.h> 
  #include <Ethernet.h>
  #define S7WIFI
#endif

#ifdef ESP32_WIFI
  #include <Ethernet.h>
  #include <WiFi.h>
  #define S7WIFI
#endif

#ifdef M5STACK_WIFI
  #include <M5Stack.h>
  #include <Ethernet.h>
  #include <WiFi.h>
  #define S7WIFI
#endif

#ifdef M5STACK_LAN
  #include <M5Stack.h>
  #include <Ethernet2.h>
  #define S7WIRED
#endif

#ifdef S7WIRED
  #include "EthernetClient.h"  
#else
  #include "WiFiClient.h"
#endif

#endif //PLATFORM_H

settimino.cpp
/*=============================================================================|
|  PROJECT SETTIMINO                                                     2.0.0 |
|==============================================================================|
|  Copyright (C) 2013, 2019 Davide Nardella                                    |
|  All rights reserved.                                                        |
|==============================================================================|
|  SETTIMINO is free software: you can redistribute it and/or modify           |
|  it under the terms of the Lesser GNU General Public License as published by |
|  the Free Software Foundation, either version 3 of the License, or           |
|  (at your option) any later version.                                         |
|                                                                              |
|  It means that you can distribute your commercial software linked with       |
|  SETTIMINO without the requirement to distribute the source code of your     |
|  application and without the requirement that your application be itself     |
|  distributed under LGPL.                                                     |
|                                                                              |
|  SETTIMINO is distributed in the hope that it will be useful,                |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of              |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
|  Lesser GNU General Public License for more details.                         |
|                                                                              |
|  You should have received a copy of the GNU General Public License and a     |
|  copy of Lesser GNU General Public License along with Snap7.                 |
|  If not, see  http://www.gnu.org/licenses/                                   |
|------------------------------------------------------------------------------|
|                                                                              |
|  1.1.0 Added support for ESP8266 (Thanks to Geoffrey Hayward Piggot)         |
|  2.0.0 Added new hardware support                                            |
|        Added Read/Write consistent bit into the CPU                          |
|        Added new 18 helper functions                                         |
|        Small bugfixes (Thanks to Daniel Förstmann and Schöneberg Swen)       |
|                                                                              |
|=============================================================================*/
#include "Settimino.h"

// For further informations about structures (the byte arrays and they meanins)
// see http://snap7.sourceforge.net project.

/*
	
Arduino has not a multithread environment and all Client functions are 
	
fully synchronous, so to save memory we can define telegrams and I/O
	
data areas as globals, since only one client at time will use them.
*/

// ISO Connection Request telegram (contains also ISO Header and COTP Header)
	
byte ISO_CR[] = {
	
	
// TPKT (RFC1006 Header)
	
	
0x03// RFC 1006 ID (3) 
	
	
0x00// Reserved, always 0
	
	
0x00// High part of packet lenght (entire frame, payload and TPDU included)
	
	
0x16// Low part of packet lenght (entire frame, payload and TPDU included)
	
	
// COTP (ISO 8073 Header)
	
	
0x11// PDU Size Length
	
	
0xE0// CR - Connection Request ID
	
	
0x00// Dst Reference HI
	
	
0x00// Dst Reference LO
        
0x00// Src Reference HI
	
	
0x01// Src Reference LO
	
	
0x00// Class + Options Flags
	
	
0xC0// PDU Max Length ID
	
	
0x01// PDU Max Length HI
	
	
0x0A// PDU Max Length LO
	
	
0xC1// Src TSAP Identifier
	
	
0x02// Src TSAP Length (2 bytes)
        
0x01// Src TSAP HI (will be overwritten by ISOConnect())
	
	
0x00// Src TSAP LO (will be overwritten by ISOConnect())
	
	
0xC2// Dst TSAP Identifier
	
	
0x02// Dst TSAP Length (2 bytes)
	
	
0x01// Dst TSAP HI (will be overwritten by ISOConnect())
	
	
0x02  // Dst TSAP LO (will be overwritten by ISOConnect())
	
};

// S7 PDU Negotiation Telegram (contains also ISO Header and COTP Header)
	
byte S7_PN[] = {
	
	
0x030x000x000x190x020xf00x80// TPKT + COTP (see above for info)
	
	
0x320x010x000x000x040x000x000x080x00
	
	
0x000xf00x000x000x010x000x01
	
	
0x000xf0 // PDU Length Requested = HI-LO 240 bytes
	
};

// S7 Read/Write Request Header (contains also ISO Header and COTP Header)
	
byte S7_RW[] = { // 31-35 bytes
	
	
0x030x00
	
	
0x000x1f// Telegram Length (Data Size + 31 or 35)
	
	
0x020xf00x80// COTP (see above for info)
	
	
0x32,       // S7 Protocol ID 
	
	
0x01,       // Job Type
	
	
0x000x00// Redundancy identification
	
	
0x050x00// PDU Reference
	
	
0x000x0e// Parameters Length
	
	
0x000x00// Data Length = Size(bytes) + 4      
	
	
0x04,       // Function 4 Read Var, 5 Write Var  
	
	
0x01,       // Items count
	
	
0x12,       // Var spec.
	
	
0x0a,       // Length of remaining bytes
	
	
0x10,       // Syntax ID 
	
	
S7WLByte,   // Transport Size (default, could be changed)                       
	
	
0x00,0x00,  // Num Elements                          
	
	
0x00,0x00,  // DB Number (if any, else 0)            
	
	
0x84,       // Area Type                            
	
	
0x000x000x00// Area Offset                     
	
	
// WR area
	
	
0x00,       // Reserved 
	
	
0x04,       // Transport size
	
	
0x000x00// Data Length * 8 (if not timer or counter) 
	
};

#ifdef _EXTENDED

// S7 Get Block Info Request Header (contains also ISO Header and COTP Header)
	
byte S7_BI[] = {
	
	
0x030x000x000x250x020xf00x800x32
	
	
0x070x000x000x050x000x000x080x00
	
	
0x0c0x000x010x120x040x110x430x03
	
	
0x000xff0x090x000x080x300x41
	
	
0x300x300x300x300x30// ASCII DB Number
	
	
0x41 
	
};

// S7 Put PLC in STOP state Request Header (contains also ISO Header and COTP Header)
	
byte S7_STOP[] = {
	
	
0x030x000x000x210x020xf00x800x32
	
	
0x010x000x000x0e0x000x000x100x00
	
	
0x000x290x000x000x000x000x000x09
	
	
0x500x5f0x500x520x4f0x470x520x41
	
	
0x4d 
	
};

// S7 Put PLC in RUN state Request Header (contains also ISO Header and COTP Header)
	
byte S7_START[] = {
	
	
0x030x000x000x250x020xf00x800x32
	
	
0x010x000x000x0f0x000x000x140x00
	
	
0x000x280x000x000x000x000x000x00
	
	
0xfd0x000x000x090x500x5f0x500x52
	
	
0x4f0x470x520x410x4d 
	
};

// S7 Get PLC Status Request Header (contains also ISO Header and COTP Header)
	
byte S7_PLCGETS[] = {
	
	
0x030x000x000x210x020xf00x800x32
	
	
0x070x000x000x2c0x000x000x080x00
	
	
0x080x000x010x120x040x110x440x01
	
	
0x000xff0x090x000x040x040x240x00
	
	
0x00 
	
};

#endif // _EXTENDED

	
TPDU PDU;

#ifdef _S7HELPER

	
S7Helper S7;
//-----------------------------------------------------------------------------
bool S7Helper::BitAt(void *Bufferint ByteIndexbyte BitIndex)
{
	
byte mask[] = {0x010x020x040x080x100x200x400x80};
	
pbyte Pointer pbyte(Buffer) + ByteIndex;
	

	
if (
BitIndex>7)
	
	
return 
false;
	
else
	
	
return (*
Pointer mask[BitIndex]);
}
//-----------------------------------------------------------------------------
bool S7Helper::BitAt(int ByteIndexint BitIndex)
{
	
return 
BitAt(&PDU.DATA[0], ByteIndexBitIndex);
}
//-----------------------------------------------------------------------------
byte S7Helper::ByteAt(void *Bufferint index)
{
	
pbyte Pointer pbyte(Buffer) + index;
	
return *
Pointer;
}
//-----------------------------------------------------------------------------
byte S7Helper::ByteAt(int index)
{
	
return 
ByteAt(&PDU.DATAindex);
}
//-----------------------------------------------------------------------------
word S7Helper::WordAt(void *Bufferint index)
{
	
word hi=(*(pbyte(Buffer) + index))<<8;
	
return 
hi+*(pbyte(Buffer) + index+1);
}
//-----------------------------------------------------------------------------
word S7Helper::WordAt(int index)
{
	
return 
WordAt(&PDU.DATAindex);
}
//-----------------------------------------------------------------------------
dword S7Helper::DWordAt(void *Bufferint index)
{
	
pbyte pb;
	
dword dw1;

	
pb=pbyte(Buffer) + index;
	
dw1=*pb;dw1<<=8;
	
pb=pbyte(Buffer) + index 1;
	
dw1+=*pb;dw1<<=8;
	
pb=pbyte(Buffer) + index 2;
	
dw1+=*pb;dw1<<=8;
	
pb=pbyte(Buffer) + index 3;
	
dw1+=*pb;
	
return 
dw1;
}
//-----------------------------------------------------------------------------
dword S7Helper::DWordAt(int index)
{
	
return 
DWordAt(&PDU.DATAindex);
}
//-----------------------------------------------------------------------------
float S7Helper::FloatAt(void *Bufferint index)
{
	
dword dw DWordAt(Bufferindex);
	
return *(
pfloat(&dw));
}
//-----------------------------------------------------------------------------
float S7Helper::FloatAt(int index)
{
	
return 
FloatAt(&PDU.DATAindex);
}
//-----------------------------------------------------------------------------
integer S7Helper::IntegerAt(void *Bufferint index)
{
	
word w WordAt(Bufferindex);
	
return *(
pinteger(&w));
}
//-----------------------------------------------------------------------------
integer S7Helper::IntegerAt(int index)
{
	
return 
IntegerAt(&PDU.DATAindex);
}
//-----------------------------------------------------------------------------
long S7Helper::DintAt(void *Bufferint index)
{
	
dword dw DWordAt(Bufferindex);
	
return *(
pdint(&dw));
}
//-----------------------------------------------------------------------------
long S7Helper::DintAt(int index)
{
	
return 
DintAt(&PDU.DATAindex);
}
//-----------------------------------------------------------------------------
void S7Helper::SetBitAt(void *Bufferint ByteIndexint BitIndexbool Value)
{
	
byte Mask[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
	
pbyte Pointer pbyte(Buffer) + ByteIndex;
	
if (
BitIndex 0BitIndex 0;
	
if (
BitIndex 7BitIndex 7;

	
if (
Value)
	
	
*
Pointer=*Pointer Mask[BitIndex];
	
else
	
	
*
Pointer=*Pointer & ~Mask[BitIndex];
}
//-----------------------------------------------------------------------------
void S7Helper::SetBitAt(int ByteIndexint BitIndexbool Value)
{
	
SetBitAt(&PDU.DATAByteIndexBitIndexValue);
}
//-----------------------------------------------------------------------------
void S7Helper::SetByteAt(void *Bufferint indexbyte value)
{
  *(
pbyte(Buffer)+index)=value;
}
//-----------------------------------------------------------------------------
void S7Helper::SetByteAt(int indexbyte value)
{
  
PDU.DATA[index]=value;
}
//-----------------------------------------------------------------------------
void S7Helper::SetIntAt(void *Bufferint indexinteger value)
{
  *(
pbyte(Buffer)+index)  =byte(value>>8);
  *(
pbyte(Buffer)+index+1)=byte(value 0x00FF);
}
//-----------------------------------------------------------------------------
void S7Helper::SetIntAt(int indexinteger value)
{
	
SetIntAt(&PDU.DATAindexvalue);
}
//-----------------------------------------------------------------------------
void S7Helper::SetDIntAt(void *Bufferint indexdint value)
{
  *(
pbyte(Buffer)+index)  =byte((value >> 24) & 0xFF);
  *(
pbyte(Buffer)+index+1)=byte((value >> 16) & 0xFF);
  *(
pbyte(Buffer)+index+2)=byte((value >> 8) & 0xFF);
  *(
pbyte(Buffer)+index+3)=byte(value 0x00FF);
}
//-----------------------------------------------------------------------------
void S7Helper::SetDIntAt(int indexdint value)
{
	
SetDIntAt(&PDU.DATAindexvalue);
}
//-----------------------------------------------------------------------------
void S7Helper::SetWordAt(void *Bufferint indexword value)
{
  *(
pbyte(Buffer)+index)  =byte(value>>8);
  *(
pbyte(Buffer)+index+1)=byte(value 0x00FF);
}
//-----------------------------------------------------------------------------
void S7Helper::SetWordAt(int indexword value)
{
	
SetWordAt(&PDU.DATAindexvalue);
}
//-----------------------------------------------------------------------------
void S7Helper::SetDWordAt(void *Bufferint indexdword value)
{
  *(
pbyte(Buffer)+index)  =byte((value >> 24) & 0xFF);
  *(
pbyte(Buffer)+index+1)=byte((value >> 16) & 0xFF);
  *(
pbyte(Buffer)+index+2)=byte((value >> 8) & 0xFF);
  *(
pbyte(Buffer)+index+3)=byte(value 0x00FF);
}
//-----------------------------------------------------------------------------
void S7Helper::SetDWordAt(int indexword value)
{
	
SetDWordAt(&PDU.DATAindexvalue);
}
//-----------------------------------------------------------------------------
void S7Helper::SetFloatAt(void *Bufferint indexfloat value)
{
  
pdword dvalue pdword(&value);
  
  *(
pbyte(Buffer)+index)  =byte((*dvalue >> 24) & 0xFF);
  *(
pbyte(Buffer)+index+1)=byte((*dvalue >> 16) & 0xFF);
  *(
pbyte(Buffer)+index+2)=byte((*dvalue >> 8) & 0xFF);
  *(
pbyte(Buffer)+index+3)=byte(*dvalue 0x00FF);
	

}
//-----------------------------------------------------------------------------
void S7Helper::SetFloatAt(int indexfloat value)
{
	
SetFloatAt(&PDU.DATAindexvalue);
}
//-----------------------------------------------------------------------------
char S7Helper::StringAt(void *Bufferint index)
{
  return 
pchar(Buffer+index);
}
//-----------------------------------------------------------------------------
char S7Helper::StringAt(int index)
{
  return 
pchar(&PDU.DATA[index]);
}
//-----------------------------------------------------------------------------
void S7Helper::SetStringAt(void *Bufferint indexchar *value)
{
  
strcpy(pchar(Buffer+index),value);
}
//-----------------------------------------------------------------------------
void S7Helper::SetStringAt(int indexchar *value)
{
  
strcpy(pchar(&PDU.DATA[index]),value);
}

#endif // _S7HELPER

//-----------------------------------------------------------------------------
// Ethernet initialization 
//-----------------------------------------------------------------------------
void EthernetInit(uint8_t *macIPAddress ip)
{
#ifdef S7WIRED
  #ifdef M5STACK_LAN    
      
SPI.begin(181923, -1);
      
Ethernet.init(26);
  
#endif  
      // Start Ethernet
      
Ethernet.begin(macip); 
#endif    
}
//-----------------------------------------------------------------------------
S7Client::S7Client()
{
	
// Default TSAP values for connectiong as PG to a S7300 (Rack 0, Slot 2)
	
LocalTSAP_HI 0x01;
	
LocalTSAP_LO 0x00;
	
RemoteTSAP_HI0x01;
	
RemoteTSAP_LO0x02;
	
ConnType PG;
	
Connected false;
	
LastError 0;
	
PDULength 0;
	
RecvTimeout 500// 500 ms
	

#ifdef S7WIFI
	

    
TCPClient = new(WiFiClient);
#endif    
#ifdef S7WIRED 
    
TCPClient = new(EthernetClient);
#endif    
}
//-----------------------------------------------------------------------------
S7Client::~S7Client()
{
	
Disconnect();
	
delete TCPClient;
}
//-----------------------------------------------------------------------------
int S7Client::SetLastError(int Error)
{
	
LastError=Error;
	
return 
Error;
}
//-----------------------------------------------------------------------------
int S7Client::WaitForData(uint16_t Sizeuint16_t Timeout)
{
	
unsigned long Elapsed millis();
	
uint16_t BytesReady;

	
do
	
{
	
	
// I don't like next function because imho is buggy, it returns 0 also if _sock==MAX_SOCK_NUM
	
	
// but it belongs to the standard Ethernet library and there are too many dependencies to skip them.
	
	
// So be very carefully with the munber of the clients, they must be <=4.
	
	
BytesReady=TCPClient->available();
	
	
if (
BytesReady<Size)
	
	
	
delayMicroseconds(500);
	
	
else
	
	
	
return 
SetLastError(0);

	
	
// Check for rollover - should happen every 52 days without turning off Arduino.
	
	
if (
millis()<Elapsed)
	
	
	
Elapsed=millis(); // Resets the counter, in the worst case we will wait some additional millisecs.

	
}while(
millis()-Elapsed<Timeout);
	


	
// Here we are in timeout zone, if there's something into the buffer, it must be discarded.
	
if (
BytesReady>0)
	
	
TCPClient->flush();
	
else
	
{
	
	
if (!
TCPClient->connected())
	
	
	
return 
SetLastError(errTCPConnectionReset);
	
}

	
return 
SetLastError(errTCPDataRecvTout);
}
//-----------------------------------------------------------------------------
int S7Client::IsoPduSize()
{
	
uint16_t Size PDU.H[2];
	
return (
Size<<8) + PDU.H[3];
}
//-----------------------------------------------------------------------------
int S7Client::RecvPacket(uint8_t *bufuint16_t Size)
{
	
WaitForData(Size,RecvTimeout);
	
if (
LastError!=0)
	
	
return 
LastError;
	
if (
TCPClient->read(bufSize)==0)
	
	
return 
SetLastError(errTCPConnectionReset);
	
return 
SetLastError(0);
}
//-----------------------------------------------------------------------------
void S7Client::SetConnectionParams(IPAddress Addressuint16_t LocalTSAPuint16_t RemoteTSAP)
{
	
Peer Address;
	
LocalTSAP_HI LocalTSAP>>8;
	
LocalTSAP_LO LocalTSAP 0x00FF;
	
RemoteTSAP_HI RemoteTSAP>>8;
	
RemoteTSAP_LO RemoteTSAP 0x00FF;
}
//-----------------------------------------------------------------------------
void S7Client::SetConnectionType(uint16_t ConnectionType)
{
	
ConnType ConnectionType;
}
//-----------------------------------------------------------------------------
int S7Client::ConnectTo(IPAddress Addressuint16_t Rackuint16_t Slot)
{
	
SetConnectionParams(Address0x0100, (ConnType<<8)+(Rack 0x20) + Slot);
	
return 
Connect();
}
//-----------------------------------------------------------------------------
int S7Client::Connect()
{
	
LastError 0;
	
if (!
Connected)
	
{
	
	
TCPConnect();
	
	
if (
LastError==0// First stage : TCP Connection
	
	
{
	
	
	
ISOConnect();
	
	
	
if (
LastError==0// Second stage : ISOTCP (ISO 8073) Connection
	
	
	
{
	
	
	
	
LastError=NegotiatePduLength(); // Third stage : S7 PDU negotiation
	
	
	
}
	
	
}
	

	
}
	
Connected=LastError==0;
	
return 
LastError;
}
//-----------------------------------------------------------------------------
void S7Client::Disconnect()
{
	
if (
Connected)
	
{
	
	
TCPClient->stop();
	
	
Connected false;
	
	
PDULength 0;
	
	
LastError 0;
	
}
	

}
//-----------------------------------------------------------------------------
int S7Client::TCPConnect()
{
	
if (
TCPClient->connect(Peerisotcp))
	
	
return 
SetLastError(0);
	
else
	
	
return 
SetLastError(errTCPConnectionFailed);
}
//-----------------------------------------------------------------------------
int S7Client::RecvISOPacket(uint16_t *Size)
{
	
bool Done false;
	
pbyte Target pbyte(&PDU.H[0])+Shift;
	
LastError=0;

	
while ((
LastError==0) && !Done)
	
{
	
	
// Get TPKT (4 bytes)
	
	
RecvPacket(PDU.H4); 
	
	
if (
LastError==0)
	
	
{
	
	
	
*
Size IsoPduSize();
	
	
	
// Check 0 bytes Data Packet (only TPKT+COTP - 7 bytes)
	
	
	
if (*
Size==7)
	
	
	
	
RecvPacket(PDU.H3); // Skip remaining 3 bytes and Done is still false
	
	
	
else
	
	
	
{
	
	
	
	
if ((*
Size>MaxPduSize) || (*Size<MinPduSize))
	
	
	
	
	
LastError=errISOInvalidPDU;
	
	
	
	
else
	
	
	
	
	
Done true// a valid Length !=7 && >16 && <247
	
	
	
}
	
	
}
	
}
	
if (
LastError==0)
	
{
	
	
RecvPacket(PDU.H3); // Skip remaining 3 COTP bytes
	
	
LastPDUType=PDU.H[1]; // Stores PDU Type, we need it 
	
	
*
Size-=ISOSize;
	
	
// We need to align with PDU.DATA
	
	
RecvPacket(Target, *Size);
	
}
	
if (
LastError!=0)
	
	
TCPClient->flush();
	
return 
LastError;
}
//-----------------------------------------------------------------------------
int S7Client::ISOConnect()
{
	
bool Done false;
	
uint16_t Length;
	
// Setup TSAPs
	
ISO_CR[16]=LocalTSAP_HI;
	
ISO_CR[17]=LocalTSAP_LO;
	
ISO_CR[20]=RemoteTSAP_HI;
	
ISO_CR[21]=RemoteTSAP_LO;

	
if (
TCPClient->write(&ISO_CR[0], sizeof(ISO_CR))==sizeof(ISO_CR))
	
{
	
	
RecvISOPacket(&Length);
	
	
if ((
LastError==0) && (Length==15)) // 15 = 22 (sizeof CC telegram) - 7 (sizeof Header)
	
	
{
	
	
	
if (
LastPDUType==CC// Connection confirm
	
	
	
	
return 
0;
	
	
	
else
	
	
	
	
return 
SetLastError(errISOInvalidPDU);
	
	
}
	
	
else
	
	
	
return 
LastError;
	
}
	
else
	
	
return 
SetLastError(errISOConnectionFailed);
}
//-----------------------------------------------------------------------------
int S7Client::NegotiatePduLength()
{
	
uint16_t Length;
	
if (
TCPClient->write(&S7_PN[0], sizeof(S7_PN))==sizeof(S7_PN))
	
{
	
	
RecvISOPacket(&Length);
	
	
if (
LastError==0)                 
	
	
{
	
	
	
// check S7 Error
	
	
	
if ((
Length==20) && (PDU.H[27]==0) && (PDU.H[28]==0))  // 20 = size of Negotiate Answer
	
	
	
{
	
	
	
	
PDULength PDU.RAW[35];
	
	
	
	
PDULength = (PDULength<<8) + PDU.RAW[36]; // Value negotiated
	
	
	
	
if (
PDULength>0)
	
	
	
	
    return 
0;
	
	
	
	
else
	
	
	
	
	
return 
SetLastError(errISONegotiatingPDU);
	
	
	
}
	
	
	
else 
	
	
	
	
return 
SetLastError(errISONegotiatingPDU);
	
	
}
	
	
else
	
	
	
return 
LastError;
	
}
	
else
	
	
return 
SetLastError(errISONegotiatingPDU);
}
//-----------------------------------------------------------------------------
int S7Client::ReadArea(int Areauint16_t DBNumberuint16_t Startuint16_t Amountint WordLenvoid *ptrData)
{
	
unsigned long Address;
	
unsigned long LongStart=Start;
	
uint16_t NumElements;
	
uint16_t MaxElements;
	
uint16_t TotElements;
	
uint16_t SizeRequested;
	
uint16_t Length;

	
pbyte Target;
	
uintptr_t Offset 0;
	
int WordSize 1;

	
LastError=0;
	

	
// If we are addressing Timers or counters the element size is 2
	
if ((
Area==S7AreaCT) || (Area==S7AreaTM))
	
	
WordSize 2;

	
if (
WordLen==S7WLBit// Only one bit can be transferred in S7Protocol when WordLen==S7WLBit
	
	
Amount=1;

    
MaxElements=(PDULength-18) / WordSize// 18 = Reply telegram header
	
TotElements=Amount;
	
// If we use the internal buffer only, we cannot exced the PDU limit
	
if (
ptrData==NULL)
	
{
	
	
if (
TotElements>MaxElements)
	
	
	
TotElements=MaxElements;
	
}
	

    while ((
TotElements>0) && (LastError==0))
    {
        
NumElements=TotElements;
        if (
NumElements>MaxElements)
           
NumElements=MaxElements;
	
	

	
	
SizeRequested =NumElements WordSize;

	
	
Target=pbyte(ptrData)+Offset;

	
	
// Setup the telegram
	
	
memcpy(&PDU.HS7_RWSize_RD); 

	
	
// Set DB Number
	
	
PDU.H[27] = Area;
	
	
if (
Area==S7AreaDB
	
	
{
	
	
	
PDU.H[25] = DBNumber>>8;
	
	
	
PDU.H[26] = DBNumber 0x00FF;
	
	
}

	
	
// Adjusts Start and word length
	
	
if ((
WordLen == S7WLBit) || (Area==S7AreaCT) || (Area==S7AreaTM))
	
	
{
	
	
	
Address LongStart;
	
	
	
if (
WordLen==S7WLBit)
	
	
	
	
PDU.H[22]=S7WLBit;
	
	
	
else {
	
	
	
	
if (
Area==S7AreaCT)
	
	
	
	
	
PDU.H[22]=S7WLCounter;
	
	
	
	
else
	
	
	
	
	
PDU.H[22]=S7WLTimer;
	
	
	
}
	
	
}
	
	
else
	
	
	
Address LongStart<<3;

	
	
// Num elements
	
	
PDU.H[23]=NumElements<<8;
	
	
PDU.H[24]=NumElements;

	
	
// Address into the PLC
        
PDU.H[30] = Address 0x000000FF;
        
Address Address >> 8;
	
	
PDU.H[29] = Address 0x000000FF;
        
Address Address >> 8;
        
PDU.H[28] = Address 0x000000FF;

	
	
if (
TCPClient->write(&PDU.H[0], Size_RD)==Size_RD)
	
	
{
	
	
	
RecvISOPacket(&Length);
	
	
	
if (
LastError==0)
	
	
	
{
	
	
	
	
if (
Length>=18)
	
	
	
	
{
	
	
	
	
	
if ((
Length-18==SizeRequested) && (PDU.H[31]==0xFF))
	
	
	
	
	
{
	
	
	
	
	
	
if (
ptrData!=NULL)
	
	
	
	
	
	
	
memcpy(Target, &PDU.DATA[0], SizeRequested); // Copies in the user's buffer
	
	
	
	
	
	
Offset+=SizeRequested;
	
	
	
	
	
}
	
	
	
	
	
else
	
	
	
	
	
	
LastError errS7DataRead;
	
	
	
	
}
	
	
	
	
else
	
	
	
	
	
LastError errS7InvalidPDU;
	
	
	
}
	
	
}
	
	
else
	
	
	
LastError errTCPDataSend;
	
	

	
	
TotElements -= NumElements;
        
LongStart += NumElements*WordSize;
	
}
	
return 
LastError;
}
//-----------------------------------------------------------------------------
int S7Client::ReadArea(int Areauint16_t DBNumberuint16_t Startuint16_t Amountvoid *ptrData)
{
	
return 
ReadArea(AreaDBNumberStartAmountS7WLByteptrData);
}
//-----------------------------------------------------------------------------
int S7Client::ReadBit(int Areauint16_t DBNumberuint16_t BitStartbool &Bit)
{
	
return 
ReadArea(AreaDBNumberBitStart1S7WLBit, &Bit);
}
//-----------------------------------------------------------------------------
int S7Client::WriteArea(int Areauint16_t DBNumberuint16_t Startuint16_t Amountint WordLenvoid *ptrData)
{
	
unsigned long Address;
	
unsigned long LongStart=Start;
	
uint16_t NumElements;
	
uint16_t MaxElements;
	
uint16_t TotElements;
	
uint16_t DataSize;
	
uint16_t IsoSize;
	
uint16_t Length;

	
pbyte Source;
	
uintptr_t Offset 0;
	
int WordSize 1;

	
LastError=0;
	

	
// If we are addressing Timers or counters the element size is 2
	
if ((
Area==S7AreaCT) || (Area==S7AreaTM))
	
	
WordSize 2;

	
if (
WordLen==S7WLBit// Only one bit can be transferred in S7Protocol when WordLen==S7WLBit
	
	
Amount=1;

    
MaxElements=(PDULength-35) / WordSize// 35 = Write telegram header
	
TotElements=Amount;
	
if (
ptrData==NULL)
	
{
	
	
if (
TotElements>MaxElements)
	
	
	
TotElements=MaxElements;
	
}
	

    while ((
TotElements>0) && (LastError==0))
    {
        
NumElements=TotElements;
        if (
NumElements>MaxElements)
           
NumElements=MaxElements;
	
	
// If we use the internal buffer only, we cannot exced the PDU limit
	
	
DataSize=NumElements*WordSize;
	
	
IsoSize=Size_WR+DataSize;

	
	
// Setup the telegram
	
	
memcpy(&PDU.HS7_RWSize_WR); 
	
	
// Whole telegram Size
	
	
PDU.H[2]=IsoSize>>8;
	
	
PDU.H[3]=IsoSize 0x00FF;
	
	
// Data Length
	
	
Length=DataSize+4;
	
	
PDU.H[15]=Length>>8;
	
	
PDU.H[16]=Length 0x00FF;
	
	
// Function
	
	
PDU.H[17]=0x05;
	
	
// Set DB Number
	
	
PDU.H[27] = Area;
	
	
if (
Area==S7AreaDB
	
	
{
	
	
	
PDU.H[25] = DBNumber>>8;
	
	
	
PDU.H[26] = DBNumber 0x00FF;
	
	
}
	
	
// Adjusts Start and word length
	
	
if ((
WordLen == S7WLBit) || (Area==S7AreaCT) || (Area==S7AreaTM))
	
	
{
	
	
	
Address LongStart;
	
	
	
Length DataSize;
	
	
	
if (
WordLen==S7WLBit)
	
	
	
	
PDU.H[22]=S7WLBit;
	
	
	
else {
	
	
	
	
if (
Area==S7AreaCT)
	
	
	
	
	
PDU.H[22]=S7WLCounter;
	
	
	
	
else
	
	
	
	
	
PDU.H[22]=S7WLTimer;
	
	
	
}
	
	
}
	
	
else
	
	
{
	
	
	
Address LongStart<<3;
	
	
	
Length DataSize<<3;
	
	
}
	
	
// Num elements
	
	
PDU.H[23]=NumElements<<8;
	
	
PDU.H[24]=NumElements;
	
	
// Address into the PLC
        
PDU.H[30] = Address 0x000000FF;
        
Address Address >> 8;
	
	
PDU.H[29] = Address 0x000000FF;
        
Address Address >> 8;
        
PDU.H[28] = Address 0x000000FF;
	
	

	
	
// Transport Size
	
	
switch (
WordLen)
	
	
{
	
	
	
case 
S7WLBit:
	
	
	
	
PDU.H[32] = TS_ResBit;
	
	
	
	
break;
	
	
	
case 
S7WLCounter:
	
	
	
case 
S7WLTimer:
	
	
	
	
PDU.H[32] = TS_ResOctet;
	
	
	
	
break;
	
	
	
default:
	
	
	
	
PDU.H[32] = TS_ResByte// byte/word/dword etc.
	
	
	
	
break;
	
	
};
	
	

	
	
// Length
	
	
PDU.H[33]=Length>>8;
	
	
PDU.H[34]=Length 0x00FF;
	
	
// Copy data
	
	
Source=pbyte(ptrData)+Offset;
	
	
if (
ptrData!=NULL)
	
	
	
memcpy(&PDU.RAW[35], SourceDataSize);

	
	
if (
TCPClient->write(&PDU.H[0], IsoSize)==IsoSize)
	
	
{
	
	
	
RecvISOPacket(&Length);
	
	
	
if (
LastError==0)
	
	
	
{
	
	
	
	
if (
Length==15)
	
	
	
	
{
	
	
	
	
	
if ((
PDU.H[27]!=0x00) || (PDU.H[28]!=0x00) || (PDU.H[31]!=0xFF))
	
	
	
	
	
	
LastError errS7DataWrite;
	
	
	
	
}
	
	
	
	
else
	
	
	
	
	
LastError errS7InvalidPDU;
	
	
	
}
	
	
}
	
	
else
	
	
	
LastError errTCPDataSend;

	
	
Offset+=DataSize;
	
	
TotElements -= NumElements;
        
LongStart += NumElements*WordSize;
	
}
	
return 
LastError;
}
//-----------------------------------------------------------------------------
int S7Client::WriteArea(int Areauint16_t DBNumberuint16_t Startuint16_t Amountvoid *ptrData)
{
	
return 
WriteArea(AreaDBNumberStartAmountS7WLByteptrData);
}
//-----------------------------------------------------------------------------
int S7Client::WriteBit(int Areauint16_t DBNumberuint16_t BitIndexbool Bit)
{
	
bool BitToWrite=Bit;
	
return 
WriteArea(AreaDBNumberBitIndex1S7WLBit, &BitToWrite);
}
//-----------------------------------------------------------------------------
int S7Client::WriteBit(int Areauint16_t DBNumberuint16_t ByteIndexuint16_t BitInBytebool Bit)
{
	
bool BitToWrite=Bit;
	
return 
WriteArea(AreaDBNumberByteIndex*8+BitInByte1S7WLBit, &BitToWrite);
}
//-----------------------------------------------------------------------------
#ifdef _EXTENDED

int S7Client::GetDBSize(uint16_t DBNumberuint16_t *Size)
{
	
uint16_t Length;
	
LastError=0;
	
*
Size=0;
	
// Setup the telegram
	
memcpy(&PDU.H[0], S7_BIsizeof(S7_BI));
	
// Set DB Number
    
PDU.RAW[31]=(DBNumber 10000)+0x30;
    
DBNumber=DBNumber 10000;
    
PDU.RAW[32]=(DBNumber 1000)+0x30;
    
DBNumber=DBNumber 1000;
    
PDU.RAW[33]=(DBNumber 100)+0x30;
    
DBNumber=DBNumber 100;
    
PDU.RAW[34]=(DBNumber 10)+0x30;
    
DBNumber=DBNumber 10;
    
PDU.RAW[35]=(DBNumber 1)+0x30;
	
if (
TCPClient->write(&PDU.H[0], sizeof(S7_BI))==sizeof(S7_BI))
	
{
	
	
RecvISOPacket(&Length);
	
	
if (
LastError==0)
	
	
{
	
	
	
if (
Length>25// 26 is the minimum expected
	
	
	
{
	
	
	
	
if ((
PDU.RAW[37]==0x00) && (PDU.RAW[38]==0x00) && (PDU.RAW[39]==0xFF))
	
	
	
	
{
	
	
	
	
	
*
Size=PDU.RAW[83];
	
	
	
	
	
*
Size=(*Size<<8)+PDU.RAW[84];
	
	
	
	
}
	
	
	
	
else
	
	
	
	
	
LastError errS7Function;
	
	
	
}
	
	
	
else
	
	
	
	
LastError errS7InvalidPDU;
	
	
}
	
}
	
else
	
	
LastError errTCPDataSend;
	

	
return 
LastError;
}
//-----------------------------------------------------------------------------
int S7Client::DBGet(uint16_t DBNumbervoid *ptrDatauint16_t *Size)
{
	
uint16_t Length;
	
int Result;

	
Result=GetDBSize(DBNumber, &Length);
	
if (
Result==0)
	
{
	
	
if (
Length<=*Size// Check if the buffer supplied is big enough 
	
	
{
	
	
	
Result=ReadArea(S7AreaDBDBNumber0LengthptrData);
	
	
	
if (
Result==0)
	
	
	
	
*
Size=Length;
	
	
}
	
	
else
	
	
	
Result=errBufferTooSmall;
	
}

	
return 
Result;
}
//-----------------------------------------------------------------------------
int S7Client::PlcStop()
{
	
uint16_t Length;
	
LastError=0;
	
// Setup the telegram
	
memcpy(&PDU.HS7_STOPsizeof(S7_STOP)); 
	
if (
TCPClient->write(&PDU.H[0], sizeof(S7_STOP))==sizeof(S7_STOP))
	
{
	
	
RecvISOPacket(&Length);
	
	
if (
LastError==0)
	
	
{
	
	
	
if (
Length>12// 13 is the minimum expected
	
	
	
{
	
	
	
	
if ((
PDU.H[27]!=0x00) || (PDU.H[28]!=0x00))
	
	
	
	
	
LastError errS7Function;
	
	
	
}
	
	
	
else
	
	
	
	
LastError errS7InvalidPDU;
	
	
}
	
}
	
else
	
	
LastError errTCPDataSend;
	

	
return 
LastError;
}
//-----------------------------------------------------------------------------
int S7Client::PlcStart()
{
	
uint16_t Length;
	
LastError=0;
	
// Setup the telegram
	
memcpy(&PDU.HS7_STARTsizeof(S7_START)); 
	
if (
TCPClient->write(&PDU.H[0], sizeof(S7_START))==sizeof(S7_START))
	
{
	
	
RecvISOPacket(&Length);
	
	
if (
LastError==0)
	
	
{
	
	
	
if (
Length>12// 13 is the minimum expected
	
	
	
{
	
	
	
	
if ((
PDU.H[27]!=0x00) || (PDU.H[28]!=0x00))
	
	
	
	
	
LastError errS7Function;
	
	
	
}
	
	
	
else
	
	
	
	
LastError errS7InvalidPDU;
	
	
}
	
}
	
else
	
	
LastError errTCPDataSend;
	

	
return 
LastError;
}
//-----------------------------------------------------------------------------
int S7Client::GetPlcStatus(int *Status)
{
	
uint16_t Length;
	
LastError=0;
	
// Setup the telegram
	
memcpy(&PDU.HS7_PLCGETSsizeof(S7_PLCGETS)); 
	
if (
TCPClient->write(&PDU.H[0], sizeof(S7_PLCGETS))==sizeof(S7_PLCGETS))
	
{
	
	
RecvISOPacket(&Length);
	
	
if (
LastError==0)
	
	
{
	
	
	
if (
Length>53// 54 is the minimum expected
	
	
	
{
	
	
	
	
switch (
PDU.RAW[54])
	
	
	
	
{
	
	
	
	
	
case 
S7CpuStatusUnknown :
	
	
	
	
	
case 
S7CpuStatusRun     :
	
	
	
	
	
case 
S7CpuStatusStop    : *Status=PDU.RAW[54];
	
	
	
	
	
break;
	
	
	
	
	
default :
	
	
	
	
	
// Since RUN status is always 0x08 for all CPUs and CPs, STOP status
	
	
	
	
	
// sometime can be coded as 0x03 (especially for old cpu...)
	
	
	
	
	
	
*
Status=S7CpuStatusStop;
	
	
	
	
}
	
	
	
}
	
	
	
else
	
	
	
	
LastError errS7InvalidPDU;
	
	
}
	
}
	
else
	
	
LastError errTCPDataSend;
	

	
return 
LastError;
}
//-----------------------------------------------------------------------------
int S7Client::IsoExchangeBuffer(uint16_t *Size)
{
	
LastError=0;

	
if (
TCPClient->write(&PDU.H[0], int(Size))==*Size)
	
	
RecvISOPacket(Size);
	
else
	
	
LastError errTCPDataSend;
	

	
return 
LastError;
}
//-----------------------------------------------------------------------------
void S7Client::ErrorText(int Errorchar *Textint TextLen)
{

}
#endif // _EXTENDED
//-----------------------------------------------------------------------------

settimino.h

/*=============================================================================|
|  PROJECT SETTIMINO                                                     2.0.0 |
|==============================================================================|
|  Copyright (C) 2013, 2019 Davide Nardella                                    |
|  All rights reserved.                                                        |
|==============================================================================|
|  SETTIMINO is free software: you can redistribute it and/or modify           |
|  it under the terms of the Lesser GNU General Public License as published by |
|  the Free Software Foundation, either version 3 of the License, or           |
|  (at your option) any later version.                                         |
|                                                                              |
|  It means that you can distribute your commercial software linked with       |
|  SETTIMINO without the requirement to distribute the source code of your     |
|  application and without the requirement that your application be itself     |
|  distributed under LGPL.                                                     |
|                                                                              |
|  SETTIMINO is distributed in the hope that it will be useful,                |
|  but WITHOUT ANY WARRANTY; without even the implied warranty of              |
|  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
|  Lesser GNU General Public License for more details.                         |
|                                                                              |
|  You should have received a copy of the GNU General Public License and a     |
|  copy of Lesser GNU General Public License along with Snap7.                 |
|  If not, see  http://www.gnu.org/licenses/                                   |
|------------------------------------------------------------------------------|
|                                                                              |
|  1.1.0 Added support for ESP8266 (Thanks to Geoffrey Hayward Piggot)         |
|  2.0.0 Added new hardware support                                            |
|        Added Read/Write consistent bit into the CPU                          |
|        Added new 18 helper functions                                         |
|        Small bugfixes (Thanks to Daniel Förstmann and Schöneberg Swen)       |
|                                                                              |
|=============================================================================*/
#ifndef SETTIMINO_H
#define SETTIMINO_H
#include "Platform.h"

// Memory models

//#define _SMALL
//#define _NORMAL
#define _EXTENDED


#if defined(_NORMAL) || defined(_EXTENDED)
# define _S7HELPER
#endif

#pragma pack(1)
// Error Codes 
// from 0x0001 up to 0x00FF are severe errors, the Client should be disconnected
// from 0x0100 are S7 Errors such as DB not found or address beyond the limit etc..
// For Arduino Due the error code is a 32 bit integer but this doesn't change the constants use.
#define errTCPConnectionFailed 0x0001
#define errTCPConnectionReset  0x0002
#define errTCPDataRecvTout     0x0003
#define errTCPDataSend         0x0004
#define errTCPDataRecv         0x0005
#define errISOConnectionFailed 0x0006
#define errISONegotiatingPDU   0x0007
#define errISOInvalidPDU       0x0008

#define errS7InvalidPDU        0x0100
#define errS7SendingPDU        0x0200
#define errS7DataRead          0x0300
#define errS7DataWrite         0x0400
#define errS7Function          0x0500

#define errBufferTooSmall      0x0600

// Connection Type
#define PG       0x01
#define OP       0x02
#define S7_Basic 0x03

// ISO and PDU related constants
#define ISOSize        7  // Size of TPKT + COTP Header
#define isotcp       102  // ISOTCP Port
#define MinPduSize    16  // Minimum S7 valid telegram size
#define MaxPduSize   247  // Maximum S7 valid telegram size (we negotiate 240 bytes + ISOSize)
#define CC          0xD0  // Connection confirm
#define Shift         17  // We receive data 17 bytes above to align with PDU.DATA[]

// S7 ID Area (Area that we want to read/write)
#define S7AreaPE    0x81
#define S7AreaPA    0x82
#define S7AreaMK    0x83
#define S7AreaDB    0x84
#define S7AreaCT    0x1C
#define S7AreaTM    0x1D

// WordLength
#define S7WLBit     0x01
#define S7WLByte    0x02
#define S7WLWord    0x04
#define S7WLDWord   0x06
#define S7WLReal    0x08
#define S7WLCounter 0x1C
#define S7WLTimer   0x1D

#define TS_ResBit   0x03
#define TS_ResByte  0x04
#define TS_ResInt   0x05
#define TS_ResReal  0x07
#define TS_ResOctet 0x09

const byte S7CpuStatusUnknown 0x00;
const 
byte S7CpuStatusRun     0x08;
const 
byte S7CpuStatusStop    0x04;

#define RxOffset    18
#define Size_RD     31
#define Size_WR     35

//typedef uint16_t word;          // 16 bit unsigned integer

typedef int16_t integer;        // 16 bit signed integer
typedef unsigned long dword;    // 32 bit unsigned integer
typedef long dint;              // 32 bit signed integer

typedef byte *pbyte;
typedef word *pword;
typedef dword *pdword;
typedef integer *pinteger;
typedef dint *pdint;
typedef float *pfloat;
typedef char *pchar;

typedef union{
	
struct {
	
	
byte H[Size_WR];                      // PDU Header
	
	
byte DATA[MaxPduSize-Size_WR+Shift];  // PDU Data
	
};
	
byte RAW[MaxPduSize+Shift];
}
TPDU;


#pragma pack()

#ifdef _S7HELPER

class S7Helper
{
public:
	
bool BitAt(void *Bufferint ByteIndexbyte BitIndex);
	
bool BitAt(int ByteIndexint BitIndex);
	
byte ByteAt(void *Bufferint index);
	
byte ByteAt(int index);
	
word WordAt(void *Bufferint index);
	
word WordAt(int index);
	
dword DWordAt(void *Bufferint index);
	
dword DWordAt(int index);
	
float FloatAt(void *Bufferint index);
	
float FloatAt(int index);
	
integer IntegerAt(void *Bufferint index);
	
integer IntegerAt(int index);
	
long DintAt(void *Bufferint index);
	
long DintAt(int index);
	
// New 2.0
    
void SetBitAt(void *Bufferint ByteIndexint BitIndexbool Value);
	
void SetBitAt(int ByteIndexint BitIndexbool Value);
	
void SetByteAt(void *Bufferint indexbyte value);
	
void SetByteAt(int indexbyte value);
	
void SetIntAt(void *Bufferint indexinteger value);
	
void SetIntAt(int indexinteger value);
	
void SetDIntAt(void *Bufferint indexdint value);
	
void SetDIntAt(int indexdint value);
	
void SetWordAt(void *Bufferint indexword value);
	
void SetWordAt(int indexword value);
	
void SetDWordAt(void *Bufferint indexdword value);
	
void SetDWordAt(int indexword value);
	
void SetFloatAt(void *Bufferint indexfloat value);
	
void SetFloatAt(int indexfloat value);
	
char StringAt(void *Bufferint index);
	
char StringAt(int index);
	
void SetStringAt(void *Bufferint indexchar *value);
	
void SetStringAt(int indexchar *value);
	

};
extern S7Helper S7;

#endif // _S7HELPER

//-----------------------------------------------------------------------------
// Ethernet initialization
//-----------------------------------------------------------------------------
void EthernetInit(uint8_t *macIPAddress ip);
//-----------------------------------------------------------------------------
// S7 Client                                       
//-----------------------------------------------------------------------------
class S7Client 
{
private:
	
uint8_t LocalTSAP_HI
	
uint8_t LocalTSAP_LO;
	
uint8_t RemoteTSAP_HI;
	
uint8_t RemoteTSAP_LO;
	
uint8_t LastPDUType;
	
uint16_t ConnType;
	

	
IPAddress Peer;
	

	
// Since we can use either an EthernetClient or a WifiClient 
	
// we have to create the class as an ancestor and then resolve
	
// the inherited into S7Client creator.
	
Client *TCPClient;
	

	
int PDULength;    // PDU Length negotiated
	
int IsoPduSize();
	
int WaitForData(uint16_t Sizeuint16_t Timeout);
	
int RecvISOPacket(uint16_t *Size);
	
int RecvPacket(uint8_t *bufuint16_t Size);
	
int TCPConnect();
	
int ISOConnect();
	
int NegotiatePduLength();
	
int SetLastError(int Error);
public:
	
// Output properties
	
bool Connected;   // true if the Client is connected
	
int LastError;    // Last Operation error
	
// Input properties
	
uint16_t RecvTimeout// Receving timeour
	
// Methods
	
//S7Client();
	
S7Client();
	
S7Client(int Media) : S7Client(){}; // Compatibility V1.X
	
~
S7Client();
	
// Basic functions
	
void SetConnectionParams(IPAddress Addressuint16_t LocalTSAPuint16_t RemoteTSAP);
	
void SetConnectionType(uint16_t ConnectionType);
	
int ConnectTo(IPAddress Addressuint16_t Rackuint16_t Slot);
	
int Connect();
	
void Disconnect();
	
int ReadArea(int Areauint16_t DBNumberuint16_t Startuint16_t Amountvoid *ptrData); 
	
int ReadArea(int Areauint16_t DBNumberuint16_t Startuint16_t Amountint WordLenvoid *ptrData); 
	
int ReadBit(int Areauint16_t DBNumberuint16_t BitStartbool &Bit); 
	
int WriteArea(int Areauint16_t DBNumberuint16_t Startuint16_t Amountvoid *ptrData); 
	
int WriteArea(int Areauint16_t DBNumberuint16_t Startuint16_t Amountint WordLenvoid *ptrData); 
	
int WriteBit(int Areauint16_t DBNumberuint16_t BitIndexbool Bit); 
	
int WriteBit(int Areauint16_t DBNumberuint16_t ByteIndexuint16_t BitInBytebool Bit); 
	

	
int GetPDULength(){ return PDULength; }
	
// Extended functions
#ifdef _EXTENDED
	
int GetDBSize(uint16_t DBNumberuint16_t *Size);
	
int DBGet(uint16_t DBNumbervoid *ptrDatauint16_t *Size);
    
int PlcStart(); // Warm start
    
int PlcStop();
    
int GetPlcStatus(int *Status);
	
int IsoExchangeBuffer(uint16_t *Size);
	
void ErrorText(int Errorchar *Textint TextLen);
#endif
};

extern TPDU PDU;

#endif // SETTIMINO_H

TIA üzerindeki programı yükleme gereği duymadım. ama ihtiyaç olursa yükleyebilirim. Normal şartlarda arduino plc db'lerine yazıyor ya da okuyabiliyor. Ama plc get/put komutları ile işlem yapamıyorum.

Yapmak istediğim plc üzerinden get komutu ile x bir işlemciden profinet ile registerları okuyabilmek.
Bildiğini paylaşmak, Allah'ın verdiği öğrenme yeteneğinin zekatıdır.

yufuk

Arduino'yu slave yapacaksan modbus tcp ile haberleştirsen daha kolay olur bence. İnternette hazır kütüphaneleri var. Ben denedim oldu. Arduinodan PLC ya da Scada ile veri çekebiliyorum.

gokhangokcen

@yufuk siemens plc üzerinde modbus maalesef bulunmuyor. Genelde profinet ya da profibus bulunuyor. Bunun için ekstra modül almam gerekecek. Benim amacım profinet üzerinden haberleşmek.
Bildiğini paylaşmak, Allah'ın verdiği öğrenme yeteneğinin zekatıdır.

sımışka

Profinet ve modbus application layerdır. Modül almanıza gerek yok, her ikiside ethernet altyapısını kullanmakta. Ürünüzün datasheetine baktım.

Further protocols
● MODBUS Yes; MODBUS TCP


TIA Portal da Get/Put blokları eklediğin yerin alt kısmında modbus master/slave blokları mevcut.
Stm ile s7 1200 arasında bu şekilde bağlantı kurdum, holding register alanından okudum yazdım.
Port sayın bir adetse plc üzerinde, bir adet yönetilmez switch ile hem debug yapabilirsin hemde modbus üzerinden haberleşebilirsin.

gokhangokcen

@sımışka CPU kodu tam olarak SIPLUS ET 200SP CPU 1510SP-1 PN RAIL CPU link ben mi bulamadım modbusTCP olarak? yanlışım varsa düzeltirsen sevinirim.

 
Bildiğini paylaşmak, Allah'ın verdiği öğrenme yeteneğinin zekatıdır.


gokhangokcen

@yufuk evet üstad dediğin gibi modbusTCP varmış. Direkt oradan hallettim artık. Aynı anda hem modbusTCP hemde Profinet aynı port üzerinden kullanılabiliyor onuda denemiş oldum.

Profinet için yine de uğraşacağım elimde hazır kütüphane dursun. Birde merak ettiğim şu. Profinet haberleşmeyi kullanan ticari bir ürün ürettiğimde herhangi bir yere kayıt yaptırma ya da bedel ödememe gerek var mı? Malum profinet açık kaynak kodlu bir haberleşme değil. Siemens'e has bir haberleşme.
Bildiğini paylaşmak, Allah'ın verdiği öğrenme yeteneğinin zekatıdır.

yufuk

Bu konuda bilgim yok. Ben de merak ettim.

sımışka

Alıntı yapılan: gokhangokcen - 07 Temmuz 2021, 12:34:42@yufuk evet üstad dediğin gibi modbusTCP varmış. Direkt oradan hallettim artık. Aynı anda hem modbusTCP hemde Profinet aynı port üzerinden kullanılabiliyor onuda denemiş oldum.

Profinet için yine de uğraşacağım elimde hazır kütüphane dursun. Birde merak ettiğim şu. Profinet haberleşmeyi kullanan ticari bir ürün ürettiğimde herhangi bir yere kayıt yaptırma ya da bedel ödememe gerek var mı? Malum profinet açık kaynak kodlu bir haberleşme değil. Siemens'e has bir haberleşme.

Profinet için hayır para vs ödemenize gerek yok. Cihazınızın MAC id si için uygulama yapacağınız yere bağlı olarak evet. Kapalı bir ortamda çalışan bir network ve dışarısı ile bağlantı yoksa sorun olmaz. Aksi takdirde problem oluşabilir.

gokhangokcen

@sımışka üstad mac için ben şu microchip'in satmış olduğu 25AA02E48 eepromu var içerisinde gömülü mac adresi var direkt olarak alıp kullanıyorum. Tavsiye ederim. IP ve gerekli bilgileri kaydetmek içinde eeprom faydalı oluyor. Profinet, EthernetIP, EtherCAT layerlerini internette detaylıca anlatan bir yer göremedim. Acaba bulan var mı? modbusTCP vs gibi protokollerin istemediğiniz kadar örneği yapısı var.
Bildiğini paylaşmak, Allah'ın verdiği öğrenme yeteneğinin zekatıdır.

sımışka

Alıntı yapılan: gokhangokcen - 07 Temmuz 2021, 19:47:31@sımışka üstad mac için ben şu microchip'in satmış olduğu 25AA02E48 eepromu var içerisinde gömülü mac adresi var direkt olarak alıp kullanıyorum. Tavsiye ederim. IP ve gerekli bilgileri kaydetmek içinde eeprom faydalı oluyor. Profinet, EthernetIP, EtherCAT layerlerini internette detaylıca anlatan bir yer göremedim. Acaba bulan var mı? modbusTCP vs gibi protokollerin istemediğiniz kadar örneği yapısı var.
https://www.profibus.com/download/profinet-specification

gokhangokcen

Alıntı yapılan: sımışka - 08 Temmuz 2021, 13:18:36https://www.profibus.com/download/profinet-specification
üstad linke kaydolmak için siemense kayıtlı firmalardan biri olmak gerekiyor sanırım? bir türlü kayıt olamadım.
Bildiğini paylaşmak, Allah'ın verdiği öğrenme yeteneğinin zekatıdır.

Yasal Uyarı: Picproje.org sitemizde 5651 sayılı kanunun 8. maddesine ve T.C.Knın 125. maddesine göre tüm üyelerimiz yaptıkları paylaşımlardan kendileri sorumludur. Picproje.org hakkında yapılacak tüm hukuksal şikayetleri İletişim sayfamızdan bize bildirdikten en geç 3 (üç) iş günü içerisinde ilgili kanunlar ve yönetmelikler çerçevesinde tarafımızca incelenerek gereken işlemler yapılacak ve site yöneticilerimiz tarafından bilgi verilecektir.