#include <Arduino.h>
#include <Ethernet.h>
#include <WiFi.h>
#include <esp_task_wdt.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <SPIFFS.h>
#include <Ticker.h>
// 2022-04-06 v1.0.0 Erstausgabe - Board V2.2
#define prgVersion "1.0.0"
const char compile_date[] = __DATE__ " " __TIME__;
/**************************** CONST ***********************************/
#define ERROR_MAC "Cannot convert Mac Address. Please modify. Set default value."
#define ERROR_MOIP "Cannot convert IP address of ioModul. Please modify."
#define ERROR_DNS "Cannot convert IP address of DNS. Please modify."
#define ERROR_GW "Cannot convert IP address of gateway. Please modify."
#define ERROR_SN "Cannot convert IP address of subnet mask. Please modify."
#define ERROR_CBIP "Cannot convert IP address of callback server. Please modify."
#define ERROR_CBPO "Cannot convert port of callback server. Please modify."
#define ERROR_CBPA "Cannot convert path of callback server. Please modify."
#define ERROR_DBDL "Cannot convert debounce time. Please modify."
#define HTML_CONTENT_LENGTH "Content-Length: "
#define HTML_CONTENT_TYPE "Content-Type: "
#define HTTP_STATUS_200 "HTTP/1.0 200 OK"
#define HTTP_STATUS_400 "HTTP/1.0 400 Bad Request"
#define HTTP_STATUS_422 "HTTP/1.0 422 Unprocessable Entity"
#define HTTP_STATUS_500 "HTTP/1.0 500 Internal Server Error"
#define POST_OUTPUT "POST /output"
#define POST_SETTINGS "POST /settings"
#define GET_OUTPUT "GET /output"
#define GET_INPUT "GET /input"
#define GET_SETTINGS "GET /settings"
#define LED_BUILTIN 2 // flashing blue LED
#define WIZ_CS 5 // CS Pin
#define RESETW5500 4 // Reset Pin
#define OP_MODE 34 // DHCP or Configfile
#define JSIZE 1024
#define FILE_NAME "/config.txt"
#define WDT_TIMEOUT_SECONDS 10 // seconds until the watchdog bites
const int INTRPIN[] = {32, 33}; // PCF-Interrupt pin for inputs 1-8 and 9-16
const byte PCFADRIN[] = {0x38, 0x39}; // inputs 1-16
const byte PCFADROUT[] = {0x3F, 0x3B}; // outputs 1-16 #define FILE_NAME "/config.txt"
const int NUMOUTPINS = sizeof(PCFADROUT) * 8; // number of Output-Pins = 16
const int NUMINPINS = sizeof(PCFADRIN) * 8; // number of Input-Pins = 16
IPAddress ipAdrDHCP(0, 0, 0, 0);
IPAddress ipAdrMo(192, 168, 2, 88);
IPAddress ipDnsMo(192, 168, 2, 1);
IPAddress ipGwMo(192, 168, 2, 1);
IPAddress ipSnMo(255, 255, 255, 0);
IPAddress ipAdrCb(192, 168, 2, 20);
byte mac[] = {0xDE, 0x73, 0x94, 0x8A, 0x4A, 0xE6};
/************************* ENUMS *********************************/
enum class MODULSTATUS
{
START, // 0 temporarily begin
FILE_ERROR, // 1 error fatal error, can't read/write config file
ETH_CONNECT, // 2 error try connect to ethernet
ETH_CABLE_ERROR, // 3 error problems with ethernet cable detected
W5500_ERROR, // 4 error fatal problems with the Shield W5500 detected
CDATA_ERROR, // 5 failure fatal error with configuration file detected
MAC_ERROR, // 6 failure problems with MAC address detected
HTTP, // 7 normal HTTP-Server start with config file (DHCP or fix IP)
DHCP, // 8 normal HTTP-Server start with fix IP address
PCF_ERROR, // 9 failure problem with a I2C device detected
CALLBACK_ERROR, // 10 failure temporary problem with server callback detected
};
MODULSTATUS modulStatus = MODULSTATUS::START;
enum class RECONNECTCAUSE
{
REBOOT,
ETHERNET,
REST
};
RECONNECTCAUSE reconnectCause = RECONNECTCAUSE::REBOOT;
enum class Status
{
FIRST_LINE,
HEADER_LINE,
EMPTY_LINE,
BODY_LINE
};
Status status;
const String PCFError[] = {" ID 1: data too long to fit in transmit buffer",
" ID 2: received NACK on transmit of address",
" ID 3: received NACK on transmit of data",
" ID 4: other error"};
/************************* VARS *********************************/
String cb_pt = "80"; // callback port
String cb_ph = "/"; // callback path
String db_dl = "12"; // debounce time
boolean requestTypeJson;
boolean InterrutTimerActiv[NUMINPINS] = {false, false};
boolean timerPcfInActiv[NUMINPINS]; // state of the PCF timer input
boolean lastValueInput[NUMINPINS]; // last value of the input
unsigned long lastTimePcfIn[NUMINPINS]; // the last time the PCF intput pin was toggled
byte PcfValueOut[NUMOUTPINS];
EthernetServer server(80);
EthernetClient httpEthClient;
EthernetClient clnt;
boolean STATE_DHCP = true;
File file;
Ticker moTicker;
int ledCount = 0;
boolean blueLedState = false;
int currentLedStep = 1;
/************************* LOGGING *********************************/
#define DEBUG_MODE //**change #switch debugging on (commented out) // off (commented)
#ifdef DEBUG_MODE // on serial interface
#define PRINTD Serial.print
#else
#define PRINTD(format, args...) ((void)0)
#endif
void clientResponse(const char *aStatus, String aBody);
void httpSendNewState(int portNum, boolean Value);
//******************************************* HELPER **************************************************************************************
void doDelay(int aNumber)
{
for (int i = 0; i < aNumber; i++)
{
delay(1);
yield();
esp_task_wdt_reset();
}
}
void doEndlessLoop()
{
while (true)
doDelay(100);
}
void AddLogString(String aLogString)
{
PRINTD("\n" + String(micros()) + ": " + aLogString);
}
void setOpModLed(int steps, boolean expanded)
{ // expanded at least 4 steps, otherwise 2
if ((currentLedStep % 2) == 0) // starts with 1 and the first must be on(true)
blueLedState = false;
else
blueLedState = true;
if (currentLedStep < steps)
{
// AddLogString("01, current:" + String(currentLedStep) + ", step:" + String(steps) + ", ledount:" + String(ledCount));
if ((currentLedStep == 1) and expanded)
{
// AddLogString("02");
if (ledCount > 12)
{ // long start signal
// AddLogString("03");
currentLedStep = 2;
ledCount = 0;
}
}
else if (ledCount > 4)
{ // normal change
// AddLogString("04");
currentLedStep = currentLedStep + 1;
ledCount = 0;
}
}
else if (currentLedStep == steps)
{ // pause at the end
// AddLogString("05, current:" + String(currentLedStep) + ", step:" + String(steps) + ", ledount:" + String(ledCount));
if (ledCount > 15)
{
// AddLogString("06");
currentLedStep = 1;
ledCount = 0;
}
}
else
{ // possibe status change or other things
// AddLogString("07");
currentLedStep = 1;
ledCount = 0;
}
}
//******************************************* TIcKER **************************************************************************************
void cbfModul()
{
if (STATE_DHCP)
{
Ethernet.maintain();
}
ledCount++;
switch (int(modulStatus))
{
case 0:
break;
case 1:
setOpModLed(16, false);
break;
case 2:
setOpModLed(4, false);
break;
case 3:
setOpModLed(6, false);
break;
;
case 4:
setOpModLed(8, false);
break;
case 5:
setOpModLed(10, false);
break;
case 6:
setOpModLed(12, false);
break;
case 7:
if (((ledCount > 30) && (blueLedState)) || ((ledCount > 4) && (!blueLedState)))
{
blueLedState = !blueLedState;
ledCount = 0;
}
break; // 3000/400 HTTP-Server use fix IP
case 8:
if (((ledCount > 15) && (blueLedState)) || ((ledCount > 4) && (!blueLedState)))
{
blueLedState = !blueLedState;
ledCount = 0;
}
break; // 1500/400 HTTP-Server use DHCP
case 9:
setOpModLed(14, false);
break;
case 10:
setOpModLed(2, false);
break;
default:
setOpModLed(2, true); // system error
}
digitalWrite(LED_BUILTIN, blueLedState);
}
//****************************************** SETTINGS ******************************************************/
boolean setMAC(String aMAC)
{
boolean r = false;
int i = 0;
AddLogString(aMAC);
if (aMAC.length() > 16)
i = sscanf(aMAC.c_str(), "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
if (i == 6)
r = true;
else
{ // mac is needed, set default values
AddLogString(ERROR_MAC);
clientResponse(HTTP_STATUS_400, ERROR_MAC);
sscanf("DE:75:92:AA:40:E6", "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]);
modulStatus = MODULSTATUS::MAC_ERROR;
}
return r;
}
String mac2String(byte ar[])
{
String s;
for (byte i = 0; i < 6; ++i)
{
char buf[3];
sprintf(buf, "%02X", ar[i]); // J-M-L: slight modification, added the 0 in the format for padding
s += buf;
if (i < 5)
s += ':';
}
return s;
}
boolean setDebounce(String db)
{
boolean r = false;
if ((db.toInt() == 0) or (db.toInt() < 1) or (db.toInt() > 5000))
db_dl = 12;
else
{
r = true;
db_dl = db;
}
return r;
}
boolean setPort(String pt)
{
boolean r = false;
if ((pt.toInt() == 0) or (pt.toInt() < 1) or (pt.toInt() > 9999))
cb_pt = "80";
else
{
r = true;
cb_pt = pt;
}
return r;
}
boolean setPath(String pt)
{
cb_ph = pt;
return true;
}
String checkJsonData(String jsonDoc)
{
StaticJsonDocument<JSIZE> jDoc;
String error = "";
DeserializationError err = deserializeJson(jDoc, jsonDoc);
if (err)
error = "Deserialization Error: " + String(err.c_str());
else
{
if ((!setMAC(String(jDoc["macModule"].as<const char *>()))))
error = ERROR_MAC;
else if (!ipAdrMo.fromString(String(jDoc["ipModule"].as<const char *>())))
error = ERROR_MOIP;
else if (!ipDnsMo.fromString(String(jDoc["dns"].as<const char *>())))
error = ERROR_DNS;
else if (!ipGwMo.fromString(String(jDoc["gateway"].as<const char *>())))
error = ERROR_GW;
else if (!ipSnMo.fromString(String(jDoc["subnetMask"].as<const char *>())))
error = ERROR_SN;
else if (!ipAdrCb.fromString(String(jDoc["ipCallback"].as<const char *>())))
error = ERROR_CBIP;
else if ((!setPort(String(jDoc["portCallback"].as<const char *>()))))
error = ERROR_CBPO;
else if ((!setPath(String(jDoc["pathCallback"].as<const char *>()))))
error = ERROR_CBPA;
else if ((!setDebounce(String(jDoc["debounceTimer"].as<const char *>()))))
error = ERROR_DBDL;
else
AddLogString("Json file processed.");
}
return error;
}
String checkConfigFile()
{
String fileData;
if (!SPIFFS.begin(true))
{ // start reading configuration file
AddLogString("An Error has occurred while mounting SPIFFS");
modulStatus = MODULSTATUS::FILE_ERROR;
doEndlessLoop();
}
file = SPIFFS.open(FILE_NAME, FILE_READ);
if (!file)
{
AddLogString("File not exist. Create one");
file = SPIFFS.open(FILE_NAME, FILE_WRITE);
if (!file)
{
AddLogString("There was an error opening the file for writing");
modulStatus = MODULSTATUS::FILE_ERROR;
doEndlessLoop();
}
file.close();
}
else
{
fileData = file.readString();
AddLogString("Content Configuration File:");
AddLogString(fileData);
file.close();
}
String error = checkJsonData(fileData);
return error;
}
void readSettings()
{
StaticJsonDocument<JSIZE> jDoc;
char json[JSIZE];
jDoc.clear();
jDoc["macModule"] = mac2String(mac);
jDoc["ipModule"] = ipAdrMo.toString();
jDoc["dns"] = ipDnsMo.toString();
jDoc["gateway"] = ipGwMo.toString();
jDoc["subnetMask"] = ipSnMo.toString();
jDoc["ipCallback"] = ipAdrCb.toString();
jDoc["portCallback"] = cb_pt;
jDoc["pathCallback"] = cb_ph;
jDoc["debounceTimer"] = db_dl;
jDoc["version"] = String(prgVersion);
jDoc["compileDate"] = String(__DATE__);
jDoc["compileTime"] = String(__TIME__) ;
serializeJson(jDoc, json);
AddLogString(String(json));
clientResponse(HTTP_STATUS_200, json);
}
void updateSettings(String aBody)
{
String error = checkJsonData(aBody);
if (error == "")
{
file = SPIFFS.open(FILE_NAME, FILE_WRITE); // wirte data
esp_task_wdt_reset();
if (!file)
{
clientResponse(HTTP_STATUS_500, "There was an error writing the config file. Please try again.");
modulStatus = MODULSTATUS::FILE_ERROR;
doEndlessLoop();
}
if (file.print(aBody))
AddLogString("File was written");
else
{
AddLogString("File write failed");
clientResponse(HTTP_STATUS_500, "There was an error writing config file. Please try again.");
modulStatus = MODULSTATUS::FILE_ERROR;
doEndlessLoop();
}
file.close();
clientResponse(HTTP_STATUS_200, "Settings have been updated. Reboot the ESP."); // everthing okay, reboot
esp_task_wdt_reset();
httpEthClient.stop();
ESP.restart();
}
else
clientResponse(HTTP_STATUS_422, error);
}
//*************************************** * I2C Device ******************************************************/
byte ReadPCF8(byte aI2CAdr)
{
byte value = 0;
Wire.requestFrom(aI2CAdr, 1); // Read actual value
while (Wire.available())
{ // is run through only once, since PCF8574 only has 1 byte
value = Wire.read();
}
value = 0xFF - value; // invert once, then it fitss
// AddLogString( "PCF " + String(aI2CAdr) + " read " + String(value) );
return value;
}
void WritePCF8(byte aI2CAdr, byte aValue)
{
byte error;
Wire.beginTransmission(aI2CAdr);
Wire.write(0xFF - aValue);
error = Wire.endTransmission(true);
if (error == 0)
{
AddLogString("write PCF " + String(aI2CAdr) + " with " + aValue + " successful.");
}
else
{
AddLogString("write " + String(aI2CAdr) + " error: " + String(error));
}
}
void WritePCF(int aPcfNum, int aPort, boolean aValue)
{
byte neuWert, altWert;
String s;
if ((aPort > 0) and (aPort < 9))
{
if (aValue)
s = " H";
else
s = " L";
neuWert = (1 << (aPort - 1)); // Determine new value as a hex number, by bit shift
altWert = PcfValueOut[aPcfNum];
if (aValue)
altWert |= neuWert; // Add new value if not already available
else
altWert &= ~(neuWert); // reduce new value if available
if (altWert != PcfValueOut[aPcfNum])
{ // write only when necessary
WritePCF8(PCFADROUT[aPcfNum], altWert); // write new port mask
PcfValueOut[aPcfNum] = altWert;
}
}
else
AddLogString("Error WritePCF, port number to high.");
}
void initI2C()
{
int i, j;
byte error;
AddLogString("init I2C");
Wire.begin(); // i2c start
for (j = 0; j < sizeof(PCFADRIN); j++)
{ // check and init inputs
Wire.beginTransmission(PCFADRIN[j]);
error = Wire.endTransmission(true);
if (error == 0)
{ // 0: success
AddLogString("PCF8574-Input " + String(PCFADRIN[j]) + " found!");
pinMode(INTRPIN[j], INPUT_PULLUP); // init interrupt
}
else
{
modulStatus = MODULSTATUS::PCF_ERROR;
AddLogString("Error: PCF8574-Input " + String(PCFADRIN[0]) + " not found. Cause," + PCFError[error]);
}
}
for (i = 0; i < sizeof(PCFADROUT); i++)
{ // check and init outputs
Wire.beginTransmission(PCFADROUT[i]);
PcfValueOut[i] = ReadPCF8(PCFADROUT[i]);
error = Wire.endTransmission(true);
if (error == 0)
{ // 0:success
AddLogString("PCF8574-Output " + String(PCFADROUT[i]) + " found!");
AddLogString("PCF " + String(PCFADROUT[i]) + " initial value: " + PcfValueOut[i]);
}
else
{
modulStatus = MODULSTATUS::PCF_ERROR;
AddLogString("Error: PCF8574-Output " + String(PCFADROUT[i]) + " not found. Cause," + PCFError[error]);
}
}
}
void readInputs()
{
AddLogString("ioModul read inputs.....");
StaticJsonDocument<JSIZE> jDoc;
char json[JSIZE];
for (int i = 0; i < NUMINPINS; i++)
{
JsonObject j_input = jDoc.createNestedObject();
j_input["id"] = i + 1;
j_input["state"] = (lastValueInput[i] == 1);
}
serializeJson(jDoc, json);
AddLogString(String(json));
clientResponse(HTTP_STATUS_200, json);
}
void readOutputs()
{
AddLogString("ioModul read outputs.....");
StaticJsonDocument<JSIZE> jDoc;
char json[JSIZE];
byte bpcf;
int i, j;
for (i = 0; i < sizeof(PCFADROUT); i++)
{
bpcf = PcfValueOut[i];
for (j = 7; j >= 0; j--)
{
JsonObject j_output = jDoc.createNestedObject();
j_output["id"] = (i * 8) + 8 - j;
j_output["state"] = (bitRead(bpcf, j) == 1);
}
}
serializeJson(jDoc, json);
AddLogString(String(json));
clientResponse(HTTP_STATUS_200, json);
}
void updateOutput(int aPin, String aBody)
{
AddLogString("ioModul update output " + String(aPin) + "....");
StaticJsonDocument<JSIZE> jDoc;
int pcf_pin, pcf_adr;
DeserializationError err = deserializeJson(jDoc, aBody);
if (err)
{
String error = "Body deserialization error: " + String(err.f_str());
AddLogString(error);
clientResponse(HTTP_STATUS_422, error);
}
else
{
if ((aPin > 0) and (aPin < sizeof(PCFADROUT) * 8 + 1))
{
pcf_pin = (aPin % 8); // the rest of 8
if (pcf_pin == 0)
pcf_pin = 8; // when no rest then 8
pcf_adr = (aPin - pcf_pin) / 8;
pcf_pin = 9 - pcf_pin; // 8-1 is rotated, then it fits
WritePCF(pcf_adr, pcf_pin, boolean(jDoc["state"].as<boolean>()));
clientResponse(HTTP_STATUS_200, "Output has been updated.");
}
else
clientResponse(HTTP_STATUS_500, "Failed to update I2C output.");
}
}
void pcfInputChange(int pcfSel, int aPin, boolean aValue)
{
timerPcfInActiv[aPin] = true;
lastTimePcfIn[aPin] = millis();
httpSendNewState(aPin + 1, aValue);
AddLogString("Start dbTimer PIN " + String(aPin + 1));
InterrutTimerActiv[pcfSel] = true;
lastValueInput[aPin] = aValue;
}
void processInputs(byte newPcfValue, int pcfSel)
{
InterrutTimerActiv[pcfSel] = false;
for (int i = 0; i < 8; i++)
{
if (timerPcfInActiv[pcfSel * 8 + i])
{ // Timer active ?
if ((millis() - lastTimePcfIn[pcfSel * 8 + i]) > db_dl.toInt())
{ // Timer expired ?
if (bitRead(newPcfValue, i) != lastValueInput[pcfSel * 8 + i])
{
pcfInputChange(pcfSel, pcfSel * 8 + i, bitRead(newPcfValue, i));
}
else
{
timerPcfInActiv[pcfSel * 8 + i] = false;
AddLogString("Stoppe dbTimer PIN " + String(pcfSel * 8 + i + 1));
}
}
else
InterrutTimerActiv[pcfSel] = true;
}
else
{
if (bitRead(newPcfValue, i) != lastValueInput[pcfSel * 8 + i])
{
AddLogString("Neue Änderung");
pcfInputChange(pcfSel, pcfSel * 8 + i, bitRead(newPcfValue, i));
}
}
}
}
void processInterrupt()
{ // The interrupt is triggered by a change on a pin
for (int i = 0; i < sizeof(PCFADRIN); i++)
{
if ((digitalRead(INTRPIN[i]) == 0) || (InterrutTimerActiv[i]))
processInputs(ReadPCF8(PCFADRIN[i]), i);
}
}
//******************************************* http **************************************************************************************
void clientResponse(const char *aStatus, String aBody)
{
AddLogString(aStatus);
AddLogString(aBody);
httpEthClient.println(String(aStatus) + "\r\n" HTML_CONTENT_TYPE + "text/plain\r\n" // simple plain text without html tags
"\r\n" +
aBody);
httpEthClient.stop();
}
void httpSendNewState(int portNum, boolean Value)
{
if (!clnt.connected())
{
AddLogString("Try to connect to " + ipAdrCb.toString() + " Port " + cb_pt.toInt() + ".");
clnt.connect(ipAdrCb, cb_pt.toInt());
}
else
AddLogString("Callback Sever connected.");
String v = "low";
if (Value)
v = "high";
AddLogString("Input " + String(portNum) + " change to " + v + ".");
if (clnt.connected())
{
String jsonBody = "{"
"id"
": " +
String(portNum) + ", "
"state"
": ";
if (Value)
jsonBody += "true}";
else
jsonBody += "false}";
clnt.println("POST " + cb_ph + " HTTP/1.1");
clnt.println(String(HTML_CONTENT_TYPE) + "application/json");
clnt.println("Accept: */*");
clnt.println("Connection: keep-alive");
clnt.println(String(HTML_CONTENT_LENGTH) + jsonBody.length());
clnt.println("Host: " + ipAdrCb.toString());
clnt.println(); // end HTTP request header
clnt.println(jsonBody);
AddLogString("reponse: --------------------");
while (clnt.available())
Serial.print(clnt.read());
AddLogString("-----------------------------");
if (STATE_DHCP)
modulStatus = MODULSTATUS::DHCP;
else
modulStatus = MODULSTATUS::HTTP;
clnt.connect(ipAdrCb, cb_pt.toInt());
}
else
{
AddLogString("Callback server not reachable, connection failed.");
modulStatus = MODULSTATUS::CALLBACK_ERROR;
}
}
boolean checkPath(String l, String p)
{
boolean b = false;
if (l.indexOf(p) == 0)
{
String r = l.substring(p.length(), l.length());
if ((r.indexOf(" ") == 0) || (r.indexOf("/") == 0))
b = true;
}
return b;
}
boolean checkContentType(String hl)
{
boolean ctJson = false;
if (hl.indexOf(String(HTML_CONTENT_TYPE)) >= 0)
{
if (hl.indexOf(String(HTML_CONTENT_TYPE) + "application/json") >= 0)
{
AddLogString(String(HTML_CONTENT_TYPE) + "is json.... ");
ctJson = true;
}
}
return ctJson;
}
int checkContentLength(String hl)
{
int len = 0;
if (hl.indexOf(String(HTML_CONTENT_LENGTH)) >= 0)
{
int x = String(HTML_CONTENT_LENGTH).length();
len = hl.substring(x, hl.length()).toInt();
Serial.println(HTML_CONTENT_LENGTH + String(len) + ".....");
}
return len;
}
void handleHeader(String aHeader, String aBody)
{
int xy = 0;
AddLogString("Header :");
AddLogString(aHeader);
AddLogString("Body :");
AddLogString(aBody);
if (checkPath(aHeader, String(GET_OUTPUT))) // GET /output
readOutputs();
else if (checkPath(aHeader, String(GET_INPUT))) // GET /input
readInputs();
else if (checkPath(aHeader, String(GET_SETTINGS))) // GET /settings
readSettings();
else if (checkPath(aHeader, String(POST_SETTINGS))) // POST /setting
updateSettings(aBody);
else if (checkPath(aHeader, String(POST_OUTPUT))) // POST /output/
{
String r = aHeader.substring(String(POST_OUTPUT).length() + 1, String(POST_OUTPUT).length() + 3);
if (r.length() > 1)
{
xy = r.toInt();
if ((xy < 1) || (xy > 16))
clientResponse(HTTP_STATUS_400, "resource path not valid - wrong portnumber");
else
updateOutput(xy, aBody);
}
else
clientResponse(HTTP_STATUS_400, "resource path not valid - portnumber");
}
else
clientResponse(HTTP_STATUS_400, "method is not supported or resource path not valid"); // problem
httpEthClient.stop();
}
void handleHttpServer()
{
String strBuffer = "", strBody = "", strFirstLine = "";
int length = 0;
httpEthClient = server.available();
status = Status::FIRST_LINE;
if (httpEthClient)
{
AddLogString(F("client connected...."));
while (httpEthClient.connected())
{
while (httpEthClient.available())
{
char c = httpEthClient.read();
Serial.print(c);
if (c == '\n')
{
if (status == Status::EMPTY_LINE)
status = Status::BODY_LINE;
if (status > Status::FIRST_LINE && strBuffer.length() < 2)
status = Status::EMPTY_LINE;
if (status == Status::FIRST_LINE)
{
strFirstLine = strBuffer.substring(0, strBuffer.length() - 1);
status = Status::HEADER_LINE;
}
else if (status == Status::HEADER_LINE)
{
requestTypeJson = checkContentType(strBuffer);
length = checkContentLength(strBuffer);
}
else if (status == Status::BODY_LINE)
strBody += strBuffer.substring(0, strBuffer.length() - 1);
strBuffer = "";
}
else
strBuffer = strBuffer + String(c);
}
if (strBody.length() < length)
strBody += strBuffer;
handleHeader(strFirstLine, strBody);
}
}
}
//******************************************* ethernet **************************************************************************************
void initW5500()
{
pinMode(RESETW5500, OUTPUT); // Reset W5500 GPIO 0
digitalWrite(RESETW5500, LOW);
delay(20);
digitalWrite(RESETW5500, HIGH);
delay(20);
Ethernet.init(WIZ_CS);
delay(500); // time to boot up
}
void ReconnectEthernet()
{
if (!clnt.connected())
{
Serial.println("disconnected");
clnt.stop();
}
AddLogString("reconnect Ethernet ....");
MODULSTATUS oldStatus = modulStatus;
modulStatus = MODULSTATUS::ETH_CABLE_ERROR;
while (Ethernet.linkStatus() != LinkON)
{
initW5500();
if (!STATE_DHCP)
{
Ethernet.begin(mac, ipAdrMo, ipDnsMo, ipGwMo, ipSnMo);
doDelay(1000); // this takes time; do not decrease value
if (Ethernet.linkStatus() == LinkON)
{
AddLogString("Ethernet connected again....");
}
else
AddLogString("Ethernet not connected over IP address ....");
}
else
{
if (Ethernet.begin(mac) == 0)
{
AddLogString("Ethernet connected again....");
}
else
{
doDelay(1000);
AddLogString("Ethernet not connected over DHCP ....");
}
}
}
modulStatus = oldStatus;
}
void checkEthHardware()
{
doDelay(500);
AddLogString("check physical ethernet connection and W5500....");
if (Ethernet.hardwareStatus() == EthernetNoHardware)
{
AddLogString("W5500, ethernet shield was not found");
modulStatus = MODULSTATUS::W5500_ERROR;
AddLogString("current operating Mode: " + String(int(modulStatus)));
doEndlessLoop();
}
if (Ethernet.linkStatus() == LinkOFF)
{
AddLogString("Ethernet cable is not connected.");
modulStatus = MODULSTATUS::ETH_CABLE_ERROR;
AddLogString("current operating Mode: " + String(int(modulStatus)));
doEndlessLoop();
}
}
void startServer()
{
AddLogString(F("start ioModul server"));
initW5500();
if (!STATE_DHCP)
{
AddLogString(F("start ethernet connection with IP address"));
Ethernet.begin(mac, ipAdrMo, ipDnsMo, ipGwMo, ipSnMo);
checkEthHardware();
modulStatus = MODULSTATUS::HTTP;
}
else
{
Ethernet.maintain();
AddLogString("start ethernet connection use DHCP");
if (Ethernet.begin(mac) == 0)
{
AddLogString("Failed to connect with DHCP, do restart....");
ESP.restart();
}
AddLogString("Ethernet connected....");
modulStatus = MODULSTATUS::DHCP;
}
AddLogString("HTTP-Server is starting");
server.begin();
// server.begin();
AddLogString("IP Address: ");
AddLogString(Ethernet.localIP().toString());
}
//******************************************* setup + main **************************************************************************************
void setup()
{
#ifdef DEBUG_MODE
Serial.begin(115200);
while (!Serial)
;
#endif
WiFi.mode(WIFI_OFF);
btStop();
AddLogString("start ioModul...");
pinMode(LED_BUILTIN, OUTPUT); // init blue LED
pinMode(OP_MODE, INPUT_PULLUP); // init OP_MODE pin DHCP/Configfile
moTicker.attach_ms(100, cbfModul); // init Ticker
initI2C();
/************************* Check ConfigFile *********************************/
STATE_DHCP = true;
String err = checkConfigFile();
if (err != "")
AddLogString("Error Json file: " + err);
else
{
AddLogString("Json file processed, okay.");
if (ipAdrMo != ipAdrDHCP)
STATE_DHCP = false;
}
if (digitalRead(OP_MODE)) // if true, the module start in DHCP mode
STATE_DHCP = true;
startServer();
esp_task_wdt_init(WDT_TIMEOUT_SECONDS, true); // init watchdog and panicmode
esp_task_wdt_add(NULL); // add current thread to WDT watch
}
void loop()
{
yield();
if (Ethernet.linkStatus() != LinkON)
{
ReconnectEthernet();
reconnectCause = RECONNECTCAUSE::ETHERNET;
}
processInterrupt();
handleHttpServer();
esp_task_wdt_reset();
}