Esp32 Zeitschaltuhr Singel Arduino Tab.
Zeitschaltuhr.ino
// ****************************************************************
// Sketch Esp32 Schaltuhr Modular(Tab)
// created: Jens Fleischer, 2020-09-12
// last mod: Jens Fleischer, 2020-09-12
// For more information visit: https://fipsok.de
// ****************************************************************
// Hardware: Esp32, Relais Modul o. Mosfet IRF3708 o. Fotek SSR-40 DA
// für Relais Modul
// GND an GND
// IN an T7 = GPIO 27
// VCC an VIN -> je nach verwendeten Esp.. möglich
// Jumper JD-VCC VCC
// alternativ ext. 5V Netzteil verwenden
//
// für Mosfet IRF3708
// Source an GND
// Mosfet Gate an T7 = GPIO 27
//
// für 3V Solid State Relais
// GND an GND
// SSR Input + an T7 = GPIO 27
//
// Software: Esp32 Arduino Core 1.0.0 - 1.0.4
// Getestet auf: ESP32 NodeMCU-32s
/******************************************************************
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 Zeitschaltuhr sollte als Tab eingebunden werden.
// #include <SPIFFS.h> #include <WebServer.h> müssen im Haupttab aufgerufen werden
// Die Funktionalität des ESP32 Webservers ist erforderlich.
// Der Lokalzeit Tab ist zum ausführen der Zeitschaltuhr einzubinden.
// Die Funktion "setupTimerSwitch();" muss im Setup aufgerufen werden.
// Zum schalten muss die Funktion "timerSwitch();" im loop(); aufgerufen werden.
/**************************************************************************************/
#include <EEPROM.h>
const auto aktiv = LOW; // LOW für LOW aktive Relais oder HIGH für HIGH aktive (zB. SSR, Mosfet) einstellen
const uint8_t relPin = T7; // Pin für Relais einstellen
const auto count = 20; // Anzahl Schaltzeiten (analog Html Dokument) einstellen 1 bis 100
char switchTime[count * 2][6];
byte switchActive[count], wday[count];
bool fixed, relState {!aktiv};
void setupTimerSwitch() {
digitalWrite(relPin, !aktiv);
pinMode(relPin, OUTPUT);
EEPROM.begin(4);
EEPROM.get(1, fixed);
fixed = fixed > 0 ? 1 : 0;
DEBUG_F("Zeitschaltuhr Automatik %saktiviert\n", fixed ? "de" : "");
if (SPIFFS.exists("/swtime.dat")) { // Einlesen aller Daten falls die Datei im Spiffs vorhanden ist.
File f = SPIFFS.open("/swtime.dat");
f.read(switchActive, sizeof(switchActive));
f.read(wday, sizeof(wday));
while (f.read() != '\n');
for (auto& elem : switchTime) f.readBytesUntil('\n', elem, sizeof(elem));
f.close();
} else { // Sollte die Datei nicht existieren
for (auto i = 0; i < count; i++) {
switchActive[i] = 1; // werden alle Schaltzeiten
wday[i] = ~wday[i]; // und alle Wochentage aktiviert.
}
}
server.on("/timer", HTTP_POST, []() {
if (server.args() == 1) {
switchActive[server.argName(0).toInt()] = server.arg(0).toInt();
printer();
String temp = "\"";
for (auto& elem : switchActive) temp += elem;
temp += "\"";
server.send(200, "application/json", temp);
}
if (server.hasArg("sTime")) {
byte i {0};
char str[count * 14];
strcpy (str, server.arg("sTime").c_str());
char* ptr = strtok(str, ",");
while (ptr != NULL) {
strcpy (switchTime[i++], ptr);
ptr = strtok(NULL, ",");
}
if (server.arg("sDay")) {
i = 0;
strcpy (str, server.arg("sDay").c_str());
char* ptr = strtok(str, ",");
while (ptr != NULL) {
wday[i++] = atoi(ptr);
ptr = strtok(NULL, ",");
}
printer();
}
}
String temp = "[";
for (auto& elem : switchTime) {
if (temp != "[") temp += ',';
temp += (String)"\"" + elem + "\"";
}
temp += ",\"";
for (auto& elem : switchActive) {
temp += elem;
}
for (auto& elem : wday) {
temp += "\",\"";
temp += elem;
}
temp += "\"]";
server.send(200, "application/json", temp);
});
server.on("/timer", HTTP_GET, []() {
if (server.arg(0) == "tog") relState = !relState; // Relais1 Status manuell ändern
if (server.arg(0) == "fix") { // alle Schalzeiten deaktivieren/aktivieren
fixed = !fixed;
DEBUG_F("Zeitschaltuhr Automatik %saktiviert\n", fixed ? "de" : "");
EEPROM.put(1, fixed);
EEPROM.commit();
}
server.send(200, "application/json", (String)"[\"" + (relState == aktiv) + "\",\"" + localTime() + "\",\"" + fixed + "\"]");
});
}
void printer() {
File file = SPIFFS.open("/swtime.dat", "w");
if (file) {
file.write(switchActive, sizeof(switchActive));
file.write(wday, sizeof(wday));
for (auto& elem : switchTime) file.printf("\n%s", elem);
file.close();
}
}
void timerSwitch() {
static uint8_t lastmin {60}, lastState {aktiv};
localTime(); // Funktionsaufruf Uhrzeit aktualisieren
if (tm.tm_min != lastmin && !fixed) {
lastmin = tm.tm_min;
char buf[6];
sprintf(buf, "%.2d:%.2d", tm.tm_hour, tm.tm_min);
for (auto i = 0; i < count * 2; i++) {
if (switchActive[i / 2] && !strcmp(switchTime[i], buf)) {
if (wday[i / 2] & (1 << (tm.tm_wday ? tm.tm_wday - 1 : 6))) relState = i & 1 ? !aktiv : aktiv; // Relais Status nach Zeit ändern
}
}
}
if (relState != lastState) { // Relais schalten wenn sich der Status geändert hat
lastState = relState;
digitalWrite(relPin, relState);
DEBUG_F("Relais a%s\n", digitalRead(relPin) == aktiv ? "n" : "us");
}
}
Die Webseite zur Esp32 Singel Zeitschaltuhr.
zeitschaltuhr.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="style32.css">
<title>Timer</title>
<style>
main {
padding: .5em .5em .5em .1em;
box-shadow: 5px 3px 10px #4e4d4d;
background-color: #333;
color: #15dfdf;
}
div {
display: flex;
align-items: center;
justify-content: space-evenly;
}
span {
padding: 0.5em;
}
input {
background-color: inherit;
font-size: 3em;
color: inherit;
border: solid #555;
height: auto;
font-weight: bold;
}
label {
font-size: .9em;
font-style: italic;
color: #777;
}
input:checked+label {
color: #15dfdf;
}
div+span {
margin-left: 3em;
}
#top {
position: sticky;
top: 0;
background-color: #333;
z-index: 1;
}
#bottom {
position: sticky;
bottom: .3em;
padding: .5em 0 0 .5em;
}
time {
text-shadow: 1px 1px 1px #777;
font-size: 1.4em;
font-weight: bold;
}
#bu, #fix, #tog {
background-color: #333;
color: #33cccc;
cursor: pointer;
border: outset #999;
width: 100%;
margin: 0;
}
#tog {
width: 25%;
}
[name^=bu] {
width: 2em;
cursor: pointer;
}
.none {
color: #777 !important;
}
svg {
width: 3.5em;
}
u {
background-color: red !important;
position: sticky;
bottom: 4em;
}
.note:after {
content: "Schaltzeiten gespeichert";
color: #fff;
}
@media only screen and (max-width: 600px) {
input {
font-size: 2.4em;
width: auto;
}
div {
justify-content: space-between
}
div+span {
margin-left: .2em;
}
}
</style>
<script>
var count = 20; <!-- Anzahl Schaltzeiten(analog Sketch) einstellen 1 bis 60 -->
var fixed;
var d = document;
d.addEventListener('DOMContentLoaded', () => {
dom();
d.querySelector('#bu').addEventListener('click', () => {
let formData = new FormData();
let arr = [];
formData.append('sTime', Array.from(d.querySelectorAll('input[type=time]')).map(x => x.value != 0 ? x.value : 0));
for (var i = 0; i < count; i++) {
let x = 0;
d.querySelectorAll(`input[name=c${i}]`).forEach((el, i) => { if (el.checked) x = x | (1 << i) });
arr.push(x);
}
formData.append(`sDay`, arr);
send(formData);
});
d.querySelector('#tog').addEventListener('click', renew);
d.querySelector('#fix').addEventListener('click', renew);
for (var i = 0; i < count;) d.querySelector(`[name=bu${i++}]`).addEventListener('click', setActive);
}, renew(), send(), setInterval(renew, 1000));
function dom() {
var buf = '';
for (var i = 0; i < count; i++) {
buf += `<div id="ak${i}"><span name="bu${i}"></span><input type="time" id="sz${i * 2}" value=""><span> -- </span><input type="time" id="sz${i * 2 + 1}" value=""></div><span id="t${i}">`;
["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"].forEach(v => {
buf += `<input type="checkbox" name='c${i}'><label> ${v} </label>`;
});
buf += "</span>";
}
buf += '<div id="bottom"><button class="button" id="bu">⏰ Zeiten Speichern</button><button class="button" id="fix">--</button></div>';
d.querySelector('main').insertAdjacentHTML('beforeend', buf);
}
function setActive() {
let formData = new FormData();
formData.append(this.parentNode.id.substr(2, 5), this.textContent == 'ON' ? '0' : '1');
send(formData);
}
function send(arg) {
fetch('/timer', {
method: 'post',
body: arg
}).then(resp => {
if (resp.ok) {
if (arg && arg.has('sTime')) {
let el = d.querySelector('u');
el.classList.add('note');
setTimeout(() => {
el.classList.remove('note');
}, 5e3);
}
}
return resp.json();
}).then(array => {
if (array.length > count) {
array.forEach((v, i) => {
if (i < count * 2) d.getElementById(`sz${i}`).value = v;
if (i == count * 2) getActive(v);
if (i > count * 2) {
let el = d.getElementsByName(`c${i - count * 2 - 1}`);
for (let k in el) {
v & (1 << k) ? el[k].checked = true : el.checked = false;
}
}
});
}
else {
getActive(array);
}
});
}
function getActive(arg) {
for (var i = 0; i < count; i++) {
if (arg.length > 0) d.querySelector(`[name=bu${i}]`).textContent = (arg[i] % 2 ? 'ON' : 'OFF');
let el = d.getElementById(`ak${i}`).classList;
fixed == 0 ? arg[i] % 2 ? el.remove("none") : el.add("none") : el.add("none");;
el = d.getElementById(`t${i}`).childNodes;
fixed == 0 ? el.forEach(v => { arg[i] % 2 ? v.classList.remove("none") : v.classList.add("none") }) : el.forEach(v => { v.classList.add("none") });
}
}
function renew(id) {
if (id) id = id.currentTarget.id;
fetch(`timer?tog=${id}`).then(resp => {
return resp.json();
}).then(array => {
d.getElementById('color').style.fill = array[0] == 0 ? '#eee' : '#ff0';
d.getElementById('tog').innerHTML = array[0] == 0 ? '✋ ON' : '✋ OFF';
d.querySelector('time').innerHTML = array[1].slice(11, 19);
d.getElementById('fix').innerHTML = array[2] == 0 ? "✖ Auto inaktiv" : "⏳ Auto aktiv";
fixed = array[2];
if (id == 'fix') fixed == 1 ? getActive(0) : send();
});
}
</script>
</head>
<body>
<h2>Zeitschaltuhr</h2>
<main>
<div id="top">
<button id="tog">--</button>
<svg viewBox="0 0 12 15">
<polygon id="color" points="10.421,6.754 6.498,6.75 12.058,2.357 9.734,2.357 1.687,8.436 5.584,8.436 0,14.02" />
</svg>
<time>00:00:00</time>
</div>
</main>
<u></u>
</body>
</html>