Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
Hand tools and fabrication machines | ||||||
| ||||||
|
Here is the story, I put short video on youtube:
Many answers I have found on Siemens forum, link:
https://support.industry.siemens.com/tf/ww/en/threads/303/?page=0&pageSize=10
1. I have download latest SD image for IOT2020 ( V2.1.3) and with Win32DiskImager.exe put it to SD card
2. I want to have fixed IP adress, so I connect with putty to adress 192.168.200.1, PC must be in same adress range.
So I must to edit file interfaces with command:
nano /etc/network/interfaces
and put this lines inside:
# /etc/network/interfaces -- configuration file for ifup(8), ifdown(8)
# The loopback interface
auto lo
iface lo inet loopback
# Wired interfaces
auto eth0
iface eth0 inet static
address 192.168.1.20
netmask 255.255.255.0
gateway 192.168.1.1
dnd-nameservers 8.8.8.8
#auto eth1
#iface eth1 inet dhcp
Save it with CTRL-X ( Yes), and reboot with command:
reboot
Now I have fixed adress 192.168.1.20 but internet was not working, I must do some changes to file:
/etc/resolv.conf
> nano /etc/resolv.conf
#remove symbolic link /etc/resolv.conf
> unlink /etc/resolv.conf
#create new/resolv.conf file with following content:
nameserver 8.8.8.8
nameserver 8.8.4.4
Save it with CTRL-X ( Yes),
# made /etc/resolv.conf immutable after reboot with
> chattr +i /etc/resolv.conf
Now I have fixed address and internet is working on IOT2020.
3. I want to make all with nodered, I have tried arduino part and it is working good with arduino 4 relay schield. So I first disable nodered service with command:
iot2000setup and reboot.
Install some nodered software ( s7, modbus, sqlite, knx ):
Install s7:
cd /usr/lib/node_module
npm install node-red-contrib-s7
And with npm install node_name others, now I have this:
4. First I made communication between IOT2020 (192.168.1.20) and Logo 8 ( 192.168.1.22 )
I wrote small program with Logosoft comfort 8.1 ( software for program LOGO)
This is network part:
Logo have VM ( memory area 0-850 bytes) to store variables ( bit, bytes, integer, word and double word). I use this for transfer data between IOT2020 and Logo8.
I have also connect ESP 8266 module with DTH22 temperature and humidity sensor to LOGO.
5. ESP 8266 module with DTH22 temperature and humidity sensor
Here is schematic:
ESP I powered locally to 5V/2A power supply for mobile phone over micro usb, and to sensor only 3 wires. I have to program ESP, I use Arduino IDE, put some additional library for DHT22 and settimino.lib for S7 communication. ESP is connected wirelessly to my home router ( 192.168.1.1), it measure temp. and humidity and send it over S7 protocol to Logo 8 VM area ( bytes 771-776). You have all 3 files to program ESP with arduino IDE, all must be in same folder.
I have also tested IOT2020 arduino part with small programm for 4 relay schield.
6. Next is nodered part in IOT2020, I use S7 input node to read and S7 output node to write data to/from LOGO 8.
After that I use some dashboard nodes to visualize temperature, humidity, logo 8- 4 relay outputs and IOT2020 arduino 4 relay schield outputs.
Here I can change temperature setpoint with slider ( S7 ouputs value to logo 8), logo receive data from ESP (temperature, humidity- I read it with S7 input node), display with bargraf and graphic history.
Logo program is used to turn on relays on logo( 4 relays) and after that on IOT2020 (Arduino 4 relay shield), you can see that on youtube video. Output stages is delayed between with time delay block in logoconfort programm.
You can also see logo virtual display via web browser:
Here is small video:
7. I want to store data in standard database format in IOT2020. I install node sqlite, after that I want to install apache2 web server and PHP to start database browser but without sucsess, so I installed http://sqlitebrowser.org/ on windows machine. With it I create database and with scp upload to IOT. My sqlite node write data to database, i download database to PC and open with sqlitebrowser or firefox with sqlite plugin.
8. Next step is to transfer data to cloud, i deside to microsoft azure IOT platform
You must install azureiothubnode:
cd /usr/lib/node_modules
npm install -g node-red-contrib-azureiothubnode
ESP 8266 S7 code
Arduino/***
* Daten zur SPS schicken
*
* Infos: https://github.com/esp8266/Arduino/blob/master/doc/libraries.md
*
* Deep sleep mode:
* https://learn.sparkfun.com/tutorials/esp8266-thing-hookup-guide/example-sketch-goodnight-thing-sleep-mode
*
* der XPD pin ist GP16
* (http://www.esp8266.com/viewtopic.php?f=13&t=3194)
*/
#define DEBUG
#ifdef DEBUG
#define log(x) Serial.println(x)
#else
#define log(x)
#endif
#include <DHT.h>
#include <esp.h>
#include <user_interface.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include "Settimino.h"
#include <ESP8266httpUpdate.h>
//extern "C" {
//#include "user_interface.h"
//ADC_MODE(ADC_VCC);//Ist notwendig, damit der ESP seine Betreibsspannung messen kann
//}
#define ESP_S7_VERSION "V1.6"
//extern "C" {
// ADC_MODE(ADC_VCC);
//}
/*********************************
* Wifi Settings
*********************************/
const char* WiFiSSID = "ivekPivek";
const char* WiFiPSK = "26081957";
WiFiClient client;
/*********************************
* Feuchte Sensor
*********************************/
#define DHTPIN 5 // what pin we're connected to
DHT dht(DHTPIN, DHT22, 15);
/*********************************
* PLC PLC PLC PLC
*********************************/
S7Client s7;
#define S7ADDRESS 192,168,1,22 //Adress of my logo
//TSAP Format auf der Logo: hier muss einfach nur der punkt weggelassen werden
#define S7LocalTSAP 0x0200 //entspricht den auf der Logo eingetragener Partner TSAP
#define S7RemoteTSAP 0x0300 //entspricht den auf der Logo eingetragener lokalen TSAP,dh TSAP der Logo
#define S7DBNR 1 //logo verwendet immer DB 0
#define S7AREA S7AreaDB
#define S7STARTPOS 771 //wir schreiben die Daten ab position 771
//#define S7LENGTH FVVVVVVV
/*********************************
* Sleeping
*********************************/
// Time to sleep (in microseconds):
const unsigned long SLEEP_TIME = 20 * 1000000; //20sekunden
/********************************
* Global Variablen
********************************/
//variables for the sensor
//set them to useless values
#define UNKNOWN 999.9;
float vTemperature = UNKNOWN
;
float vHumidity = UNKNOWN
;
float vVcc = 0;
/************************************
* main function
************************************/
//The setup function is called once at startup of the sketch
void setup() {
ESP.wdtEnable(WDTO_500MS); //500ms Watchdog
initHardware();
ESP.wdtFeed();
getAllData();
ESP.wdtFeed();
connectWiFi();
ESP.wdtFeed();
if (checkFirmwareUpdate()) {
ESP.wdtFeed();
sendData2PLC();
ESP.wdtFeed();
}
deconnectWifi();
ESP.wdtFeed();
#ifdef DEBUG
Serial.println("");
Serial.println("==============================================");
Serial.print("main Loop end --> going into deep sleep for ");
Serial.print((unsigned long) (SLEEP_TIME / 1000000));
Serial.println("s");
Serial.println("==============================================");
delay(100);
#endif
//lets go sleeping
// deepSleep time is defined in microseconds
ESP.deepSleep(SLEEP_TIME);
}
// The loop function will not entered because of the deep sleep mode
void loop() {
}
/************************************
* Get the data from the sensores
************************************/
void getAllData() {
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
#ifdef DEBUG
log(F("Failed to read from DHT sensor!"));
log(F("Humidity = Temperature = -100"));
#endif
vHumidity = UNKNOWN
;
vTemperature = UNKNOWN
;
} else {
#ifdef DEBUG
Serial.println(F("\n===============DHT========================="));
Serial.print("DHT: received Sensor data: Humidity=");
Serial.print(h, 1);
Serial.print("%, Temperature=");
Serial.print(t, 1);
Serial.println(" deg");
#endif
vHumidity = h;
vTemperature = t;
}
}
/************************************
* Send Data to PLC
************************************/
void sendData2PLC() {
#ifdef DEBUG
Serial.println(F("\n===============PLC========================="));
#endif
//Connect to PLC
if (!connect2PLC()) {
ESP.wdtFeed();
//transmitt Data
transmittData2PLC();
//disconnectFromPLC
ESP.wdtFeed();
disConnectFromPLC();
ESP.wdtFeed();
}
}
/************************************
* Transmitt Data to PLC
************************************/
void transmittData2PLC() {
byte buffer[8];
signed int t = round(vTemperature * 10);
unsigned int h = round(vHumidity * 10);
unsigned int v = round(analogRead(0) / 812.0 * 4.86 * 1000);
//byte HI/LO
//Put temperature into puffer
buffer[0] = (t >> 8) & 0xFF;
buffer[1] = t & 0xFF;
buffer[2] = (h >> 8) & 0xFF;
buffer[3] = h & 0xFF;
buffer[4] = (v >> 8) & 0xFF;
buffer[5] = v & 0xFF;
//buffer[4] = 0xFF;//Testing purpose
int res = s7.WriteArea( S7AREA, S7DBNR, S7STARTPOS, 6, buffer);
#ifdef DEBUG
if (res) {
log(String("Error while sending data: ") + String(res));
} else {
log(String("Data sent successfully to PLC"));
}
#endif
}
/************************************
* disconnect from PLC
************************************/
void disConnectFromPLC() {
s7.Disconnect();
log(String("disconnected from PLC"));
}
/************************************
* connect to PLC
************************************/
int connect2PLC() {
s7.SetConnectionParams(IPAddress(S7ADDRESS), S7LocalTSAP, S7RemoteTSAP);
//s7.SetConnectionType(uint16_t ConnectionType); we use the defaut connection type
int res = s7.Connect();
#ifdef DEBUG
if (res) {
log(String("Could not connect to PLC: ") + String(res));
} else {
log(String("successfully connected to PLC"));
}
#endif
return res;
}
/************************************
* close connection
************************************/
void deconnectWifi() {
//diconnect and switch off wifi
WiFi.disconnect(true);
}
/************************************
* connect to wlan not to host
************************************/
void connectWiFi() {
#ifdef DEBUG
Serial.println(F("\n===============WIFI========================="));
log(String("connecting to ") + String(WiFiSSID));
#endif
// Set WiFi mode to station (as opposed to AP or AP_STA)
WiFi.mode(WIFI_STA);
// WiFI.begin([ssid], [passkey]) initiates a WiFI connection
// to the stated [ssid], using the [passkey] as a WPA, WPA2,
// or WEP passphrase.
WiFi.begin(WiFiSSID, WiFiPSK);
// Use the WiFi.status() function to check if the ESP8266
// is connected to a WiFi network.
while (WiFi.status() != WL_CONNECTED) {
// Blink the LED
// Delays allow the ESP8266 to perform critical tasks
// defined outside of the sketch. These tasks include
// setting up, and maintaining, a WiFi connection.
ESP.wdtFeed();
delay(100);
#ifdef DEBUG
Serial.print(".");
#endif
// Potentially infinite loops are generally dangerous.
// Add delays -- allowing the processor to perform other
// tasks -- wherever possible.
}
#ifdef DEBUG
Serial.println(F("WiFi connected"));
Serial.print(F("IP address: "));
Serial.println(WiFi.localIP());
Serial.print(F("MAC address: "));
Serial.println(WiFi.macAddress());
Serial.print(F("Vcc: "));
int vcc = analogRead(0);
Serial.print(vcc);
Serial.print(F("digits, "));
Serial.print(vcc / 823.0 * 5.0, 3);
Serial.println(F("V"));
#endif
}
/************************************
* initialisize Hardware
************************************/
void initHardware() {
#ifdef DEBUG
Serial.begin(115200);
// Serial.setDebugOutput(true);
Serial.println(F("\n=================INIT==========================="));
Serial.print(F("Version: "));
Serial.println(F(ESP_S7_VERSION));
Serial.println();
Serial.print(F("Heap: "));
Serial.println(ESP.getFreeHeap());
Serial.print(F("Boot Vers: "));
Serial.println(ESP.getBootVersion());
Serial.print(F("CPU: "));
Serial.println(ESP.getCpuFreqMHz());
Serial.print(F("SDK: "));
Serial.println(ESP.getSdkVersion());
Serial.print(F("Chip ID: "));
Serial.println(ESP.getChipId());
Serial.print(F("Flash ID: "));
Serial.println(ESP.getFlashChipId());
Serial.print(F("Flash Size: "));
Serial.println(ESP.getFlashChipSizeByChipId());
Serial.print(F("Vcc: "));
int vcc = analogRead(0);
Serial.print(vcc);
Serial.print(F("digits, "));
Serial.print(vcc / 823.0 * 5.0, 3);
Serial.println(F("V"));
#endif
dht.begin();
}
bool checkFirmwareUpdate() {
//------------------------------------Config File
/*
Serial.println("Update SPIFFS...");
t_httpUpdate_return ret = ESPhttpUpdate.updateSpiffs(
"https://server/spiffs.bin");
if (ret == HTTP_UPDATE_OK) {
Serial.println("Update sketch...");
ret = ESPhttpUpdate.update("https://server/file.bin");
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s",
ESPhttpUpdate.getLastError(),
ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
*/
//------------------------------------Firmware
#ifdef DEBUG
Serial.println(
F("\n=================HTTP UPDATE==========================="));
#endif
t_httpUpdate_return ret = ESPhttpUpdate.update(
"http://10.0.0.142/esp/update/sensors.php", ESP_S7_VERSION);
// t_httpUpdate_return ret = ESPhttpUpdate.update("10.0.0.142", 80, "/esp/update/sensors.php", ESP_S7_VERSION,false);
switch (ret) {
case HTTP_UPDATE_FAILED:
#ifdef DEBUG
// Serial.println("[update] Update failed.");
Serial.printf("Update: Error (%d): %s --> Failed\n",
ESPhttpUpdate.getLastError(),
ESPhttpUpdate.getLastErrorString().c_str());
#endif
return true;
break;
case HTTP_UPDATE_NO_UPDATES:
#ifdef DEBUG
Serial.println(F("Update: no Update --> OK."));
#endif
return true;
break;
case HTTP_UPDATE_OK:
#ifdef DEBUG
Serial.println(F("Update: Update --> ok.")); // may not called we reboot the ESP
return false;
#endif
break;
}
return true;
}
Settimino.h
Arduino/*=============================================================================|
| PROJECT SETTIMINO 1.0.0 |
|==============================================================================|
| Copyright (C) 2013, 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/ |
|=============================================================================*/
#ifndef SETTIMINO_H
#define SETTIMINO_H
// Memory models
// _SMALL
// _NORMAL
// _EXTENDED
#define _EXTENDED
#if defined(_NORMAL) || defined(_EXTENDED)
# define _S7HELPER
#endif
#include "WiFiClient.h"
#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
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 struct{
byte H[Size_WR]; // PDU Header
byte DATA[MaxPduSize-Size_WR+Shift]; // PDU Data
}TPDU;
#pragma pack()
#ifdef _S7HELPER
class S7Helper
{
public:
bool BitAt(void *Buffer, int ByteIndex, byte BitIndex);
bool BitAt(int ByteIndex, int BitIndex);
byte ByteAt(void *Buffer, int index);
byte ByteAt(int index);
word WordAt(void *Buffer, int index);
word WordAt(int index);
dword DWordAt(void *Buffer, int index);
dword DWordAt(int index);
float FloatAt(void *Buffer, int index);
float FloatAt(int index);
integer IntegerAt(void *Buffer, int index);
integer IntegerAt(int index);
long DintAt(void *Buffer, int index);
long DintAt(int index);
};
extern S7Helper S7;
#endif // _S7HELPER
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;
WiFiClient TCPClient;
int PDULength; // PDU Length negotiated
int IsoPduSize();
int WaitForData(uint16_t Size, uint16_t Timeout);
int RecvISOPacket(uint16_t *Size);
int RecvPacket(uint8_t *buf, uint16_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();
// Basic functions
void SetConnectionParams(IPAddress Address, uint16_t LocalTSAP, uint16_t RemoteTSAP);
void SetConnectionType(uint16_t ConnectionType);
int ConnectTo(IPAddress Address, uint16_t Rack, uint16_t Slot);
int Connect();
void Disconnect();
int ReadArea(int Area, uint16_t DBNumber, uint16_t Start, uint16_t Amount, void *ptrData);
int WriteArea(int Area, uint16_t DBNumber, uint16_t Start, uint16_t Amount, void *ptrData);
int GetPDULength(){ return PDULength; }
// Extended functions
#ifdef _EXTENDED
int GetDBSize(uint16_t DBNumber, uint16_t *Size);
int DBGet(uint16_t DBNumber, void *ptrData, uint16_t *Size);
int PlcStart(); // Warm start
int PlcStop();
int GetPlcStatus(int *Status);
int IsoExchangeBuffer(uint16_t *Size);
void ErrorText(int Error, char *Text, int TextLen);
#endif
};
extern TPDU PDU;
#endif // SETTIMINO_H
C++ part of code
C/C++/*=============================================================================|
| PROJECT SETTIMINO 1.0.0 |
|==============================================================================|
| Copyright (C) 2013, 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/ |
|=============================================================================*/
#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[] = {
0x03, 0x00, 0x00, 0x19, 0x02, 0xf0, 0x80, // TPKT + COTP (see above for info)
0x32, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x08, 0x00,
0x00, 0xf0, 0x00, 0x00, 0x01, 0x00, 0x01,
0x00, 0xf0 // 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
0x03, 0x00,
0x00, 0x1f, // Telegram Length (Data Size + 31 or 35)
0x02, 0xf0, 0x80, // COTP (see above for info)
0x32, // S7 Protocol ID
0x01, // Job Type
0x00, 0x00, // Redundancy identification
0x05, 0x00, // PDU Reference
0x00, 0x0e, // Parameters Length
0x00, 0x00, // 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
0x00,0x00, // Num Elements
0x00,0x00, // DB Number (if any, else 0)
0x84, // Area Type
0x00, 0x00, 0x00, // Area Offset
// WR area
0x00, // Reserved
0x04, // Transport size
0x00, 0x00, // 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[] = {
0x03, 0x00, 0x00, 0x25, 0x02, 0xf0, 0x80, 0x32,
0x07, 0x00, 0x00, 0x05, 0x00, 0x00, 0x08, 0x00,
0x0c, 0x00, 0x01, 0x12, 0x04, 0x11, 0x43, 0x03,
0x00, 0xff, 0x09, 0x00, 0x08, 0x30, 0x41,
0x30, 0x30, 0x30, 0x30, 0x30, // ASCII DB Number
0x41
};
// S7 Put PLC in STOP state Request Header (contains also ISO Header and COTP Header)
byte S7_STOP[] = {
0x03, 0x00, 0x00, 0x21, 0x02, 0xf0, 0x80, 0x32,
0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00,
0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
0x50, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41,
0x4d
};
// S7 Put PLC in RUN state Request Header (contains also ISO Header and COTP Header)
byte S7_START[] = {
0x03, 0x00, 0x00, 0x25, 0x02, 0xf0, 0x80, 0x32,
0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x14, 0x00,
0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfd, 0x00, 0x00, 0x09, 0x50, 0x5f, 0x50, 0x52,
0x4f, 0x47, 0x52, 0x41, 0x4d
};
// S7 Get PLC Status Request Header (contains also ISO Header and COTP Header)
byte S7_PLCGETS[] = {
0x03, 0x00, 0x00, 0x21, 0x02, 0xf0, 0x80, 0x32,
0x07, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x08, 0x00,
0x08, 0x00, 0x01, 0x12, 0x04, 0x11, 0x44, 0x01,
0x00, 0xff, 0x09, 0x00, 0x04, 0x04, 0x24, 0x00,
0x00
};
#endif // _EXTENDED
TPDU PDU;
#ifdef _S7HELPER
S7Helper S7;
//-----------------------------------------------------------------------------
bool S7Helper::BitAt(void *Buffer, int ByteIndex, byte BitIndex)
{
byte mask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
pbyte Pointer = pbyte(Buffer) + ByteIndex;
if (BitIndex>7)
return false;
else
return (*Pointer & mask[BitIndex]);
}
//-----------------------------------------------------------------------------
bool S7Helper::BitAt(int ByteIndex, int BitIndex)
{
return BitAt(&PDU.DATA[0], ByteIndex, BitIndex);
}
//-----------------------------------------------------------------------------
byte S7Helper::ByteAt(void *Buffer, int index)
{
pbyte Pointer = pbyte(Buffer) + index;
return *Pointer;
}
//-----------------------------------------------------------------------------
byte S7Helper::ByteAt(int index)
{
return ByteAt(&PDU.DATA, index);
}
//-----------------------------------------------------------------------------
word S7Helper::WordAt(void *Buffer, int index)
{
word hi=(*(pbyte(Buffer) + index))<<8;
return hi+*(pbyte(Buffer) + index+1);
}
//-----------------------------------------------------------------------------
word S7Helper::WordAt(int index)
{
return WordAt(&PDU.DATA, index);
}
//-----------------------------------------------------------------------------
dword S7Helper::DWordAt(void *Buffer, int 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.DATA, index);
}
//-----------------------------------------------------------------------------
float S7Helper::FloatAt(void *Buffer, int index)
{
dword dw = DWordAt(Buffer, index);
return *(pfloat(&dw));
}
//-----------------------------------------------------------------------------
float S7Helper::FloatAt(int index)
{
return FloatAt(&PDU.DATA, index);
}
//-----------------------------------------------------------------------------
integer S7Helper::IntegerAt(void *Buffer, int index)
{
word w = WordAt(Buffer, index);
return *(pinteger(&w));
}
//-----------------------------------------------------------------------------
integer S7Helper::IntegerAt(int index)
{
return IntegerAt(&PDU.DATA, index);
}
//-----------------------------------------------------------------------------
long S7Helper::DintAt(void *Buffer, int index)
{
dword dw = DWordAt(Buffer, index);
return *(pdint(&dw));
}
//-----------------------------------------------------------------------------
long S7Helper::DintAt(int index)
{
return DintAt(&PDU.DATA, index);
}
#endif // _S7HELPER
//-----------------------------------------------------------------------------
S7Client::S7Client()
{
// Default TSAP values for connectiong as PG to a S7300 (Rack 0, Slot 2)
LocalTSAP_HI = 0x01;
LocalTSAP_LO = 0x00;
RemoteTSAP_HI= 0x01;
RemoteTSAP_LO= 0x02;
ConnType = PG;
Connected = false;
LastError = 0;
PDULength = 0;
RecvTimeout = 500; // 500 ms
}
//-----------------------------------------------------------------------------
S7Client::~S7Client()
{
Disconnect();
}
//-----------------------------------------------------------------------------
int S7Client::SetLastError(int Error)
{
LastError=Error;
return Error;
}
//-----------------------------------------------------------------------------
int S7Client::WaitForData(uint16_t Size, uint16_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 *buf, uint16_t Size)
{
WaitForData(Size,RecvTimeout);
if (LastError!=0)
return LastError;
if (TCPClient.read(buf, Size)==0)
return SetLastError(errTCPConnectionReset);
return SetLastError(0);
}
//-----------------------------------------------------------------------------
void S7Client::SetConnectionParams(IPAddress Address, uint16_t LocalTSAP, uint16_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 Address, uint16_t Rack, uint16_t Slot)
{
SetConnectionParams(Address, 0x0100, (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(Peer, isotcp))
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.H, 4);
if (LastError==0)
{
*Size = IsoPduSize();
// Check 0 bytes Data Packet (only TPKT+COTP - 7 bytes)
if (*Size==7)
RecvPacket(PDU.H, 3); // 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.H, 3); // 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((const uint8_t *) &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((const uint8_t *) &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.H[35];
PDULength = (PDULength<<8) + PDU.H[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 Area, uint16_t DBNumber, uint16_t Start, uint16_t Amount, void *ptrData)
{
unsigned long Address;
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;
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.H, S7_RW, Size_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 ((Area==S7AreaCT) || (Area==S7AreaTM))
{
Address = Start;
if (Area==S7AreaCT)
PDU.H[22]=S7WLCounter;
else
PDU.H[22]=S7WLTimer;
}
else
Address = Start<<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((const uint8_t *) &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;
Start += NumElements*WordSize;
}
return LastError;
}
//-----------------------------------------------------------------------------
int S7Client::WriteArea(int Area, uint16_t DBNumber, uint16_t Start, uint16_t Amount, void *ptrData)
{
unsigned long Address;
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;
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.H, S7_RW, Size_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 ((Area==S7AreaCT) || (Area==S7AreaTM))
{
Address = Start;
Length = DataSize;
if (Area==S7AreaCT)
PDU.H[22]=S7WLCounter;
else
PDU.H[22]=S7WLTimer;
}
else
{
Address = Start<<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;
// Length
PDU.H[33]=Length>>8;
PDU.H[34]=Length & 0x00FF;
// Copy data
Source=pbyte(ptrData)+Offset;
if (ptrData!=NULL)
memcpy(&PDU.H[35], Source, DataSize);
if (TCPClient.write((const uint8_t *) &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;
Start += NumElements*WordSize;
}
return LastError;
}
//-----------------------------------------------------------------------------
#ifdef _EXTENDED
int S7Client::GetDBSize(uint16_t DBNumber, uint16_t *Size)
{
uint16_t Length;
LastError=0;
*Size=0;
// Setup the telegram
memcpy(&PDU.H, S7_BI, sizeof(S7_BI));
// Set DB Number
PDU.H[31]=(DBNumber / 10000)+0x30;
DBNumber=DBNumber % 10000;
PDU.H[32]=(DBNumber / 1000)+0x30;
DBNumber=DBNumber % 1000;
PDU.H[33]=(DBNumber / 100)+0x30;
DBNumber=DBNumber % 100;
PDU.H[34]=(DBNumber / 10)+0x30;
DBNumber=DBNumber % 10;
PDU.H[35]=(DBNumber / 1)+0x30;
if (TCPClient.write((const uint8_t *) &PDU.H[0], sizeof(S7_BI))==sizeof(S7_BI))
{
RecvISOPacket(&Length);
if (LastError==0)
{
if (Length>25) // 26 is the minimum expected
{
if ((PDU.H[37]==0x00) && (PDU.H[38]==0x00) && (PDU.H[39]==0xFF))
{
*Size=PDU.H[83];
*Size=(*Size<<8)+PDU.H[84];
}
else
LastError = errS7Function;
}
else
LastError = errS7InvalidPDU;
}
}
else
LastError = errTCPDataSend;
return LastError;
}
//-----------------------------------------------------------------------------
int S7Client::DBGet(uint16_t DBNumber, void *ptrData, uint16_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(S7AreaDB, DBNumber, 0, Length, ptrData);
if (Result==0)
*Size=Length;
}
else
Result=errBufferTooSmall;
}
return Result;
}
//-----------------------------------------------------------------------------
int S7Client::PlcStop()
{
uint16_t Length;
LastError=0;
// Setup the telegram
memcpy(&PDU.H, S7_STOP, sizeof(S7_STOP));
if (TCPClient.write((const uint8_t *) &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.H, S7_START, sizeof(S7_START));
if (TCPClient.write((const uint8_t *) &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.H, S7_PLCGETS, sizeof(S7_PLCGETS));
if (TCPClient.write((const uint8_t *) &PDU.H[0], sizeof(S7_PLCGETS))==sizeof(S7_PLCGETS))
{
RecvISOPacket(&Length);
if (LastError==0)
{
if (Length>53) // 54 is the minimum expected
{
switch (PDU.H[54])
{
case S7CpuStatusUnknown :
case S7CpuStatusRun :
case S7CpuStatusStop : *Status=PDU.H[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((const uint8_t *) &PDU.H[0], int(Size))==*Size)
RecvISOPacket(Size);
else
LastError = errTCPDataSend;
return LastError;
}
//-----------------------------------------------------------------------------
void S7Client::ErrorText(int Error, char *Text, int TextLen)
{
}
#endif // _EXTENDED
//-----------------------------------------------------------------------------
No preview (download only).
/*4-Relays Shield Example*/
//define variable
int RELAY1 = 4;
int RELAY2 = 7;
int RELAY3 = 8;
int RELAY4 = 12;
void setup()
{
//set Relays as Output
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);
pinMode(RELAY3, OUTPUT);
pinMode(RELAY4, OUTPUT);
}
void loop()
{
digitalWrite(RELAY1,HIGH); // Turns ON Led1
delay(1000); // Wait 1 seconds
digitalWrite(RELAY2,HIGH); // Turns ON Led2
delay(1000); // Wait 1 seconds
digitalWrite(RELAY3,HIGH); // Turns ON Led3
delay(1000); // Wait 1 seconds
digitalWrite(RELAY4,HIGH); // Turns ON Led4
delay(1000); // Wait 1 seconds
digitalWrite(RELAY4,LOW); // Turns OFF Led4
delay(1000); // Wait 1 seconds
digitalWrite(RELAY3,LOW); // Turns OFF Led3
delay(1000); // Wait 1 seconds
digitalWrite(RELAY2,LOW); // Turns OFF Led2
delay(1000); // Wait 1 seconds
digitalWrite(RELAY1,LOW); // Turns OFF Led1
delay(1000); // Wait 1 seconds
}
Nodered code IOT2020 and LOGO 8 - relays and ESP 8266 temp. and humidity
Plain text[{"id":"6708ea81.dd6f9c","type":"tab","label":"LOGO + IOT2020 8 OUTPUTS ESP T,H"},{"id":"40b9073.8b400f8","type":"debug","z":"6708ea81.dd6f9c","name":"Debug","active":true,"console":"false","complete":"payload","x":490,"y":35,"wires":[]},{"id":"11211bde.9d2354","type":"s7 in","z":"6708ea81.dd6f9c","endpoint":"2e357d78.85a42a","mode":"all-split","variable":"Word","diff":true,"name":"S7 Read","x":152,"y":78,"wires":[["cc2ec8d4.ae5118","40b9073.8b400f8","84b4aeaa.8de73"]]},{"id":"cc2ec8d4.ae5118","type":"function","z":"6708ea81.dd6f9c","name":"","func":"//if (msg.payload <= 0)\n//msg.filename = 'S7Log2.txt';\n//else\n//{\n var StorageLocation = '/home/LogFiles/';\n var Filename = 'MyDatalog';\n var now = new Date();\n var Day = now.getDate();\n var Month = now.getMonth();\n var Year = now.getFullYear();\n var Hour = now.getHours();\n var Minute = now.getMinutes();\n var Second = now.getSeconds();\n var value = msg.payload;\n var name = msg.topic;\n Month = Month + 1;\n Filename = StorageLocation + Filename;\nmsg.filename = Filename;\nmsg.payload = \"Variable: \" + name + \" Value: \" + value + ' Timestamp: ' + Year + '.' + Day + '.' + Month + '_' + Hour + ':' + Minute +':' + Second; \n//}\nreturn msg;\n","outputs":1,"noerr":0,"x":412,"y":98.63999938964844,"wires":[["ea193cf6.cdbe18"]]},{"id":"ea193cf6.cdbe18","type":"file","z":"6708ea81.dd6f9c","name":"","filename":"","appendNewline":true,"createDir":true,"overwriteFile":"false","x":628,"y":81.18003845214844,"wires":[]},{"id":"84b4aeaa.8de73","type":"switch","z":"6708ea81.dd6f9c","name":"Display switch","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"LOGO Q1","vt":"str"},{"t":"eq","v":"LOGO Q2","vt":"str"},{"t":"eq","v":"LOGO Q3","vt":"str"},{"t":"eq","v":"LOGO Q4","vt":"str"},{"t":"eq","v":"NQ1","vt":"str"},{"t":"eq","v":"NQ2","vt":"str"},{"t":"eq","v":"NQ3","vt":"str"},{"t":"eq","v":"NQ4","vt":"str"},{"t":"eq","v":"Word 0","vt":"str"},{"t":"eq","v":"Word 2","vt":"str"}],"checkall":"true","outputs":10,"x":155,"y":430,"wires":[["d6053e7e.032848"],["c1684a30.d22fd8"],["c6fd415e.b060b8"],["fc36fb06.3c444"],["247b9774.f3a7b"],["4957ce30.7ae628"],["5a6284bd.db3644"],["f98fa190.0c04b8"],["9c77575c.7d733"],["fb09a37e.6a674"]]},{"id":"238b2347.711b3c","type":"mraa-gpio-dout","z":"6708ea81.dd6f9c","name":"NQ1","pin":"4","set":true,"level":"0","x":750,"y":374,"wires":[]},{"id":"11338767.ef0a91","type":"mraa-gpio-dout","z":"6708ea81.dd6f9c","name":"NQ2","pin":"7","set":true,"level":"0","x":753,"y":416,"wires":[]},{"id":"516bcb86.72764c","type":"mraa-gpio-dout","z":"6708ea81.dd6f9c","name":"NQ3","pin":"8","set":true,"level":"0","x":753.7509765625,"y":459.3750305175781,"wires":[]},{"id":"b1eaf7d1.a08e38","type":"mraa-gpio-dout","z":"6708ea81.dd6f9c","name":"NQ4","pin":"12","set":true,"level":"0","x":754,"y":503,"wires":[]},{"id":"247b9774.f3a7b","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"NQ1","group":"10ed9ba3.1dab04","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":623,"y":374,"wires":[["238b2347.711b3c"]]},{"id":"4957ce30.7ae628","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"NQ2","group":"10ed9ba3.1dab04","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":621,"y":416,"wires":[["11338767.ef0a91"]]},{"id":"5a6284bd.db3644","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"NQ3","group":"10ed9ba3.1dab04","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":621,"y":458,"wires":[["516bcb86.72764c"]]},{"id":"f98fa190.0c04b8","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"NQ4","group":"10ed9ba3.1dab04","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":621,"y":501,"wires":[["b1eaf7d1.a08e38"]]},{"id":"3eba6341.45be04","type":"s7 out","z":"6708ea81.dd6f9c","endpoint":"9ecde535.565618","variable":"Byte 5","name":"","x":803,"y":715,"wires":[]},{"id":"fa947e51.2134e8","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"START","group":"e2224c4d.d051b","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":401,"y":719,"wires":[["3eba6341.45be04"]]},{"id":"5976e89f.b8b5f","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"RESET","group":"e2224c4d.d051b","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":403,"y":772,"wires":[["4792056d.9d0f74"]]},{"id":"4792056d.9d0f74","type":"s7 out","z":"6708ea81.dd6f9c","endpoint":"29d34aa8.11a4e6","variable":"Byte 6","name":"","x":804,"y":761,"wires":[]},{"id":"1c079b51.37051d","type":"ui_gauge","z":"6708ea81.dd6f9c","name":"TEMPERATURE MEASURED","group":"946372b5.768378","order":4,"width":0,"height":0,"gtype":"gage","title":"Temperature","label":"","format":"{{'%.1f'|sprintf:value}}°C","min":"15","max":"30","colors":["#00b500","#e6e600","#ca3838"],"x":815,"y":599,"wires":[]},{"id":"9c77575c.7d733","type":"function","z":"6708ea81.dd6f9c","name":"","func":"// declare place holder variables\nvar t = msg.payload;\nTemperature = t/10;\nmsg.payload = Temperature;\nreturn msg;","outputs":1,"noerr":0,"x":585,"y":600,"wires":[["1c079b51.37051d","2acf4be7.5f8994","9fa27241.5480d8"]]},{"id":"17426228.6c078e","type":"ui_text","z":"6708ea81.dd6f9c","group":"946372b5.768378","order":6,"width":0,"height":0,"name":"","label":"WORD 0","format":"{{msg.payload}}","layout":"row-spread","x":434,"y":596,"wires":[]},{"id":"1fc6b78f.b97648","type":"ui_text","z":"6708ea81.dd6f9c","group":"946372b5.768378","order":7,"width":0,"height":0,"name":"","label":"WORD 2","format":"{{msg.payload}}","layout":"row-spread","x":431,"y":644,"wires":[]},{"id":"fb09a37e.6a674","type":"function","z":"6708ea81.dd6f9c","name":"","func":"// declare place holder variables\nvar h = msg.payload;\nHumidity = h/10;\nmsg.payload = Humidity;\nreturn msg;\n\n","outputs":1,"noerr":0,"x":578,"y":650,"wires":[["257f92b1.59aef6","a255422f.94faa","9263db90.afe98"]]},{"id":"257f92b1.59aef6","type":"ui_gauge","z":"6708ea81.dd6f9c","name":"HUMIDITY MEASURED","group":"946372b5.768378","order":5,"width":0,"height":0,"gtype":"gage","title":"Humidity","label":"","format":"{{'%.1f'|sprintf:value}}%rH","min":0,"max":"100","colors":["#00b500","#e6e600","#ca3838"],"x":789,"y":652,"wires":[]},{"id":"66f68c47.01626c","type":"ui_slider","z":"6708ea81.dd6f9c","name":"","label":"TEMP SETPOINT SLIDER","group":"946372b5.768378","order":1,"width":0,"height":0,"passthru":true,"topic":"","min":"15","max":"30","step":"1","x":468.3666687011719,"y":866.25,"wires":[["9bd60a34.88bc48","591d32fa.1d81b4"]]},{"id":"9bd60a34.88bc48","type":"s7 out","z":"6708ea81.dd6f9c","endpoint":"24b7fff0.98f6","variable":"TEMP SETPOINT","name":"","x":825.36669921875,"y":867.5,"wires":[]},{"id":"591d32fa.1d81b4","type":"ui_text_input","z":"6708ea81.dd6f9c","name":"","label":"TEMP SETPOINT NUMBER","group":"946372b5.768378","order":2,"width":0,"height":0,"passthru":true,"mode":"text","delay":300,"topic":"","x":724.36669921875,"y":950.75,"wires":[["b36b7d40.fd8158"]]},{"id":"b36b7d40.fd8158","type":"ui_gauge","z":"6708ea81.dd6f9c","name":"TEMP SETPOINT","group":"946372b5.768378","order":3,"width":0,"height":0,"gtype":"gage","title":"TEMP SETPOINT","label":"","format":"{{value}}°C","min":"15","max":"25","colors":["#0000ff","#ffff00","#ca3838"],"x":1007.36669921875,"y":961.25,"wires":[]},{"id":"d6053e7e.032848","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"LOGO Q1","group":"73f5f34d.7f0a7c","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":628.75,"y":187.5,"wires":[[]]},{"id":"c1684a30.d22fd8","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"LOGO Q2","group":"73f5f34d.7f0a7c","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":631.75,"y":231.5,"wires":[[]]},{"id":"c6fd415e.b060b8","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"LOGO Q3","group":"73f5f34d.7f0a7c","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":629.75,"y":274.5,"wires":[[]]},{"id":"fc36fb06.3c444","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"LOGO Q4","group":"73f5f34d.7f0a7c","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":632.75,"y":320.5,"wires":[[]]},{"id":"a255422f.94faa","type":"ui_chart","z":"6708ea81.dd6f9c","name":"Humid.","group":"a1c8f732.933848","order":2,"width":0,"height":0,"label":"Humidity","chartType":"line","legend":"false","xformat":"%H:%M:%S","interpolate":"linear","nodata":"","ymin":"0","ymax":"100","removeOlder":1,"removeOlderUnit":"3600","x":959,"y":694,"wires":[[],[]]},{"id":"2acf4be7.5f8994","type":"ui_chart","z":"6708ea81.dd6f9c","name":"Temp.","group":"a1c8f732.933848","order":1,"width":0,"height":0,"label":"Temperature","chartType":"line","legend":"false","xformat":"%H:%M:%S","interpolate":"linear","nodata":"","ymin":"15","ymax":"30","removeOlder":1,"removeOlderUnit":"86400","x":954,"y":561,"wires":[[],[]]},{"id":"9fa27241.5480d8","type":"function","z":"6708ea81.dd6f9c","name":"","func":"var sql = \"\";\nvar d = new Date();\nvar epoch = d.getTime();\nvar outputs = [];\n\nsql = \"INSERT INTO sensor_data (device,sensor,value,epoch) \" +\n \"VALUES ('esp1','temp',\"+msg.payload+\",\"+epoch+\")\";\noutputs.push({topic:sql}); \n\n\n// Update the status with current timestamp\nvar now = new Date();\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\nnode.status({fill:\"blue\",shape:\"ring\",text:\"Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss}); \n \nreturn [ outputs ];","outputs":1,"noerr":0,"x":248.88888888888889,"y":993.3333333333333,"wires":[["8053c45d.de6258","8eab2196.44b4d"]]},{"id":"8053c45d.de6258","type":"sqlite","z":"6708ea81.dd6f9c","mydb":"e4c92c06.3040b8","name":"","x":480,"y":1058.888888888889,"wires":[[]]},{"id":"bdeec097.1f9c98","type":"function","z":"6708ea81.dd6f9c","name":"","func":"var sql = \"\";\nvar d = new Date();\nvar epoch = d.getTime();\nvar outputs = [];\nsql = \"INSERT INTO sensor_data (device,sensor,value,epoch) \" +\n \"VALUES ('esp1','humid',\"+msg.payload+\",\"+epoch+\")\";\noutputs.push({topic:sql}); \n \n\n// Update the status with current timestamp\nvar now = new Date();\nvar yyyy = now.getFullYear();\nvar mm = now.getMonth() < 9 ? \"0\" + (now.getMonth() + 1) : (now.getMonth() + 1); // getMonth() is zero-based\nvar dd = now.getDate() < 10 ? \"0\" + now.getDate() : now.getDate();\nvar hh = now.getHours() < 10 ? \"0\" + now.getHours() : now.getHours();\nvar mmm = now.getMinutes() < 10 ? \"0\" + now.getMinutes() : now.getMinutes();\nvar ss = now.getSeconds() < 10 ? \"0\" + now.getSeconds() : now.getSeconds();\nnode.status({fill:\"blue\",shape:\"ring\",text:\"Last update: \"+dd + \".\" + mm + \".\" + yyyy + \" \" + hh + \":\" + mmm + \":\" + ss}); \n \nreturn [ outputs ];","outputs":1,"noerr":0,"x":222.22222222222223,"y":1145.5555555555554,"wires":[["3e0c6e18.44f462","b10ad2aa.81215"]]},{"id":"8eab2196.44b4d","type":"debug","z":"6708ea81.dd6f9c","name":"","active":true,"console":"false","complete":"false","x":456.66668701171875,"y":1103.3333740234375,"wires":[]},{"id":"3e0c6e18.44f462","type":"sqlite","z":"6708ea81.dd6f9c","mydb":"e4c92c06.3040b8","name":"","x":478.88888888888886,"y":1160,"wires":[[]]},{"id":"b10ad2aa.81215","type":"debug","z":"6708ea81.dd6f9c","name":"","active":true,"console":"false","complete":"false","x":450,"y":1207.7777777777778,"wires":[]},{"id":"9263db90.afe98","type":"delay","z":"6708ea81.dd6f9c","name":"","pauseType":"delay","timeout":"2","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":120,"y":1071,"wires":[["bdeec097.1f9c98"]]},{"id":"5b260a60.eecb14","type":"knx-out","z":"6708ea81.dd6f9c","name":"siemens ip gateway","controller":"1f6c58f5.7bd5a7","x":536,"y":1360,"wires":[]},{"id":"ab888d63.e8d2c8","type":"function","z":"6708ea81.dd6f9c","name":"","func":"{ \"topic\": \"knx: write\",\n \"payload\": {\n \"srcphy\": \"1.1.11\",\n \"dstgad\": \"0/1/3\",\n \"value\": [0,0]\n }\n}","outputs":1,"noerr":0,"x":303,"y":1357,"wires":[["5b260a60.eecb14","23e23f6f.ae18e"]]},{"id":"bfd2e4fc.be30f8","type":"ui_switch","z":"6708ea81.dd6f9c","name":"","label":"knx","group":"946372b5.768378","order":0,"width":0,"height":0,"passthru":true,"topic":"","style":"","onvalue":"true","onvalueType":"bool","onicon":"","oncolor":"","offvalue":"false","offvalueType":"bool","officon":"","offcolor":"","x":118,"y":1359,"wires":[["ab888d63.e8d2c8"]]},{"id":"23e23f6f.ae18e","type":"debug","z":"6708ea81.dd6f9c","name":"","active":true,"console":"false","complete":"false","x":657,"y":1268,"wires":[]},{"id":"6378fecb.3c24b","type":"azureiothub","z":"6708ea81.dd6f9c","name":"Azure IoT Hub","protocol":"amqp","x":580.7666015625,"y":1483.1667175292969,"wires":[[]]},{"id":"dc03caa3.054858","type":"inject","z":"6708ea81.dd6f9c","name":"","topic":"send number to cloud","payload":"88","payloadType":"num","repeat":"","crontab":"","once":false,"x":290.76666259765625,"y":1486.0166320800781,"wires":[["6378fecb.3c24b"]]},{"id":"2e357d78.85a42a","type":"s7 endpoint","z":"6708ea81.dd6f9c","address":"192.168.1.22","port":"102","rack":"1","slot":"0","cycletime":"500","timeout":"","name":"","vartable":[{"addr":"db1,x1390.0","name":"NQ1"},{"addr":"db1,x1390.1","name":"NQ2"},{"addr":"db1,x1390.2","name":"NQ3"},{"addr":"db1,x1390.3","name":"NQ4"},{"addr":"db1,x1064.0","name":"LOGO Q1"},{"addr":"db1,x1064.1","name":"LOGO Q2"},{"addr":"db1,x1064.2","name":"LOGO Q3"},{"addr":"db1,x1064.3","name":"LOGO Q4"},{"addr":"db1,byte771","name":"Byte 771"},{"addr":"db1,byte772","name":"Byte 772"},{"addr":"db1,byte773","name":"Byte 773"},{"addr":"db1,byte774","name":"Byte 774"},{"addr":"db1,byte775","name":"Byte 775"},{"addr":"db1,byte776","name":"Byte 776"},{"addr":"db1,word0","name":"Word 0"},{"addr":"db1,word2","name":"Word 2"}]},{"id":"10ed9ba3.1dab04","type":"ui_group","z":"","name":"IOT2020 OUPUTS","tab":"9f3b8fa1.34657","order":4,"disp":true,"width":"6"},{"id":"9ecde535.565618","type":"s7 endpoint","z":"","address":"192.168.1.22","port":"102","rack":"1","slot":"0","cycletime":"500","timeout":"1500","name":"","vartable":[{"addr":"db1,byte5","name":"Byte 5"}]},{"id":"e2224c4d.d051b","type":"ui_group","z":"","name":"IOT2020 INPUTS","tab":"9f3b8fa1.34657","order":6,"disp":true,"width":"6"},{"id":"29d34aa8.11a4e6","type":"s7 endpoint","z":"","address":"192.168.1.22","port":"102","rack":"1","slot":"0","cycletime":"500","timeout":"1500","name":"","vartable":[{"addr":"db1,byte6","name":"Byte 6"}]},{"id":"946372b5.768378","type":"ui_group","z":"","name":"ESP","tab":"9f3b8fa1.34657","order":1,"disp":true,"width":"6"},{"id":"24b7fff0.98f6","type":"s7 endpoint","z":"","address":"192.168.1.22","port":"102","rack":"1","slot":"0","cycletime":"500","timeout":"1500","name":"","vartable":[{"addr":"db1,byte10","name":"TEMP SETPOINT"}]},{"id":"73f5f34d.7f0a7c","type":"ui_group","z":"","name":"LOGO 8 OUTPUTS","tab":"9f3b8fa1.34657","order":3,"disp":true,"width":"6"},{"id":"a1c8f732.933848","type":"ui_group","z":"","name":"T H HISTORY","tab":"9f3b8fa1.34657","order":2,"disp":true,"width":"6"},{"id":"e4c92c06.3040b8","type":"sqlitedb","z":"","db":"/home/nodered.db"},{"id":"1f6c58f5.7bd5a7","type":"knx-controller","z":"","ipAddr":"192.168.1.159","ipPort":"3671"},{"id":"9f3b8fa1.34657","type":"ui_tab","z":"","name":"Simatic S7 communication to LOGO 8","icon":"dashboard"}]
Comments