Darko Vranesa
Published © GPL3+

IOT2020 Protocol Converter To Cloud

Integrate Siemens SIMATIC and LOGO and some modbus, bacnet HVAC controllers to cloud, prepare data for visualization and standard storage.

IntermediateWork in progress6 hours7,028
IOT2020 Protocol Converter To Cloud

Things used in this project

Hardware components

ESP8266 ESP-12E
Espressif ESP8266 ESP-12E
×1
dth22
temperature and humidity sensor
×1
Siemens LOGO 8
Small smart relay
×1
Arduino 4 relay shield
×1
Samsung PRO 16 GB Micro SD Card
×1
Power Supply 24V dc, 2.5A Level V 1
×1
FTDI Chip, 3.3 V TTL USB to UART Cable, TTL-232R-3V3
×1
SIMATIC IOT2020
Siemens SIMATIC IOT2020
×1

Software apps and online services

Node-RED
Node-RED
Logosoft confort 8.1

Hand tools and fabrication machines

Mastech MS8217 Autorange Digital Multimeter
Digilent Mastech MS8217 Autorange Digital Multimeter
screwdriver

Story

Read more

Schematics

Schematic what can be done with IOT2020

I want to use in HVAC application and KNX bus application

IOT 2020 autocad DXF files

I made schematic with Wscad, and than convert each page to DXF, so you can inport to Autocad or similar programm and edit yourself

Aplication HVAC

I want to program this application at the end for solar heating

Code

ESP 8266 S7 code

Arduino
You must install Arduino IDE for ESP, not my code only few changes to work with my equipment
/***
 * 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
Great project made by Davide Nardella, I use it with ESP, must be in same directory
/*=============================================================================|
|  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++
Made to Davide, many thanks to him, must be in same directory with ESP ino file
/*=============================================================================|
|  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
//-----------------------------------------------------------------------------

LOGO 8 network project

Plain text
Program Logo 8 with it, You will need Logosoft comfort 8.1
No preview (download only).

IOT 2020 arduino code for 4 relay schield

Arduino
Test code for 4 relay schield
/*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
In nodered in new flow, import from clipboard
[{"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"}]

Credits

Darko Vranesa

Darko Vranesa

5 projects • 6 followers

Comments