// Includes: <Arduino.h> for Serial etc.
#include <Arduino.h>
#include <ArduinoOTA.h> // Arduino over the air update
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <ShiftRegister74HC595.h> // Includes: <ShiftRegister74HC595.h> for 7-Segment and Relays
#include <ESPAsyncWebServer.h>
#include "ModbusClientRTU.h" // Include the header for the ModbusClient RTU style
#include "Logging.h"
// Definitions for this special case
#define RXPIN GPIO_NUM_3
#define TXPIN GPIO_NUM_1
#define REDEPIN GPIO_NUM_22
#define BAUDRATE 9600
#define FIRST_REGISTER 0x0000
#define NUM_VALUES 8
#define READ_INTERVAL 1000
bool data_ready = false;
int values[NUM_VALUES];
byte mbResponse[32];
String mbData;
float value;
uint32_t request_time;
uint32_t spmillis;
int digit, digitpos, relais;
bool dp1, dp2, dp3, dp4;
int state7segment = 0, i = 0;
char SDbuf[8];
// 7-Segment Anzeige
// 74HC595 Outpin Connection with 7-segment display.
// -----
// a
// -----
// |f| |b|
// -----
// g
// -----
// |e| |c|
// -----
// d dp
// -----
const bool commonCathode = true;
const byte digit_pattern[19] = {
// Q7 Q6 Q5 Q4 Q3 Q2 Q1 Q0
// DP g f e d c b a
0b00111111, // 0 [0]
0b00000110, // 1 [1]
0b01011011, // 2 [2]
0b01001111, // 3 [3]
0b01100110, // 4 [4]
0b01101101, // 5 [5]
0b01111101, // 6 [6]
0b00000111, // 7 [7]
0b01111111, // 8 [8]
0b01101111, // 9 [9]
0b01110111, // A [10]
0b01111100, // b [11]
0b00111001, // C [12]
0b01011110, // d [13]
0b01111001, // E [14]
0b01110001, // F [15]
0b01000000, // - [16]
0b10000000, // . [17]
0b00000000 // [18]
};
// Define Connections to 74HC595
static const int latch595 = 14; // Pin connected to ST_CP of 74HC595
static const int clk595 = 27; // Pin connected to SH_CP of 74HC595
static const int dt595 = 13; // Pin connected to DS of 74HC595
static const int oe595 = 4; // Pin connected to OE of 74HC595
// Define Connections to 74HC165
static const int load = 16; // Pin connected to PL of 74HC165
//static const int clockEn = 4; // Pin connected to CE of 74HC165
static const int dataIn = 5; // Pin connected to Q7 of 74HC165
static const int clockIn = 17; // Pin connected to CP of 74HC165
//WiFi
char ssid1[] = "WLAN-RBA1"; // your network SSID (name)
char pass1[] = "LeaHannah31.08.1994"; // your network password
char ssid2[] = "WLAN-RBA2"; // your network SSID (name)
char pass2[] = "LeaHannah31.08.1994"; // your network password
char ssid3[] = "WLAN-RBA3"; // your network SSID (name)
char pass3[] = "LeaHannah31.08.1994"; // your network password
WiFiClient client;
WiFiMulti wifiMulti;
// create a global shift register object
// parameters: <number of shift registers> (data pin, clock pin, latch pin)
ShiftRegister74HC595<3> sr(GPIO_NUM_13, GPIO_NUM_27, GPIO_NUM_14);
// Register 1 / QA - 7-Segment A
// Register 1 / QB - 7-Segment B
// Register 1 / QC - 7-Segment C
// Register 1 / QD - 7-Segment D
// Register 1 / QE - 7-Segment E
// Register 1 / QF - 7-Segment F
// Register 1 / QG - 7-Segment G
// Register 1 / QH - 7-Segment DP
// Register 2 / QA - 7-Segment G1
// Register 2 / QB - 7-Segment G2
// Register 2 / QC - 7-Segment G3
// Register 2 / QD - 7-Segment G4
// Register 2 / QE - 7-Segment
// Register 2 / QF - 7-Segment
// Register 2 / QG - 7-Segment
// Register 2 / QH - 7-Segment
// Register 3 / QA - Relay K1
// Register 3 / QB - Relay K2
// Register 3 / QC - Relay K3
// Register 3 / QD - Relay K4
// Register 3 / QE - Relay K5
// Register 3 / QF - Relay K6
// Register 3 / QG - Relay K7
// Register 3 / QH - Relay K8
AsyncWebServer server(80);
// Create a ModbusRTU client instance
// The RS485 module has no halfduplex, so the parameter with the DE/RE pin is required!
ModbusClientRTU MB(REDEPIN);
// Define an onData handler function to receive the regular responses
// Arguments are received response message and the request's token
void handleData(ModbusMessage response, uint32_t token) {
int i=0, k=0, j=0;
uint16_t offs = 3; // First value is on pos 3, after server ID, function code and length byte
// The device has values all as IEEE754 float32 in two consecutive registers
// Read the requested in a loop
//for (uint8_t i = 0; i < NUM_VALUES; ++i) {
// offs = response.get(offs, values[i]);
//}
j=0;
for(j = 0; j < sizeof(mbResponse); j++){
mbResponse[j] = 0;
}
k=0;
for (auto& byte : response) {
mbResponse[k] = byte;
k+=1;
}
for (i = 0; i < NUM_VALUES * 2; i += 2) {
values[i/2] = (response[i+offs] << 8) | response[i+offs+1];
}
// Signal "data is complete"
request_time = token;
data_ready = true;
}
// Define an onError handler function to receive error responses
// Arguments are the error code returned and a user-supplied token to identify the causing request
void handleError(Error error, uint32_t token) {
ModbusError me(error); // ModbusError wraps the error code and provides a readable error message for it
LOG_E("Error response: %02X - %s\n", (int)me, (const char *)me);
}
// Ansteuerung Schieberegister U6->U8->U5 auf eletechsup ES32A08
// Bits von links nach rechts
// data1 = Digit+dp
// Q7 Q6 Q5 Q4 Q3 Q2 Q1 Q0
// DP g f e d c b a
// data2 = /G1../G4
// Q7 Q6 Q5 Q4 Q3 Q2 Q1 Q0
// /G4 /G3 /G2 /G1
// data3 = Relais 8..1
// Q7 Q6 Q5 Q4 Q3 Q2 Q1 Q0
// Re8 Re7 Re6 Re5 Re4 Re3 Re2 Re1
void SR74HC595(byte data1, byte data2, byte data3) {
digitalWrite(latch595, LOW);
if (commonCathode == true) {
shiftOut(dt595, clk595, MSBFIRST, data3); // Relais 1..8
shiftOut(dt595, clk595, MSBFIRST, data2); // /G1..G4(7-Segment 1..4)
shiftOut(dt595, clk595, MSBFIRST, data1); // Digit+DP
} else {
shiftOut(dt595, clk595, MSBFIRST, ~(data3)); // Relais 1..8
shiftOut(dt595, clk595, MSBFIRST, ~(data2)); // /G1..G4(7-Segment 1..4)
shiftOut(dt595, clk595, MSBFIRST, ~(data1)); // Digit+DP
}
digitalWrite(latch595, HIGH);
digitalWrite(oe595, LOW);
}
//-----------------------------------------------------------------------------
// Setup() - initialization happens here
//-----------------------------------------------------------------------------
void setup() {
// set the 74HC595 Control pin as output
pinMode(latch595, OUTPUT); // ST_CP of 74HC595
pinMode(clk595, OUTPUT); // SH_CP of 74HC595
pinMode(dt595, OUTPUT); // DS of 74HC595
pinMode(oe595, OUTPUT); // OE of 74HC595
// Init Serial monitor
Serial.begin(115200);
while (!Serial) {}
Serial.println("__ OK __");
wifiMulti.addAP(ssid1, pass1);
wifiMulti.addAP(ssid2, pass2);
wifiMulti.addAP(ssid3, pass3);
Serial.println("Connecting Wifi...");
if (wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname("ESP32Heizung");
// No authentication by default
ArduinoOTA.setPassword("LeaHannah31894");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_FS
type = "filesystem";
}
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
// Webserver
// Define a route to serve the HTML page
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
//Serial.println("ESP32 Web Server: New request received:"); // for debugging
//Serial.println("GET /"); // for debugging
// Format the temperature with one decimal places
String temperatureStr0 = String(float(values[0])/10.0, 1);
String temperatureStr1 = String(float(values[1])/10.0, 1);
String temperatureStr2 = String(float(values[2])/10.0, 1);
String temperatureStr3 = String(float(values[3])/10.0, 1);
String temperatureStr4 = String(float(values[4])/10.0, 1);
String temperatureStr5 = String(float(values[5])/10.0, 1);
String temperatureStr6 = String(float(values[6])/10.0, 1);
String temperatureStr7 = String(float(values[7])/10.0, 1);
// Ausgabe der vom Modbus empfangenen Bytes auf der Webseite
mbData = "";
for(i = 0; i < sizeof(mbResponse); i+=1){
mbData += String(mbResponse[i]);
mbData += " ";
}
String html = "<!DOCTYPE HTML>";
html += "<html>";
html += "<head>";
html += "<link rel=\"icon\" href=\"data:,\">";
html += "</head>";
html += "<p>";
html += "Modbus Daten: <span style=\"color: red;\">";
html += mbData;
html += "</span>";
html += "</p>";
html += "<p>";
html += "Temperatur 0: <span style=\"color: blue;\">";
html += temperatureStr0;
html += "°C</span>";
html += "</p>";
html += "<p>";
html += "Temperatur 1: <span style=\"color: blue;\">";
html += temperatureStr1;
html += "°C</span>";
html += "</p>";
html += "<p>";
html += "Temperatur 2: <span style=\"color: blue;\">";
html += temperatureStr2;
html += "°C</span>";
html += "</p>";
html += "<p>";
html += "Temperatur 3: <span style=\"color: blue;\">";
html += temperatureStr3;
html += "°C</span>";
html += "</p>";
html += "<p>";
html += "Temperatur 4: <span style=\"color: blue;\">";
html += temperatureStr4;
html += "°C</span>";
html += "</p>";
html += "<p>";
html += "Temperatur 5: <span style=\"color: blue;\">";
html += temperatureStr5;
html += "°C</span>";
html += "</p>";
html += "<p>";
html += "Temperatur 6: <span style=\"color: blue;\">";
html += temperatureStr6;
html += "°C</span>";
html += "</p>";
html += "<p>";
html += "Temperatur 7: <span style=\"color: blue;\">";
html += temperatureStr7;
html += "°C</span>";
html += "</p>";
html += "</html>";
request->send(200, "text/html", html);
});
// Start the webserver
server.begin();
// Set up Serial2 connected to Modbus RTU
RTUutils::prepareHardwareSerial(Serial2);
Serial2.begin(BAUDRATE, SERIAL_8N1, RXPIN, TXPIN);
// Set up ModbusRTU client.
// - provide onData handler function
MB.onDataHandler(&handleData);
// - provide onError handler function
MB.onErrorHandler(&handleError);
// Set message timeout to 2000ms
MB.setTimeout(2000);
// Start ModbusRTU background task
MB.begin(Serial2);
}
//-----------------------------------------------------------------------------
// loop() - cyclically request the data
//-----------------------------------------------------------------------------
void loop() {
static unsigned long next_request = millis();
// Over the air update
ArduinoOTA.handle();
// Ansteuerung Schieberegister 3x 74HC595
if (millis() - spmillis >= 1000) {
if(i>7){i = -1;}
i+=1;
value = float(values[i]) / 10.0;
// Dezimalzahl in array of char umwandeln
SDbuf[0] = '\0'; // Nullstring setzen
dtostrf(value, 5, 1, &SDbuf[strlen(SDbuf)]);
spmillis = millis();
}
// Ansteuerung Schieberegister 3x 74HC595
switch (state7segment) {
case 0: // initialisierung
digit = digit_pattern[18]; // kein Segment aktiviert
digitpos = 0b00001111; // kein Digit ansteuern
state7segment = 1; // in nächsten State wechseln
break;
case 1: // digit 1
if (isDigit(SDbuf[0])) {
digit = digit_pattern[SDbuf[0]-'0']; // Zeichen in Zahl umwandeln
} else if (SDbuf[0]=='-'){
digit = digit_pattern[16];
} else {
digit = digit_pattern[18];
}
digitpos = 0b00001110; // 1. (linke) 7-Segmentanzeige aktivieren
state7segment = 2;
break;
case 2: // digit 2
if (isDigit(SDbuf[1])) {
digit = digit_pattern[SDbuf[1]-'0']; // Zeichen in Zahl umwandeln
} else if (SDbuf[1]=='-'){
digit = digit_pattern[16];
} else {
digit = digit_pattern[18];
}
digitpos = 0b00001101; // 2. 7-Segmentanzeige aktivieren
state7segment = 3;
break;
case 3: // digit 3
if (isDigit(SDbuf[2])) {
digit = digit_pattern[SDbuf[2]-'0'] | digit_pattern[17]; // Zeichen in Zahl umwandeln und Dezimalpunkt setzen
} else if (SDbuf[2]=='-'){
digit = digit_pattern[16];
} else {
digit = digit_pattern[18];
}
digitpos = 0b00001011; // 3. 7-Segmentanzeige aktivieren
state7segment = 4;
break;
case 4: // digit 4
if (isDigit(SDbuf[4])) {
digit = digit_pattern[SDbuf[4]-'0']; // Zeichen in Zahl umwandeln
} else if (SDbuf[4]=='-'){
digit = digit_pattern[16];
} else {
digit = digit_pattern[18];
}
digitpos = 0b00000111; // 4. 7-Segmentanzeige aktivieren
state7segment = 0;
break;
default:
// if nothing else matches, do the default
state7segment = 0;
break;
}
SR74HC595(digit, digitpos, relais);
//}
// Shall we do another request?
if (millis() - next_request > READ_INTERVAL) {
// Yes.
data_ready = false;
// Issue the request
Error err = MB.addRequest((uint32_t)millis(), 1, READ_HOLD_REGISTER, FIRST_REGISTER, NUM_VALUES);
if (err != SUCCESS) {
ModbusError e(err);
LOG_E("Error creating request: %02X - %s\n", (int)e, (const char *)e);
}
// Save current time to check for next cycle
next_request = millis();
} else {
// No, but we may have another response
if (data_ready) {
// We do. Print out the data
//Serial.printf("Requested at %8.3fs:\n", request_time / 1000.0);
//for (uint8_t i = 0; i < NUM_VALUES; ++i) {
// Serial.printf(" %04X: %3.1f\n", i + FIRST_REGISTER, (float)values[i] / 10.0); // Ergebnis durch 10 Teilen, da eine Kommastelle
//}
//Serial.printf("----------\n\n");
data_ready = false;
}
}
}