Holzheizung SL 18K mit Pufferspeicher und Esp8266 Nodemcu Komforterweiterung
Die sieben DS18B20 sind Sternförmig verdrahtet.
Holzheizung SL 18K mit Pufferspeicher und Esp8266 Nodemcu Komforterweiterung
Die sieben DS18B20 sind Sternförmig verdrahtet.
Die LCD Anzeige im Heizungsraum.
Die Anzeige als Chart.
// ****************************************************************
// Sketch Esp8266 Heizung Modular(Tab)
// created: Jens Fleischer, 2020-02-11
// last mod: Jens Fleischer, 2023-04-08
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 3.1.0 - 3.1.2
// Getestet auf: Nodemcu
/******************************************************************
Copyright (c) 2020 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
// Der WebServer Tab ist der Haupt Tab mit "setup" und "loop".
// #include <FS.h> #include <ESP8266WebServer.h> müssen im Haupttab aufgerufen werden
// Die Funktionalität des ESP8266 Webservers ist erforderlich.
// "server.onNotFound()" darf nicht im Setup des ESP8266 Webserver stehen.
// Inklusive Arduino OTA-Updates (Erfordert freien Flash-Speicher)
/**************************************************************************************/
#include <ESP8266WebServer.h>
#include <ArduinoOTA.h>
#include <LittleFS.h>
#include <DallasTemperature.h> // https://github.com/milesburton/Arduino-Temperature-Control-Library Version 3.9.1
#include <map>
ESP8266WebServer server(80);
constexpr uint8_t OPERAND {128};
constexpr uint16_t BRIGHTNESS = 850;
bool minmax {true}; // Merker für Min oder Max // Beim neu aufspielen minmax einstellen. minmax Speicher leer true - Speicher voll false
bool pushStatusAddWood {false}; // Status für Push Nachricht Holz nachlegen
bool releaseAddWood {false}; // Globale Freigabe Holz nachlegen
bool lcdStatus {false}; // LCD Beleuchtung An/Aus
std::map<uint8_t, String>mixingTime; // Anzahl und Uhrzeit für Speicher gemischt
//#define DEBUGGING // Einkommentieren für die Serielle Ausgabe
#ifdef DEBUGGING
//#define TEMPDEBUG // Einkommentieren für die Serielle Temperaturausgabe
#define DEBUG_B(...) Serial.begin(__VA_ARGS__)
#define DEBUG_P(...) Serial.println(__VA_ARGS__)
#define DEBUG_F(...) Serial.printf(__VA_ARGS__)
#else
#define DEBUG_B(...)
#define DEBUG_P(...)
#define DEBUG_F(...)
#endif
struct Ds18b20 {
DeviceAddress address;
int16_t currentMin;
int16_t currentMax;
int16_t alwaysMin;
int16_t alwaysMax;
int16_t current; // Aktuelle Temperatur
char minTime[9]; // Zeitpunkt für minimale Temperatur
char maxTime[9]; // Zeitpunkt für maximale Temperatur
char alwaysMinTime[20]; // Zeitpunkt für Langzeit Min Temperatur
char alwaysMaxTime[20]; // Zeitpunkt für Langzeit Max Temperatur
};
struct Collection {
Ds18b20 KES; // Heizkessel
Ds18b20 HVL; // Haus Vorlauf
Ds18b20 HRL; // Haus Rücklauf
Ds18b20 SWW; // Pufferspeicher Warmwasser
Ds18b20 SVL; // Pufferspeicher Vorlauf
Ds18b20 SRL; // Pufferspeicher Rücklauf
Ds18b20 ATE; // Aussentemperatur
};
struct Collection sensors { {{0x28, 0xFF, 0x2E, 0x17, 0x81, 0x16, 0x04, 0x42}, INT16_MAX, INT16_MIN, INT16_MAX, INT16_MIN, 0, "", "", "", ""},
{{0x28, 0xFF, 0xEA, 0x8C, 0x30, 0x17, 0x04, 0xD9}, INT16_MAX, INT16_MIN, INT16_MAX, INT16_MIN, 0, "", "", "", ""},
{{0x28, 0xff, 0xd7, 0xfb, 0x24, 0x17, 0x03, 0xc8}, INT16_MAX, INT16_MIN, INT16_MAX, INT16_MIN, 0, "", "", "", ""},
{{0x28, 0xFF, 0x29, 0x17, 0x81, 0x16, 0x04, 0x13}, INT16_MAX, INT16_MIN, INT16_MAX, INT16_MIN, 0, "", "", "", ""},
{{0x28, 0xFF, 0x03, 0x18, 0x81, 0x16, 0x04, 0xCC}, INT16_MAX, INT16_MIN, INT16_MAX, INT16_MIN, 0, "", "", "", ""},
{{0x28, 0xFF, 0xF6, 0x1E, 0x81, 0x16, 0x04, 0xB1}, INT16_MAX, INT16_MIN, INT16_MAX, INT16_MIN, 0, "", "", "", ""},
{{0x28, 0xff, 0xa3, 0xdc, 0x24, 0x17, 0x03, 0x4c}, INT16_MAX, INT16_MIN, INT16_MAX, INT16_MIN, 0, "", "", "", ""},
};
struct timevalues {
char minmaxTime[9];
char fileName[11];
char timeOfDay[9];
char timeStamp[20];
} lt;
String sketchName() { // Dateiname für den Admin Tab
char file[sizeof(__FILE__)] = __FILE__;
char * pos = strrchr(file, '.'); *pos = '\0';
return file;
}
float convert(int16_t &temp) {
return static_cast<float>(temp) / OPERAND;
}
void setup() {
DEBUG_B(115200);
delay(100);
DEBUG_F("\n\nSketchname: %s\nBuild: %s\t\tIDE: %d.%d.%d\n%s\n\n",
(__FILE__), (__TIMESTAMP__), ARDUINO / 10000, ARDUINO % 10000 / 100, ARDUINO % 100 / 10 ? ARDUINO % 100 : ARDUINO % 10, ESP.getFullVersion().c_str());
setupFS();
connectWifi();
admin();
initLcd();
setupTime();
tempsetup();
setupAPI();
setupButton();
delay(250);
localTime();
toRead();
ArduinoOTA.onStart([]() {
toSave(); // Min/Max Werte speichern bevor OTA Update
});
ArduinoOTA.begin();
server.begin();
server.enableCORS(true); // Sendet den "CORS" Header an den Client.
}
void loop() {
ArduinoOTA.handle();
server.handleClient();
if (millis() < 0x2FFF || millis() > 0xFFFFF0FF) runtime();
if (static auto once {0}; millis() > 54e4 && !once++) connectWifi(); // Bei Stromausfall Wifi Verbindung einmalig nach 9 Minuten neu starten.
localTime();
static uint32_t previousMillis[] {0, 0};
uint32_t currentMillis {millis()};
if (currentMillis - previousMillis[0] >= 250) {
queryTemp();
previousMillis[0] = currentMillis;
}
currentMillis = millis();
if (currentMillis - previousMillis[1] >= 100) {
process();
previousMillis[1] = currentMillis;
}
getButton();
thingspeak();
}
Admin.ino
// ****************************************************************
// Sketch Esp8266 Admin IPv6 Modular(Tab)
// created: Jens Fleischer, 2020-01-26
// last mod: Jens Fleischer, 2021-06-09
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 2.6.1 - 3.1.2
// Geprüft: von 1MB bis 16MB Flash
// Getestet auf: Nodemcu, Wemos D1 Mini Pro, Sonoff Switch, Sonoff Dual
/******************************************************************
Copyright (c) 2020 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
// Diese Version von Admin sollte als Tab eingebunden werden.
// #include <LittleFS.h> #include <ESP8266WebServer.h> müssen im Haupttab aufgerufen werden
// Die Funktionalität des ESP8266 Webservers ist erforderlich.
// Die LittleFS.ino muss im ESP8266 Webserver enthalten sein
// Funktion "admin();" muss im setup() nach setupFS() und dem Verbindungsaufbau aufgerufen werden.
// Die Funktion "runtime();" muss mindestens zweimal innerhalb 49 Tage aufgerufen werden.
// Entweder durch den Client(Webseite) oder zur Sicherheit im "loop();"
// In der IDE unter Werkzeuge auf lwip Variant: ;"v2 IPv6 Lower Memory;" einstellen.
/**************************************************************************************/
#include <AddrList.h>
const char* const PROGMEM flashChipMode[] = {"QIO", "QOUT", "DIO", "DOUT", "Unbekannt"};
void admin() { // Funktionsaufruf "admin();" muss im Setup eingebunden werden
File file = LittleFS.open(existFolder("Config") + "/config.json", "r");
if (file) {
String newhostname = file.readStringUntil('\n');
if (newhostname != "") {
WiFi.hostname(newhostname.substring(1, newhostname.length() - 1));
file.close();
ArduinoOTA.setHostname(WiFi.hostname().c_str());
}
}
server.on("/admin/renew", handlerenew);
server.on("/admin/once", handleonce);
server.on("/reconnect", []() {
server.send(304, "message/http");
WiFi.reconnect();
});
server.on("/restart", []() {
server.send(304, "message/http");
toSave(); //Wenn Werte vor dem Neustart gespeichert werden sollen
ESP.restart();
});
}
//Es kann entweder die Spannung am ADC-Pin oder die Modulversorgungsspannung (VCC) ausgegeben werden.
void handlerenew() { // Um die am ADC-Pin anliegende externe Spannung zu lesen, verwende analogRead (A0)
server.send(200, "application/json", "[\"" + runtime() + "\",\"" + WiFi.RSSI() + "\",\"" + analogRead(A0) + "\"]"); // Json als Array
}
/*
ADC_MODE(ADC_VCC);
void handlerenew() { // Zum Lesen der Modulversorgungsspannung (VCC), verwende ESP.getVcc()
server.send(200, "application/json", "[\"" + runtime() + "\",\"" + WiFi.RSSI() + "\",\"" + ESP.getVcc() / 1024.0 + " V" + "\"]");
}
*/
void handleonce() {
String ipv6Local, ipv6Global;
#if LWIP_IPV6
for (auto &a : addrList) {
if (a.isV6()) a.isLocal() ? ipv6Local = a.toString() : ipv6Global = a.toString();
}
#endif
if (server.arg(0) != "") {
WiFi.hostname(server.arg(0));
File f = LittleFS.open(existFolder("Config") + "/config.json", "w"); // Datei zum schreiben öffnen
f.printf("\"%s\"\n", WiFi.hostname().c_str());
f.close();
}
String temp = "{\"File\":\"" + sketchName() + "\", \"Build\":\"" + __DATE__ + " " + __TIME__ + "\", \"SketchSize\":\"" + formatBytes(ESP.getSketchSize()) +
"\", \"SketchSpace\":\"" + formatBytes(ESP.getFreeSketchSpace()) + "\", \"LocalIP\":\"" + WiFi.localIP().toString() + "\", \"IPv6l\":\"" + ipv6Local +
"\", \"IPv6g\":\"" + ipv6Global + "\", \"Hostname\":\"" + WiFi.hostname() + "\", \"SSID\":\"" + WiFi.SSID() + "\", \"GatewayIP\":\"" + WiFi.gatewayIP().toString() +
"\", \"Channel\":\"" + WiFi.channel() + "\", \"MacAddress\":\"" + WiFi.macAddress() + "\", \"SubnetMask\":\"" + WiFi.subnetMask().toString() +
"\", \"BSSID\":\"" + WiFi.BSSIDstr() + "\", \"ClientIP\":\"" + server.client().remoteIP().toString() + "\", \"DnsIP\":\"" + WiFi.dnsIP().toString() +
"\", \"ResetReason\":\"" + ESP.getResetReason() + "\", \"CpuFreqMHz\":\"" + F_CPU / 1000000 + "\", \"FreeHeap\":\"" + formatBytes(ESP.getFreeHeap()) +
"\", \"HeapFrag\":\"" + ESP.getHeapFragmentation() + "\", \"ChipSize\":\"" + formatBytes(ESP.getFlashChipSize()) +
"\", \"ChipSpeed\":\"" + ESP.getFlashChipSpeed() / 1000000 + "\", \"ChipMode\":\"" + flashChipMode[ESP.getFlashChipMode()] +
"\", \"IdeVersion\":\"" + ARDUINO + "\", \"CoreVersion\":\"" + ESP.getCoreVersion() + "\", \"SdkVersion\":\"" + ESP.getSdkVersion() + "\"}";
server.send(200, "application/json", temp); // Json als Objekt
}
String runtime() {
static uint8_t rolloverCounter;
static uint32_t previousMillis;
uint32_t currentMillis {millis()};
if (currentMillis < previousMillis) rolloverCounter++; // prüft millis() auf Überlauf
previousMillis = currentMillis;
uint32_t sec {(0xFFFFFFFF / 1000) * rolloverCounter + (currentMillis / 1000)};
char buf[20];
snprintf(buf, sizeof(buf), "%*.d %.*s %02d:%02d:%02d", sec < 86400 ? 0 : 1, sec / 86400, sec < 86400 ? 0 : sec >= 172800 ? 4 : 3, "Tage", sec / 3600 % 24, sec / 60 % 60, sec % 60);
return buf;
}
Connect.ino
// ****************************************************************
// Sketch Esp8266 Connect Modular(Tab) mit optischer Anzeige
// created: Jens Fleischer, 2018-04-08
// last mod: Jens Fleischer, 2023-01-16
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 3.0.0 - 3.1.2
// Getestet auf: Nodemcu, Wemos D1 Mini Pro, Sonoff Dual
/******************************************************************
Copyright (c) 2018 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
// Diese Version von Connect sollte als Tab eingebunden werden.
// #include <ESP8266WebServer.h> oder #include <ESP8266WiFi.h> muss im Haupttab aufgerufen werden
// Die Funktion "connectWifi();" muss im Setup eingebunden werden.
/**************************************************************************************/
//#define CONFIG // Einkommentieren wenn der ESP dem Router die IP mitteilen soll.
#define NO_SLEEP // Auskommentieren wenn der Nodemcu den deep sleep Modus nutzt.
const char* ssid = "Netzwerkname"; // << kann bis zu 32 Zeichen haben
const char* password = "PasswortvomNetzwerk"; // << mindestens 8 Zeichen jedoch nicht länger als 64 Zeichen
#ifdef CONFIG
IPAddress staticIP(192, 168, 178, 99); // Statische IP des NodeMCU ESP8266
IPAddress gateway(192, 168, 178, 1); // IP-Adresse des Router
IPAddress subnet(255, 255, 255, 0); // Subnetzmaske des Netzwerkes
IPAddress dns(192, 168, 178, 1); // DNS Server
#endif
void connectWifi() { // Funktionsaufruf "connectWifi();" muss im Setup eingebunden werden.
byte i {0};
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
#ifdef CONFIG
WiFi.config(staticIP, gateway, subnet, dns);
#endif
while (WiFi.status() != WL_CONNECTED) {
#ifdef NO_SLEEP
pinMode(LED_BUILTIN, OUTPUT); // OnBoardLed Nodemcu, Wemos D1 Mini Pro
digitalWrite(LED_BUILTIN, 0);
#endif
delay(500);
digitalWrite(LED_BUILTIN, 1);
delay(500);
DEBUG_F(" %d sek\n", i);
if (++i > 9) {
DEBUG_P("\nVerbindung zum AP fehlgeschlagen !\n\n");
ESP.restart();
}
}
DEBUG_P("\nVerbunden mit: " + WiFi.SSID());
DEBUG_P("Esp8266 IP: " + WiFi.localIP().toString());
}
Daten_API.ino
// ****************************************************************
// Sketch Esp8266 Daten_API Modular(Tab)
// created: Jens Fleischer, 2020-02-11
// last mod: Jens Fleischer, 2023-04-08
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 2.6.0 - 3.1.2
// Getestet auf: Nodemcu
/******************************************************************
Copyright (c) 2020 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
// Diese Version von Connect sollte als Tab eingebunden werden.
// #include <ESP8266WebServer.h> muss im Haupttab aufgerufen werden
// Die Funktion "setupAPI();" muss im Setup eingebunden werden.
/**************************************************************************************/
void setupAPI() { // Funktionsaufruf "setupAPI();" muss im Setup eingebunden werden.
server.on("/heater/current", handleCurrent);
server.on("/heater/detail", handledetail);
server.on("/test", test);
}
const String buildJson(const char* name, Ds18b20 &device) {
String temp = "\"],\"";
temp += name;
temp += "\":[\"";
temp += device.minTime;
temp += "\",\"";
temp += device.maxTime;
temp += "\",\"";
temp += device.alwaysMinTime;
temp += "\",\"";
temp += device.alwaysMaxTime;
temp += "\",\"";
temp += device.currentMin;
temp += "\",\"";
temp += device.currentMax;
temp += "\",\"";
temp += device.alwaysMin;
temp += "\",\"";
temp += device.alwaysMax;
return temp;
}
void handledetail() {
String temp = "{\"status\":[\"";
temp += minmax;
temp += "\",\"";
temp += lt.minmaxTime;
temp += "\",\"";
temp += pushStatusAddWood;
temp += "\",\"";
temp += lcdStatus;
temp += "\",\"";
for (const auto& [key, value] : mixingTime) {
temp += static_cast<String>(key) + " x gemischt um " + value + "<br>";
}
temp += buildJson((char*)"KES", sensors.KES);
temp += buildJson((char*)"HVL", sensors.HVL);
temp += buildJson((char*)"HRL", sensors.HRL);
temp += buildJson((char*)"SWW", sensors.SWW);
temp += buildJson((char*)"SVL", sensors.SVL);
temp += buildJson((char*)"SRL", sensors.SRL);
temp += buildJson((char*)"ATE", sensors.ATE);
temp += "\"]}";
DEBUG_P("Json: " + temp);
serverResponse(temp);
}
void handleCurrent() {
String temp = "{\"SWW\":\"";
temp += sensors.SWW.current;
temp += "\", \"SVL\":\"";
temp += sensors.SVL.current;
temp += "\", \"SRL\":\"";
temp += sensors.SRL.current;
temp += "\", \"KES\":\"";
temp += sensors.KES.current;
temp += "\", \"HVL\":\"";
temp += sensors.HVL.current;
temp += "\", \"HRL\":\"";
temp += sensors.HRL.current;
temp += "\"}";
serverResponse(temp);
}
void test() {
String temp = "{\"pushStatusAddWood\":\"";
temp += pushStatusAddWood;
temp += "\", \"minmax\":\"";
temp += minmax;
temp += "\", \"releaseAddWood\":\"";
temp += releaseAddWood;
temp += "\"}";
serverResponse(temp);
}
void serverResponse(String &temp) {
server.sendHeader("Cache-Control", "no-cache, no-store");
server.send(200, "application/json", temp);
}
HttpClient.ino
// ****************************************************************
// Sketch HttpClient Modular(Tab)
// created: Jens Fleischer, 2018-06-29
// last mod: Jens Fleischer, 2020-10-31
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: ESP8266
// D3 = GPIO0 Anschluss Taster vom GPIO0 auf GND
// D4 = GPIO2 Anschluss Taster vom GPIO2 auf GND
// Software: Esp8266 Arduino Core 2.5.2 - 3.1.2
// Getestet auf: Nodemcu, Wemos D1 Mini Pro
/******************************************************************
Copyright (c) 2018 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
// Diese Version von HttpClient sollte als Tab eingebunden werden.
// #include <ESP8266WebServer.h> oder #include <ESP8266WiFi.h> muss im Haupttab aufgerufen werden
// Zum schalten muss die Funktion "getbutton();" im loop(); aufgerufen werden.
/**************************************************************************************/
#include <ESP8266HTTPClient.h>
constexpr uint8_t BUTTON[] {D4, D3}; // Pin für Taster einstellen
void setupButton() {
for (auto &pin : BUTTON) pinMode(pin, INPUT_PULLUP);
}
void getButton() {
bool currentStatus[2] {0, 0};
static bool previousStatus[2] {0, 0};
static uint32_t debounceMillis;
uint32_t currentMillis {millis()};
if (currentMillis - debounceMillis >= 50) { // 50 ms Taster Entprellzeit
debounceMillis = currentMillis;
for (auto i = 0; i < 2; i++) {
currentStatus[i] = !digitalRead(BUTTON[i]);
if (!currentStatus[i] && currentStatus[i] != previousStatus[i]) {
httpClientLight(i);
}
previousStatus [i] = currentStatus[i];
}
}
}
void httpClientLight(const byte &message) {
const char* URL {"http://192.168.178.38/timer?tog="}; // URL des ESP... für Aussenbeleuchtung
if (WiFi.status() == WL_CONNECTED) {
WiFiClient client;
HTTPClient http;
DEBUG_F(PSTR("\nFunktion: %s meldet in Zeile: %d -> HttpClient Begin\n"), __PRETTY_FUNCTION__, __LINE__);
DEBUG_P((String)URL + message);
http.begin(client, (String)URL + message);
int16_t httpCode = http.GET();
if (httpCode > 0) {
DEBUG_P("HTTP / 1.1 " + static_cast<String>(httpCode));
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
DEBUG_P("Antwort: " + payload);
bool smallSize = payload.substring(2, payload.length()).toInt();
bool largeSize = payload.substring(6, payload.length()).toInt();
showStatusLight(smallSize, largeSize);
}
}
else {
DEBUG_P("Fehler: " + http.errorToString(httpCode));
}
http.end();
}
}
LCD.ino
// ****************************************************************
// Sketch Esp8266 LCD Modular(Tab)
// created: Jens Fleischer, 2020-02-11
// last mod: Jens Fleischer, 2023-01-23
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266, LCD MODUL 4X20
// Software: Esp8266 Arduino Core 3.0.0 - 3.1.2
// Getestet auf: Nodemcu
/******************************************************************
Copyright (c) 2020 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
#include <LiquidCrystal_I2C.h> // https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
LiquidCrystal_I2C lcd(0x3F, 20, 4);
void initLcd() {
lcd.begin();
lcd.backlight();
server.on("/lcd", lcdBacklight);
server.on("/clear", []() {
server.send(304, "message/http");
lcd.clear();
});
}
void lcdBacklight() { // LCD Beleuchtung An/Aus
lcdStatus = !lcdStatus;
handledetail();
}
void showTime() {
static bool previousStatus {true};
lcd.setCursor(12, 3);
lcd.printf("%s", lt.timeOfDay); // Uhrzeit auf LCD ausgeben
if (lcdStatus != previousStatus) {
previousStatus = lcdStatus;
lcd.setBacklight(lcdStatus); // LCD Hintergrundbeleuchtung schalten.
}
}
void showTemperatures() { // aktuelle Temperaturen auf LCD ausgeben
lcd.setCursor(0, 0);
lcd.printf("WW %.2f%cC", convert(sensors.SWW.current), 0xDF);
lcd.setCursor(0, 1);
lcd.printf("VL %.2f%cC", convert(sensors.SVL.current), 0xDF);
lcd.setCursor(0, 2);
lcd.printf("RL %.2f%cC", convert(sensors.SRL.current), 0xDF);
lcd.setCursor(0, 3);
if (sensors.KES.current < 12800) {
lcd.printf("KE %.2f%cC", convert(sensors.KES.current), 0xDF);
}
else {
lcd.printf("KE %.1f%cC", convert(sensors.KES.current), 0xDF);
}
lcd.setCursor(11, 0);
lcd.printf("HV %.1f%cC", convert(sensors.HVL.current), 0xDF);
lcd.setCursor(11, 2);
lcd.printf("AT %4.1f%cC", convert(sensors.ATE.current), 0xDF);
}
void showStatusAddWood(int16_t &threshold) {
lcd.setCursor(11, 1);
if (pushStatusAddWood && threshold > 9664) {
if (static uint8_t logState, viewsCount; !(++viewsCount, viewsCount %= 4)) {
(logState = !logState) ? lcd.printf("FN %4.1f%cC", convert(threshold), 0xDF) : lcd.printf("HR %4.1f%cC", convert(sensors.HRL.current), 0xDF);
}
}
else {
lcd.printf("HR %4.1f%cC", convert(sensors.HRL.current), 0xDF);
}
}
void showStatusLight(bool &smallSize, bool &largeSize) { // Anzeige Aussenbeleuchtung auf LCD ausgeben
lcd.setCursor(10, 0);
smallSize ? lcd.printf("%c", (char)255) : lcd.printf(" ") ;
lcd.setCursor(11, 2);
smallSize ? lcd.printf("10 ON") : lcd.printf(" ") ;
DEBUG_P(smallSize ? "10 Watt an" : "10 Watt aus");
lcd.setCursor(17, 2);
largeSize ? lcd.printf("30 ON") : lcd.printf(" ") ;
DEBUG_P(largeSize ? "30 Watt an" : "30 Watt aus");
}
LittleFS.ino
// ****************************************************************
// Sketch Esp8266 Filesystem Manager spezifisch sortiert Modular(Tab)
// created: Jens Fleischer, 2020-06-08
// last mod: Jens Fleischer, 2023-01-14
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 3.1.0 - 3.1.2
// Geprüft: von 1MB bis 2MB Flash
// Getestet auf: Nodemcu
/******************************************************************
Copyright (c) 2020 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
// Diese Version von LittleFS sollte als Tab eingebunden werden.
// #include <LittleFS.h> #include <ESP8266WebServer.h> müssen im Haupttab aufgerufen werden
// Die Funktionalität des ESP8266 Webservers ist erforderlich.
// "server.onNotFound()" darf nicht im Setup des ESP8266 Webserver stehen.
// Die Funktion "setupFS();" muss im Setup aufgerufen werden.
/**************************************************************************************/
#include <list>
#include <tuple>
const char WARNING[] PROGMEM = R"(<h2>Der Sketch wurde mit "FS:none" kompilliert!)";
const char HELPER[] PROGMEM = R"(<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="[]" multiple><button>Upload</button></form>Lade die fs.html hoch.)";
void setupFS() { // Funktionsaufruf "setupFS();" muss im Setup eingebunden werden
LittleFS.begin();
server.on("/format", formatFS);
server.on("/upload", HTTP_POST, sendResponce, handleUpload);
server.onNotFound([]() {
if (!handleFile(server.urlDecode(server.uri())))
server.send(404, "text/plain", "FileNotFound");
});
}
bool handleList() { // Senden aller Daten an den Client
FSInfo fs_info; LittleFS.info(fs_info); // Füllt FSInfo Struktur mit Informationen über das Dateisystem
Dir dir = LittleFS.openDir("/");
using namespace std;
using records = tuple<String, String, int>;
list<records> dirList;
while (dir.next()) { // Ordner und Dateien zur Liste hinzufügen
if (dir.isDirectory()) {
uint8_t ran {0};
Dir fold = LittleFS.openDir(dir.fileName());
while (fold.next()) {
ran++;
dirList.emplace_back(dir.fileName(), fold.fileName(), fold.fileSize());
}
if (!ran) dirList.emplace_back(dir.fileName(), "", 0);
}
else {
dirList.emplace_back("", dir.fileName(), dir.fileSize());
}
}
dirList.sort([](const records & f, const records & l) { // Dateien sortieren
if (server.arg(0) == "1") {
return get<2>(f) > get<2>(l);
} else {
for (uint8_t i = 0; i < 31; i++) {
if (tolower(get<1>(f)[i]) < tolower(get<1>(l)[i])) return true;
else if (tolower(get<1>(f)[i]) > tolower(get<1>(l)[i])) return false;
}
return false;
}
});
dirList.sort([](const records & f, const records & l) { // Ordner sortieren
if (get<0>(f)[0] != 0x00 || get<0>(l)[0] != 0x00) {
for (uint8_t i = 0; i < 31; i++) {
if (tolower(get<0>(f)[i]) < tolower(get<0>(l)[i])) return true;
else if (tolower(get<0>(f)[i]) > tolower(get<0>(l)[i])) return false;
}
}
return false;
});
String temp = "[";
for (auto& t : dirList) {
if (temp != "[") temp += ',';
temp += "{\"folder\":\"" + get<0>(t) + "\",\"name\":\"" + get<1>(t) + "\",\"size\":\"" + formatBytes(get<2>(t)) + "\"}";
}
temp += ",{\"usedBytes\":\"" + formatBytes(fs_info.usedBytes) + // Berechnet den verwendeten Speicherplatz
"\",\"totalBytes\":\"" + formatBytes(fs_info.totalBytes) + // Zeigt die Größe des Speichers
"\",\"freeBytes\":\"" + (fs_info.totalBytes - fs_info.usedBytes) + "\"}]"; // Berechnet den freien Speicherplatz
server.send(200, "application/json", temp);
return true;
}
void deleteRecursive(const String &path) {
if (LittleFS.remove(path)) {
LittleFS.open(path.substring(0, path.lastIndexOf('/')) + "/", "w");
return;
}
Dir dir = LittleFS.openDir(path);
while (dir.next()) {
deleteRecursive(path + '/' + dir.fileName());
}
LittleFS.rmdir(path);
}
bool handleFile(String &&path) {
if (server.hasArg("new")) {
String folderName {server.arg("new")};
for (auto& c : {
34, 37, 38, 47, 58, 59, 92
}) for (auto& e : folderName) if (e == c) e = 95; // Ersetzen der nicht erlaubten Zeichen
LittleFS.mkdir(folderName);
}
if (server.hasArg("sort")) return handleList();
if (server.hasArg("delete")) {
deleteRecursive(server.arg("delete"));
sendResponce();
return true;
}
if (!LittleFS.exists("fs.html")) server.send(200, "text/html", LittleFS.begin() ? HELPER : WARNING); // ermöglicht das hochladen der fs.html
if (path.endsWith("/")) path += "index.html";
return LittleFS.exists(path) ? ({File f = LittleFS.open(path, "r"); server.streamFile(f, mime::getContentType(path)); f.close(); true;}) : false;
}
void handleUpload() { // Dateien ins Filesystem schreiben
static File fsUploadFile;
HTTPUpload& upload = server.upload();
if (upload.status == UPLOAD_FILE_START) {
if (upload.filename.length() > 31) { // Dateinamen kürzen
upload.filename = upload.filename.substring(upload.filename.length() - 31, upload.filename.length());
}
printf(PSTR("handleFileUpload Name: /%s\n"), upload.filename.c_str());
fsUploadFile = LittleFS.open(server.arg(0) + "/" + server.urlDecode(upload.filename), "w");
} else if (upload.status == UPLOAD_FILE_WRITE) {
printf(PSTR("handleFileUpload Data: %u\n"), upload.currentSize);
fsUploadFile.write(upload.buf, upload.currentSize);
} else if (upload.status == UPLOAD_FILE_END) {
printf(PSTR("handleFileUpload Size: %u\n"), upload.totalSize);
fsUploadFile.close();
}
}
void formatFS() { // Formatiert das Filesystem
LittleFS.format();
sendResponce();
}
void sendResponce() {
server.sendHeader("Location", "fs.html");
server.send(303, "message/http");
}
const String formatBytes(size_t const& bytes) { // lesbare Anzeige der Speichergrößen
return bytes < 1024 ? static_cast<String>(bytes) + " Byte" : bytes < 1048576 ? static_cast<String>(bytes / 1024.0) + " KB" : static_cast<String>(bytes / 1048576.0) + " MB";
}
const String existFolder(const char* foldername) {
if (!LittleFS.exists(foldername)) LittleFS.mkdir(foldername);
return foldername;
}
void lightDataRecorder() {
static bool previouslogState {false};
bool logState = analogRead(A0) >= BRIGHTNESS;
if (logState != previouslogState) {
lcdStatus = logState; // Status der Hintergrundbeleuchtung ändern..
DEBUG_P(PSTR("====== LIGHT DATA LOGGING ======="));
if (File f = LittleFS.open(existFolder("Visits") + lt.fileName + "txt", "a")) {
logState ? f.printf("Licht an %s", lt.timeStamp) : f.printf(" aus %s\n", lt.timeOfDay);
f.close();
}
}
previouslogState = logState;
}
void archiveHeatUp() {
if (File f = LittleFS.open(existFolder("Flow") + "/anheizen.txt", "a")) { // Datei zum schreiben öffnen
f.printf("%s SWW %.2f SVL %.2f Max SVL %.2f SRL %.2f Max SRL %.2f HVL %.2f\n",
lt.timeStamp, convert(sensors.SWW.current), convert(sensors.SVL.current), convert(sensors.SVL.currentMax),
convert(sensors.SRL.current), convert(sensors.SRL.currentMax), convert(sensors.HVL.current));
f.close();
}
}
void archiveAddWood(int16_t &threshold) {
if (File f = LittleFS.open(existFolder("Flow") + "/nachlegen.txt", "a")) { // Datei zum schreiben öffnen
f.printf("%s SRL %.2f KES %.2f S %.2f\n",
lt.timeStamp, convert(sensors.SRL.current), convert(sensors.KES.current), convert(threshold));
f.close();
}
}
void archiveProcess(char record[]) {
if (File f = LittleFS.open(existFolder("Flow") + "/ablauf.txt", "a")) {
f.printf("%s %s\n", lt.timeStamp, record);
f.close();
}
}
bool toRead() { // Einlesen aller Daten falls die Datei im Filesystem vorhanden ist.
if (File f = LittleFS.open(existFolder("Data") + lt.fileName + "dat", "r")) {
DEBUG_P(PSTR("====== READ DATA LOGGING ======="));
f.read(reinterpret_cast<byte*>(&sensors), sizeof(sensors)); // Deserialisierung
f.close();
return true;
}
return false;
}
bool toSave() {
if (File f = LittleFS.open(existFolder("Data") + lt.fileName + "dat", "w")) { // Datei zum schreiben öffnen
DEBUG_P(PSTR("====== SAVE DATA LOGGING ======="));
f.write(reinterpret_cast<byte*>(&sensors), sizeof(sensors));
f.close();
return true;
}
return false;
}
Localtime.ino
// ****************************************************************
// Sketch Esp8266 Lokalzeit Modular(Tab)
// created: Jens Fleischer, 2018-07-10
// last mod: Jens Fleischer, 2021-01-09
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 2.6.0 - 3.1.2
// Getestet auf: Nodemcu, Wemos D1 Mini Pro, Sonoff Switch, Sonoff Dual
/******************************************************************
Copyright (c) 2018 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
// Diese Version von Lokalzeit sollte als Tab eingebunden werden.
// #include <ESP8266WebServer.h> oder #include <ESP8266WiFi.h> muss im Haupttab aufgerufen werden.
// Funktion "setupTime();" muss im setup() nach dem Verbindungsaufbau aufgerufen werden.
/**************************************************************************************/
#include <time.h>
struct tm tm;
const char* const PROGMEM monthShort[] = {"Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"};
void setupTime() { // Es ist kein NTP Server erforderlich wenn der Timestamp vom DHCP-Server(FritzBox) geholt werden kann.
setTZ("CET-1CEST,M3.5.0/02,M10.5.0/03"); // Zeitzone einstellen https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
}
uint32_t sntp_update_delay_MS_rfc_not_less_than_15000() {
return 12 * 36e5; // SNTP Update Intervall einstellen
}
void fileName() {
snprintf(lt.fileName, sizeof(lt.fileName), "/%u_%s.", 1900 + tm.tm_year, monthShort[tm.tm_mon]);
}
void localTime() {
static uint8_t lastmon {CHAR_MAX};
static time_t previous;
time_t now = time(&now);
if (now != previous) {
previous = now;
localtime_r(&now, &tm);
strftime (lt.timeOfDay, sizeof(lt.timeOfDay), "%T", &tm); // http://www.cplusplus.com/reference/ctime/strftime/
strftime (lt.timeStamp, sizeof(lt.timeStamp), "%T %d.%m.%Y", &tm); // http://www.cplusplus.com/reference/ctime/strftime/
showTime(); // Uhrzeit auf LCD ausgeben
if (tm.tm_mon != lastmon) {
lastmon = tm.tm_mon;
fileName();
}
}
}
Push.ino
// ****************************************************************
// Sketch Esp8266 PushBullet Modular(Tab)
// created: Jens Fleischer, 2018-06-26
// last mod: Jens Fleischer, 2021-01-09
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 2.7.0 - 3.1.2
// Getestet auf: Nodemcu, Wemos D1 Mini Pro, Sonoff Switch, Sonoff Dual
/******************************************************************
Copyright (c) 2018 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
// Diese Version von PushBullet sollte als Tab eingebunden werden.
// #include <ESP8266WebServer.h> oder #include <ESP8266WiFi.h> muss im Haupttab aufgerufen werden
// Die Übergabe der Nachricht an "pushbullet(string oder String-Object);" erfolgt als char oder String.
// Die Funktion "Connect();" muss im Setup eingebunden sein.
/**************************************************************************************/
#include <WiFiClientSecure.h> // für PushBullet
WiFiClientSecure secureClient;
bool pushbullet(const char* message) { // Push Nachricht senden
constexpr uint16_t TIMEOUT {2000}; // Zeit für Wartezeit auf Antwort in Millisekunden einstellen
const char* PushBulletAPIKEY = {"Key vom Pushbullet-Konto"}; // Konto Schlüssel einfügen http://pushbullet.com
const char* HOST {"api.pushbullet.com"};
uint32_t broadcastingTime {millis()};
secureClient.setInsecure();
if (!secureClient.connect(HOST, 443)) {
DEBUG_P("Pushbullet Verbindung fehlgeschlagen !");
return false;
}
else {
String messagebody = "{\"type\": \"note\", \"title\": \"Push vom ESP\", \"body\": \"" + (String)message + " " + lt.timeStamp + "\"}";
secureClient.printf(PSTR("POST /v2/pushes HTTP/1.1\r\nHost: %s\r\nAuthorization: Bearer %s\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n%s\r\n"),
HOST, PushBulletAPIKEY, messagebody.length(), messagebody.c_str());
DEBUG_P("Message gesendet");
}
while (!secureClient.available()) {
if (millis() - broadcastingTime > TIMEOUT) {
DEBUG_P("Pushbullet Client Timeout !");
secureClient.stop();
return false;
}
}
while (secureClient.available()) { //Emfängt Antwort
DEBUG_F("Pushbullet Antwort nach: %4ld ms\n", millis() - broadcastingTime); // zeigt die Zeit bis zur Antwort --> passe den Timeout entsprechend an
String line = secureClient.readStringUntil('\n');
DEBUG_F("Funktion: %s meldet in Zeile: %d Responce: %s\n", __PRETTY_FUNCTION__, __LINE__, line.c_str());
if (line.startsWith("HTTP/1.1 200 OK")) {
secureClient.stop();
return true;
}
}
return false;
}
Temperatur.ino
// ****************************************************************
// Sketch Esp8266 Temperatur Modular(Tab)
// created: Jens Fleischer, 2020-02-11
// last mod: Jens Fleischer, 2023-04-08
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp8266, DS18b20
// Software: Esp8266 Arduino Core 2.7.0 - 3.1.2
// Getestet auf: Nodemcu
// ****************************************************************
// Hardware: Esp8266, DS18B20, 4k7 Ohm Widerstand
// D6 = GPIO12 Anschluss DS18B20
// 4k7 Ohm Widerstand von VCC auf GPIO12
// Getestet auf: Nodemcu
/******************************************************************
Copyright (c) 2020 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
constexpr byte ONE_WIRE_PIN = D6; // Pin für Ds18b20 einstellen
OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature MaximDs18b20(&oneWire);
void tempsetup() {
MaximDs18b20.begin();
MaximDs18b20.setWaitForConversion(false);
MaximDs18b20.requestTemperatures();
server.on("/minmax", []() {
minmax ? archiveProcess((char*)"Max return durch Benutzer") : archiveProcess((char*)"Min return durch Benutzer");
minmax ? setMaxGroup() : setMinGroup(); // setzt MIN MAX Werte zurück
minmax = !minmax;
handledetail();
});
server.on("/statusAddWood", []() { // Sperrt die Push Nachricht zum Holz nachlegen
pushStatusAddWood ? archiveProcess((char*)"Push nachlegen false durch Benutzer") : archiveProcess((char*)"Push nachlegen true durch Benutzer");
pushStatusAddWood = !pushStatusAddWood;
releaseAddWood = false;
handledetail();
});
}
void measure(Ds18b20 &device) {
int16_t raw = MaximDs18b20.getTemp(device.address);
if (raw != DEVICE_DISCONNECTED_RAW) {
device.current = raw;
if (device.current < device.currentMin) { //Min Wert ermitteln
device.currentMin = device.current;
strcpy(device.minTime, lt.timeOfDay);
}
if (device.current > device.currentMax) { //Max Wert ermitteln
device.currentMax = device.current;
strcpy(device.maxTime, lt.timeOfDay);
}
if (device.currentMin < device.alwaysMin) { //Langzeit Min Werte ermitteln
device.alwaysMin = device.currentMin;
strcpy(device.alwaysMinTime, lt.timeStamp);
}
if (device.currentMax > device.alwaysMax) { //Langzeit Max Werte ermitteln
device.alwaysMax = device.currentMax;
strcpy(device.alwaysMaxTime, lt.timeStamp);
}
}
MaximDs18b20.requestTemperaturesByAddress(device.address);
}
void queryTemp() {
static uint8_t passCounter;
switch (++passCounter) {
case 1:
measure(sensors.KES);
break;
case 2:
measure(sensors.SWW);
break;
case 3:
measure(sensors.SVL);
break;
case 4:
measure(sensors.SRL);
break;
case 5:
measure(sensors.HVL);
break;
case 6:
measure(sensors.ATE);
break;
case 7:
measure(sensors.HRL);
break;
default:
showTemperatures(); // Temperatur auf LCD ausgeben
passCounter = 0;
#ifdef TEMPDEBUG
debugTemp();
#endif
}
}
void getKlima(uint16_t &klimaComp) {
static uint8_t previousDay {UCHAR_MAX};
if (tm.tm_hour == 2 && tm.tm_wday != previousDay) {
previousDay = tm.tm_wday;
switch (sensors.ATE.current) {
case 15 ... 44:
klimaComp = 0;
break;
case 0 ... 14:
klimaComp = OPERAND;
break;
default:
klimaComp = OPERAND * 2;
}
toSave(); // Min Max Werte speichern
}
}
void process() {
static bool pushStatusHeated {false};
static uint16_t klimaComp {0};
static int16_t threshold;
static uint8_t passCounter;
switch (++passCounter) {
case 1:
sendHeatUp(pushStatusHeated);
break;
case 2:
heated(pushStatusHeated, klimaComp);
break;
case 3:
sendAddWood(threshold, klimaComp);
break;
case 4:
heaterOff(pushStatusHeated, klimaComp);
break;
case 5:
storageMixed();
break;
case 6:
showStatusAddWood(threshold);
break;
default:
lightDataRecorder();
getKlima(klimaComp);
passCounter = 0;
}
}
void setMin(Ds18b20 &device) { // Min Werte zurücksetzen
device.currentMin = device.current;
strcpy(device.minTime, lt.timeOfDay);
}
void setMinGroup() {
setMin(sensors.KES);
setMin(sensors.HVL);
setMin(sensors.HRL);
setMin(sensors.SWW);
setMin(sensors.SVL);
setMin(sensors.SRL);
strcpy(lt.minmaxTime, lt.timeOfDay);
}
void setMax(Ds18b20 &device) { // Max Werte zurücksetzen
device.currentMax = device.current;
strcpy(device.maxTime, lt.timeOfDay);
}
void setMaxGroup() {
setMax(sensors.KES);
setMax(sensors.HVL);
setMax(sensors.HRL);
setMax(sensors.SWW);
setMax(sensors.SVL);
setMax(sensors.SRL);
strcpy(lt.minmaxTime, lt.timeOfDay);
}
/*-------- Erkennung ob angeheizt code ----------*/
void heated(bool &pushStatusHeated, uint16_t &klimaComp) {
if (minmax && (sensors.KES.current > sensors.KES.currentMin + 5 * OPERAND - klimaComp)) { //Erkennt das angeheitzt wurde //klimaCompensation -> 0 warm 1 kalt 2 sehr kalt
setMaxGroup(); //Max Werte zurücksetzen
archiveProcess((char*)"Push anheizen false - Push nachlegen true - Ofen angeheizt - Max return");
minmax = !minmax;
pushStatusAddWood = true; // Gibt die Benachrichtigung für nachlegen frei, wenn angeheizt wurde.
pushStatusHeated = true; // Sperrt die Benachrichtigung für anheizen.
}
}
/*-------- Push Nachlegen Kessel code ----------*/
void sendAddWood(int16_t &threshold, uint16_t &klimaComp) {
// Rücklauf unter 81°
// Kessel fällt auf 75,5° + z Grad
// Merken wenn Rücklauf über 81° freigabe löschen
if (pushStatusAddWood && (sensors.SRL.current < 80 * OPERAND + klimaComp / 2)) { // klimaCompensation -> 0 warm 1 kalt 2 sehr kalt
if (sensors.SRL.current < 61 * OPERAND) threshold = 9664; // entspricht 75.5C
else if (sensors.SRL.current < 70 * OPERAND) threshold = 9792; // entspricht 76.5C
else if (sensors.SRL.current < 74 * OPERAND) threshold = 9920; // entspricht 77.5C
else if (sensors.SRL.current < 76 * OPERAND) threshold = sensors.SRL.current + 512; // entspricht SRL + 4C
else if (sensors.SRL.current >= 76 * OPERAND) threshold = sensors.SRL.current + 640; // entspricht SRL + 5C
if (releaseAddWood && (sensors.KES.current <= threshold)) {
releaseAddWood = !releaseAddWood;
if (analogRead(A0) < BRIGHTNESS) { //nur wenn Licht aus
if (pushbullet("Holz nachlegen")) { //Push Nachricht wenn Kesseltemperatur auf z gefallen
archiveAddWood(threshold);
archiveProcess((char*)"Push nachlegen gesendet");
}
else {
archiveProcess((char*)"Push nachlegen Senden fehlgeschlagen");
}
}
}
if (!releaseAddWood && (sensors.KES.current > threshold + 1 * OPERAND)) {
releaseAddWood = !releaseAddWood;
}
}
if ((releaseAddWood && (sensors.SRL.current >= 80 * OPERAND + klimaComp)) || (releaseAddWood && (sensors.SRL.current + 15 * OPERAND) < sensors.SRL.currentMax)) { // sperrt Nachlegen bis zum nächsten Anheizen
archiveProcess((char*)"Push nachlegen false");
releaseAddWood = false;
pushStatusAddWood = false;
}
}
/*-------- Push Anheizen Kessel code ----------*/
void sendHeatUp(bool &pushStatusHeated) {
if (!pushStatusHeated && sensors.SVL.current >= sensors.SWW.current - 1 * OPERAND && sensors.SWW.current < 73 * OPERAND) { // 17.12.18 von 2 auf 1
if (analogRead(A0) < BRIGHTNESS && sensors.SRL.currentMax > (sensors.SRL.current + 15 * OPERAND)) { //nur wenn Licht aus
if (pushbullet("Anheizen")) {
pushStatusHeated = true; // wird von Ofen aus freigegeben
archiveHeatUp();
archiveProcess((char*)"Push Anheizen gesendet");
}
else {
static bool secound {0};
if (secound) {
secound = false;
pushStatusHeated = true;
}
else {
secound = true;
}
archiveProcess((char*)"Push Anheizen Senden fehlgeschlagen");
}
}
}
}
/*-------- Erkennung Ofen aus code ----------*/
void heaterOff(bool &pushStatusHeated, uint16_t &klimaComp) {
if (!minmax && sensors.SVL.currentMax > (sensors.SVL.current + 7 * OPERAND - klimaComp)) { // klimaCompensation -> 0 warm 1 kalt 2 sehr kalt
setMinGroup(); // Min Werte zurücksetzen
archiveProcess((char*)"Push anheizen true - Ofen aus - Min return");
minmax = !minmax;
pushStatusHeated = false; // gibt Push anheizen frei
}
}
/*-------- Abfrage Speicher gemischt Funktion ----------*/
void storageMixed() {
static unsigned long startzeit1, startzeit2;
static int16_t previousValue = INT16_MAX;
static uint8_t counter {0};
static bool flag {true}; // Hilfsmerker für Speicher mischen
if (minmax) { // minmax (true)kommt von Ofen aus, wird durch angeheizt (false), gibt Speicher gemischt frei ist global
if (flag) { // Abfrage Speicher gemischt
if (millis() - startzeit1 >= 60000) {
startzeit1 = millis();
previousValue = sensors.SVL.current;
}
if (sensors.SVL.current > previousValue + OPERAND) {
counter ++;
flag = false;
mixingTime[counter] = lt.timeOfDay;
char mixing[15];
snprintf(mixing, sizeof(mixing), "%d x gemischt", counter);
archiveProcess(mixing);
}
}
else {
if (millis() - startzeit2 >= 240000) {
startzeit2 = millis();
previousValue = sensors.SVL.current;
}
if (sensors.SVL.current < previousValue - OPERAND / 2) {
flag = true;
previousValue = INT16_MAX;
}
}
}
else { // minmax (false)kommt von angeheizt, wird durch Ofen aus (true), gibt Speicher gemischt frei ist global
counter = 0;
mixingTime.clear();
}
}
#ifdef TEMPDEBUG
void printDevice(Ds18b20 &device) {
DEBUG_F(" Akt: %.2f\n", convert(device.current));
DEBUG_F(" Min: %.2f\n", convert(device.alwaysMin));
DEBUG_F(" Min: %s\n", device.alwaysMinTime);
DEBUG_F(" Max: %.2f\n", convert(device.alwaysMax));
DEBUG_F(" Max: %s\n", device.alwaysMaxTime);
}
void debugTemp() {
DEBUG_P("KES");
printDevice(sensors.KES);
DEBUG_P("HVL");
printDevice(sensors.HVL);
DEBUG_P("HRL");
printDevice(sensors.HRL);
DEBUG_P("SWW");
printDevice(sensors.SWW);
DEBUG_P("SVL");
printDevice(sensors.SVL);
DEBUG_P("SRL");
printDevice(sensors.SRL);
DEBUG_P("ATE");
printDevice(sensors.ATE);
}
#endif
Thingspeak.ino
// ****************************************************************
// Sketch Thingspeak Modul(Tab) nicht blockierend
// created: Jens Fleischer, 2018-04-08
// last mod: Jens Fleischer, 2021-01-09
// ****************************************************************
// Hardware: Esp8266
// Software: Esp8266 Arduino Core 2.4.2 - 3.1.2
// Getestet auf: Nodemcu
/******************************************************************
Copyright (c) 2018 Jens Fleischer. All rights reserved.
This file is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This file 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 GNU
Lesser General Public License for more details.
*******************************************************************/
WiFiClient client;
bool thingspeak() { // Funktionsaufruf "thingspeak();" muss in loop eingebunden sein.
constexpr uint16_t HTTPPORT {80};
constexpr uint32_t INTERVAL {1000UL * 16}; // Sendeinterval in Sekunden einstellen.
constexpr uint16_t TIMEOUT {1600}; // Zeitraum für Fehlerrückgabe in Millisekunden einstellen.
const char* HOST {"184.106.153.149"};
const char* TS_KEY = {"Geheimer ApiKey"}; // trage deinen ApiKey von https://thingspeak.com
static bool error {false}, response {true};
static unsigned long previousMillis {0}; // Sendebeginn einstellen (" 0 - interval" = sofort, "0" = nach Ablauf von Interval)
/*************** Thinkspeak senden *********************/
uint32_t currentMillis {millis()};
if (currentMillis - previousMillis >= INTERVAL || error) { // Senden im Interval und erneut im Fehlerfall.
error = false; // oder bei erfolgreicher Antwort
previousMillis = currentMillis;
if (!client.connect(HOST, HTTPPORT)) {
DEBUG_P("Thingspeak Verbindung fehlgeschlagen !");
error = true;
return false;
}
else {
char buf[146];
snprintf(buf, sizeof(buf), "&field1=%.2f&field2=%.2f&field3=%.2f&field4=%.2f&field5=%.2f&field6=%.2f&field7=%.2f&field8=%.2f",
convert(sensors.SWW.current), convert(sensors.SVL.current), convert(sensors.KES.current), convert(sensors.SRL.current),
convert(sensors.SWW.current), convert(sensors.SVL.current), convert(sensors.KES.current), convert(sensors.SRL.current));
DEBUG_P(buf);
client.printf("POST /update?key=%s%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", TS_KEY, buf, HOST);
response = false;
return true;
}
}
/************** Thinkspeak auf Anwort prüfen *****************/
if (!response) {
if (millis() - previousMillis > TIMEOUT) {
DEBUG_P("Thingspeak Client Timeout !");
error = true;
client.stop();
return false;
}
if (client.available()) { // einlesen der Antwort
String line = client.readStringUntil('\n');
line.trim();
DEBUG_F("Funktion: %s meldet in Zeile: %d Responce: %s\n", __PRETTY_FUNCTION__, __LINE__, line.c_str());
if (line == "HTTP/1.1 200 OK") { // wenn Antwort mit "HTTP/1.1 200 OK" beginnt war das Senden erfolgreich
client.stop();
response = true;
}
}
}
return true;
}
admin.html
<!DOCTYPE HTML> <!-- For more information visit: https://fipsok.de -->
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>ESP8266 Admin</title>
<script>
addEventListener('load', () => {
renew(), once();
let output = document.querySelector('#note');
let btn = document.querySelectorAll('button');
let span = document.querySelectorAll('#right span');
btn[0].addEventListener('click', () => {
location = '/fs.html';
});
btn[1].addEventListener('click', () => {
location = '/';
});
btn[2].addEventListener('click', check.bind(this, document.querySelector('input')));
btn[3].addEventListener('click', re.bind(this, 'reconnect'));
btn[4].addEventListener('click', () => {
if (confirm('Bist du sicher!')) re('restart');
});
async function once(val = '',arg) {
try {
let resp = await fetch('/admin/once', { method: 'POST', body: val});
let obj = await resp.json();
output.innerHTML = '';
output.classList.remove('note');
document.querySelector('form').reset();
if (val.length == 0) myIv = setInterval(renew, 1000);
if (arg == 'reconnect') re(arg);
span[3].innerHTML = obj['File'];
span[4].innerHTML = obj['Build'];
span[5].innerHTML = obj['SketchSize'];
span[6].innerHTML = obj['SketchSpace'];
span[7].innerHTML = obj['LocalIP'];
span[8].innerHTML = obj['IPv6l'] ? obj['IPv6l'] : 'inaktiv';
span[9].innerHTML = obj['IPv6g'] ? obj['IPv6g'] : 'inaktiv';
span[10].innerHTML = obj['Hostname'];
span[11].innerHTML = obj['SSID'];
span[12].innerHTML = obj['GatewayIP'];
span[13].innerHTML = obj['Channel'];
span[14].innerHTML = obj['MacAddress'];
span[15].innerHTML = obj['SubnetMask'];
span[16].innerHTML = obj['BSSID'];
span[17].innerHTML = obj['ClientIP'];
span[18].innerHTML = obj['DnsIP'];
span[19].innerHTML = obj['ResetReason'];
span[20].innerHTML = obj['CpuFreqMHz'] + " MHz";
span[21].innerHTML = obj['FreeHeap'];
span[22].innerHTML = obj['HeapFrag'] + "%";
span[23].innerHTML = obj['ChipSize'];
span[24].innerHTML = obj['ChipSpeed'] + " MHz";
span[25].innerHTML = obj['ChipMode'];
span[26].innerHTML = obj['IdeVersion'].replace(/(\d)(\d)(\d)(\d)/,obj['IdeVersion'][3]!=0 ? '$1.$3.$4' : '$1.$3.');
span[27].innerHTML = obj['CoreVersion'].replace(/_/g,'.');
span[28].innerHTML = obj['SdkVersion'];
Object.keys(obj).forEach(val => {
if (obj[val].length > 25) document.querySelectorAll(`[data-${val}]`).forEach(el => {el.classList.add('ip')});
});
} catch(err) {
re();
}
}
async function renew() {
const resp = await fetch('admin/renew');
const array = await resp.json();
array.forEach((v, i) => {span[i].innerHTML = v});
}
function check(inObj) {
!inObj.checkValidity() ? (output.innerHTML = inObj.validationMessage, output.classList.add('note')) : (once(inObj.value, 'reconnect'));
}
function re(arg = '') {
clearInterval(myIv);
fetch(arg);
output.classList.add('note');
if (arg == 'restart') {
output.innerHTML = 'Der Server wird neu gestartet. Die Daten werden in 15 Sekunden neu geladen.';
setTimeout(once, 15000);
}
else if (arg == 'reconnect'){
output.innerHTML = 'Die WiFi Verbindung wird neu gestartet. Daten werden in 10 Sekunden neu geladen.';
setTimeout(once, 10000);
}
else {
output.innerHTML = 'Es ist ein Verbindungfehler aufgetreten. Es wird versucht neu zu verbinden.';
setTimeout(once, 3000);
}
}
});
</script>
</head>
<body>
<h1>ESP8266 Admin Page</h1>
<main>
<aside id="left">
<span>Runtime ESP:</span>
<span>WiFi RSSI:</span>
<span>ADC/VCC:</span>
<span>Sketch Name:</span>
<span>Sketch Build:</span>
<span>SketchSize:</span>
<span>FreeSketchSpace:</span>
<span>IPv4 Address:</span>
<span data-ipv6l>Link-Local:</span>
<span data-ipv6g>IPv6:</span>
<span>Hostname:</span>
<span>Connected to:</span>
<span>Gateway IP:</span>
<span>Channel:</span>
<span>MacAddress:</span>
<span>SubnetMask:</span>
<span>BSSID:</span>
<span data-clientip>Client IP:</span>
<span>DnsIP:</span>
<span>Reset Ground:</span>
<span>CPU Freq:</span>
<span>FreeHeap:</span>
<span>Heap Fragmentation:</span>
<span>FlashSize:</span>
<span>FlashSpeed:</span>
<span>FlashMode:</span>
<span>Arduino IDE Version:</span>
<span>Esp Core Version:</span>
<span>SDK Version:</span>
</aside>
<aside id="right">
<span>0</span>
<div>
<span></span>
dBm
</div>
<span>0</span>
<span>?</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span data-ipv6l>0</span>
<span data-ipv6g>0</span>
<span>?</span>
<span>?</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span data-clientip>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
<span>0</span>
</aside>
</main>
<div>
<button>Filesystem</button>
<button>Startseite</button>
</div>
<div id="note"></div>
<div>
<form>
<input placeholder="neuer Hostname" pattern="([A-Za-z0-9\-]{1,32})" title="Es dürfen nur Buchstaben (a-z, A-Z), Ziffern (0-9) und Bindestriche (-) enthalten sein. Maximal 32 Zeichen" required>
<button type="button">Name Senden</button>
</form>
</div>
<div>
<button>WiFi Reconnect</button>
<button>ESP Restart</button>
</div>
</body>
</html>
fs.html
<!DOCTYPE HTML> <!-- For more information visit: https://fipsok.de -->
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<title>Filesystem Manager</title>
<script>
document.addEventListener('DOMContentLoaded', () => {
list(JSON.parse(localStorage.getItem('sortBy')));
btn.addEventListener('click', () => { if (!confirm(`Wirklich formatieren? Alle Daten gehen verloren.\nDu musst anschließend fs.html wieder laden.`)) event.preventDefault()});
});
async function list(by, to = '/'){
let resp = await fetch(`?sort=${by}`);
let json = await resp.json();
let myList = document.querySelector('main'), noted = '';
myList.innerHTML = '<nav><input type="radio" id="/" name="group"><label for="/"> 📁</label><span id="cr">+📁</nav></span><span id="si"></span>';
document.querySelector('form').setAttribute('action', `/upload?f=${to}`);
for (var i = 0; i < json.length - 1; i++) {
let dir = '', f = json[i].folder, n = json[i].name;
if (f != noted) {
noted = f;
dir = `<nav><input type="radio" id="${f}" name="group"><label for="${f}"></label> 📁 ${f} <a href="?delete=/${f}">🗑️</a></nav>`;
}
if (n != '') dir += `<li><a href="${f}/${n}">${n}</a><small> ${json[i].size}</small><a href="${f}/${n}"download="${n}"> Download</a> or<a href="?delete=${f}/${n}"> Delete</a>`;
myList.insertAdjacentHTML('beforeend', dir);
}
myList.insertAdjacentHTML('beforeend', `<li><b id="so">${by ? '▼' : '▲'} LittleFS</b> belegt ${json[i].usedBytes.replace(".00", "")} von ${json[i].totalBytes.replace(".00", "")}`);
var free = json[i].freeBytes;
cr.addEventListener('click', () => {
document.getElementById('no').classList.toggle('no');
});
so.addEventListener('click', () => {
list(by = ++by % 2, to);
localStorage.setItem('sortBy', JSON.stringify(by));
});
fs.addEventListener('change', e => {
for (var bytes = 0, i = 0; i < e.target.files.length; i++) bytes += e.target.files[i].size;
for (var output = `${bytes} Byte`, i = 0, circa = bytes / 1024; circa > 1; circa /= 1024) output = circa.toFixed(2) + [' KB', ' MB', ' GB'][i++];
if (bytes > free) {
si.innerHTML = `<li><b> ${output}</b><strong> Ungenügend Speicher frei</strong></li>`;
up.setAttribute('disabled', 'disabled');
}
else {
si.innerHTML = `<li><b>Dateigröße:</b> ${output}</li>`;
up.removeAttribute('disabled');
}
});
let node = document.querySelectorAll('main input');
node.forEach(n => { if (n.id === to) n.setAttribute('checked', 'checked')});
node.forEach(n => {
n.addEventListener('change', e => {
if (e.target.checked) {
to = e.target.id
document.querySelector('form').setAttribute('action', `/upload?f=${to}`);
}
});
});
document.querySelectorAll('[href^="?delete=/"]').forEach(node => { node.addEventListener('click', () => { if (!confirm('Sicher!')) event.preventDefault()})});
document.querySelectorAll('main input').forEach(n => { if (n.id === to) n.setAttribute('checked', 'checked')});
}
</script>
</head>
<body>
<h2>ESP8266 Filesystem Manager</h2>
<form method="post" enctype="multipart/form-data">
<input id="fs" type="file" name="up[]" multiple>
<button id="up" disabled>Upload</button>
</form>
<form id="no" class="no" method="post">
<input name="new" placeholder="Ordner Name" pattern="[^\x22\/%&\\:;]{0,31}[^\x22\/%&\\:;\s]{1}" title="Zeichen “ % & / : ; \ sind nicht erlaubt." required>
<button>Create</button>
</form>
<main></main>
<form action="/format" method="post">
<button id="btn">Format LittleFS</button>
</form>
</body>
</html>
style.css
/* For more information visit:https://fipsok.de */
body {
font-family: sans-serif;
background-color: #87cefa;
display: flex;
flex-flow: column;
align-items: center;
}
h1,h2 {
color: #e1e1e1;
text-shadow: 2px 2px 2px black;
}
li {
background-color: #feb1e2;
list-style-type: none;
margin-bottom: 10px;
padding: 2px 5px 1px 0;
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
}
li a:first-child, li b {
background-color: #8f05a5;
font-weight: bold;
color: white;
text-decoration:none;
padding: 2px 5px;
text-shadow: 2px 2px 1px black;
cursor:pointer;
}
li strong {
color: red;
}
input {
height:35px;
font-size:14px;
padding-left: .3em;
}
label + a {
text-decoration: none;
}
h1 + main {
display: flex;
}
aside {
display: flex;
flex-direction: column;
padding: 0.2em;
}
button {
height:40px;
width:130px;
font-size:16px;
margin-top: 1em;
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
}
div button {
background-color: #7bff97;
}
nav {
display: flex;
align-items: baseline;
justify-content: space-between;
}
#left {
align-items:flex-end;
text-shadow: 0.5px 0.5px 1px #757474;
}
#cr {
font-weight: bold;
cursor:pointer;
font-size: 1.5em;
}
#up {
width: auto;
}
.note {
background-color: #fecdee;
padding: 0.5em;
margin-top: 1em;
text-align: center;
max-width: 320px;
border-radius: 0.5em;
}
.no {
display: none;
}
form [title] {
background-color: skyblue;
font-size: 1em;
width: 120px;
}
form:nth-of-type(2) {
margin-bottom: 1em;
}
[value*=Format] {
margin-top: 1em;
box-shadow: 5px 5px 5px rgba(0,0,0,0.7);
}
[name="group"] {
display: none;
}
[name="group"] + label {
font-size: 1.5em;
margin-right: 5px;
}
[name="group"] + label::before {
content: "\002610";
}
[name="group"]:checked + label::before {
content: '\002611\0027A5';
}
@media only screen and (max-width: 500px) {
.ip {
right: 6em;
position: relative;
}
aside {
max-width: 50vw;
}
}
nicht unmittelbar gesehen , der Compiler hat gemeckert
im Tab Push
Zeile 33
Fehlermeldung :
"erwartet ',' oder ';' vor 'c....'"
zum LCD Tab
"..... Pin Standard ESP8266 I2C "
wieder was gelernt !
aber mit Leerfeld kommt Fehler
Keine passende Funktion für den Aufruf von
'LiquidCrystal_......"
Ich benutze das NodeMCU Amiga ESP8266 mod 12F
als Bord "Generic ESP 8266 Module in der Arduino IDE.
Gruß Helmut
Antwort:
Das durch die Passwortersetzung auf dem Webserver unterschlagene Semikolon habe ich doch gefixt, lade bitte die Zip Datei oder die Seite neu, dann passt das wieder.
"lcd.begin();" klappt ohne Fehlermeldung wenn du die im Head des LCD Tab aufgeführte Library von Lucas Maziero verwendest.
Unverständlich für mich, warum nutzt man einen Nodemcu und stellt die IDE auf nicht näher spezifiziertes Board?
Gruß Fips
Wenn man mit über 60 Jahren von Bascom auf C bzw.
Arduino Programmierung umsteigen und ergänzen will
ist das mit den Termini nicht so einfach.
Mit Ports sind die Pins gemeint.
Die Umbenennung sollte kein Problem sein, nur als Hinweis
in deinen TAB's wird es unterschiedlich gehandhabt.
Aber nicht das Problem !!
Wenn C++ für mich auch noch viele Geheimnisse in sich
birgt , versuche ich im Web mehr Klarheit zu erhalten.
Bei den Lücken in Englisch hilft Google.
Zum Projekt Heizungsüberwachung :
Erster Start ok
Uplouds ok
Aufruf Admin ok
Aufruf Startseite -"FileNotFound"
Zurück ok
Server verlassen
Neustart
Anzeige FileNotFound
Button Zurück (Pfeil oben links) deaktiviert
Erneutes Flashen und Starten , wie bei Neustart
Warum ?
________________________________________________
Nur als Hinweis :
Fehlermeldungen beim erstmaligen kompilieren :
Tab HttpClient
const uint8_t BUTTON[] {D4, D3}; // Pin für Taster einstellen
Pins auf GPI umgestellt
________________________________________________________________
Tab LCD
void initLcd() {
lcd.begin();
mit 20,4 ergänzt
________________________________________________________________
Tab Push
bool pushbullet(const char* message) {
Mit ; ergänzt
________________________________________________________________
Tab Temperatur
const byte ONE_WIRE_PIN = D6
Pin auf GPI umgestellt
Gruß Helmut
Antwort:
Es gibt keine Startseite beim Projekt Heizung im Filesystem des Esp8266. Meine Startseite befindet sich Zentral für alle meine Esp auf einem Server, diese holt sich per Fetch Api die Daten von den jeweiligen Esp's.
Um die "D" Bezeichnungen der Pin's zu verwenden musst du vor dem Compilieren den richtigen Esp (bei mir NodeMcu) auswählen.
" lcd.begin();
mit 20,4 ergänzt"
Was bezweckst du damit?
Die Esp8266 haben keinen GPIO20!
An der Stelle überschreibst du die default Pin's für I2C des Esp8266.
Ausschnitt aus der verwendeten Library:
"void begin(uint8_t sda = SDA , uint8_t scl = SCL); // Int with pin default ESP8266 I2C "
"bool pushbullet(const char* message) {
Mit ; ergänzt"
Wo hast du da ein fehlendes Semikolon gesehen bzw. eingesetzt?
Gruß Fips
erstmal - eine "Hammer Seite!" , Danke dafür.
Als "Holzheizer" wäre dein Projekt Heizung für mich
wie gemacht.
Da das Projekt noch in Arbeit ist treten natürlich noch
Fehler auf.
Die Ports mussten umbenannt werden sowie ein ";"
hinzugefügt werden.
Nach dem Verbindungsaufbau wird im Browser allerdings
"file not found" angezeigt.
Hat das mit den spiffs zu tun?
Die Zeitschaltuhr funktioniert !
Gruß Helmut
Antwort:
Nanu, wo bist du denn? Hier gibt es doch gar kein Projekt.
";" War ein Fehler der automatischen Passwortersetzung auf dem Server. Ist behoben.
Welche Ports?
Das Projekt Heizung ist bereits von Spiffs auf LittleFS Filesystem umgestellt.
Gruß Fips