Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 3 | ||||
| × | 1 | ||||
| × | 1 |
Overview
The Automatic Transfer Switch (ATS) is a self-designed by Jörg Mahn and was developed and built in the period from March to June 2019. The controller is based on an Arduino with Ethernet Shield attached. There are also other modules such as real-time clock, LCD display, three power meters, and two one-wire temperature sensors. Switching the power supply from the utility to the emergency generator is done by a modified dual-power automatic transfer switch. This contains a servo motor which is controlled accordingly by the Arduino. The mains and generator voltage are monitored by three phase monitors. Furthermore, a web server and an e-mail client are implemented via which information can be sent and the operating mode can be changed manually.
Arduino
The microprocessor is an Arduino Mega 2560 with Ethernet Shield 2 with SD card slot. The SD card stores the most important parameters, as well as the templates for the web server and e-mail client. The real-time clock is a DS3231 and communicates with the Arduino via I²C bus. The clock is synchronized via NTP over the Internet. This happens at the start of the program. Voltage, current and power are measured on all three phases of the line from the generator to the house. This is done by three PZEM-004T digital multifunction meters. Two one-wire DS18B20 temperature sensors measure the temperature at the motor and generator. The generator is started by two relays located in the "UV PV & Generator". The relays are controlled by a ULN2004A driver. The dual-power automatic transfer switch is controlled by a small relay board located on the (carrier) board with the Arduino.
Generator
The generator has a 4-cylinder diesel engine. According to the nameplate it is a CAMINO 30MIL and produces at 400V 20KW, year 01/2009. At the time of purchase, 450 hours were read off the hour meter.
As long as the generator is connected to the control cable, it can not be started via the built-in ignition lock. For automatic operation, the ignition key must be in position "I." When set to "0," the generator is off and will not start automatically in the event of a power failure.
The ignition lock can be put back into operation if a dongle (located in the "UV PV & Generator") is plugged in instead of the control cable.
Function
The ATS works completely automatically. Each time the status changes an e-mail will be sent. Current data is displayed via the LCD and the integrated web server.
/*
ATS - Automatic Transfer Switch
(c) 03'2019 by Jrg R. Mahn
For Arduino Mega 2560
Erkennt einen Stromausfall, trennt das Versorgungsnetz,
startet das Notstromaggreagt, schaltet die Stromversorung auf das Aggregat um.
Nach dem das Versorgungnetz wieder verfgbar ist, wird das Notstromaggregat getrennt,
die Stromversorgung wird wieder auf das Versorgungsnetz umgeschaltet,
das Notstromaggregat wird gestoppt.
Features:
- 20x4 LCD
- DS3231 Realtime Clock with Internet Sync
- Webserver (Port 80) (Pages stored on SD-Card)
- E-Mail Client to send status E-Mail
- One Wire Temperature Measurement
- 3 x PZEM004T Power Meter
09.04.2019 Erstes Release mit allen Modulen
04.05.2019 Settings von SD-Karte laden
06.05.2019 IP Settings von SD Karte laden
08.05.2019 Zeiten von SD-Karte laden
17.05.2019 Umstellung auf INI-Datei
24.05.2019 Verschiebung der Strings in den Flash-Speicher
10.06.2019 ndern der Reihenfolge beim Rckschalten.
Esrt Generator ausschalten, dann Netzwiederkehr
12.06.2019 Optische Verbesserungen am LCD
22.07.2019 nderung der Leistungsanzeige
25.07.2019 Anzeige der Batteriespannung
28.07.2019 Temperaturanzeige (Motor und Auen) erfolgreich. (Pullup Widerstand von 2,2K auf 1,5K gesenkt
05.08.2019 LCD Status "Fehler INI-File" eingefhrt, Variable "Leistung" globalisiert.
13.08.2019 PIN 5 (ldruck OK) als Indikator fr den Lauf des Motors hinzugefgt. Funktion Generator abgendert.
Funktion CheckState (Abschnitt Netzausfall im Status "Netzbetrieb" oder "Netztrennung in...") auf
berwachung des Motors und Phasen ergnzt. Tastenpiep hinzugefgt.
minbatvoltage zur INI-Datei hinzugefgt
19.08.2019 Weiterer Web-Parameter hinzugefgt: showconfig. Zeigt die Aktuelle Konfiguration aus der INI-Datei an.
20.08.2019 INDEX.HTM in drei Dateien (a.htm, b.htm, c.htm) zerlegt.
21.08.2019 Webseiten der config.ini hinzugefgt. Styles der a.htm hinzugefgt.
22.08.2019 Funktion Alert fr die Sprachausgabe ber die Haussteuerung hinzugefgt. (=> V.2.7)
23.08.2019 Bug in der Generatorfunktion behoben. (Zndung ging nicht wieder an beim >1 Startversuch.)
Startzeit (ignitiontime) wird nach jedem erfolglosem Startversuch um 100ms verlngert
24.08.2019 berwachung des Zndschlosses hinzugefgt. Analogeingang A1 (=> V.2.8)
13.09.2019 Struckturen fr Generator und Temperaturdaten eingefhrt
17.09.2019 Datei-Upload implementiert. (=> V.2.9)
19.09.2019 ATS-Konfiguration komplett neu gestaltet. (=> V.3.0)
20.09.2019 Sicherheitsabfrage beim Lschen hinzugefgt
27.09.2019 JSON Abfrage eingefhrt (=> V.3.1)
02.10.2019 Volkszhler Option eingefhrt
04.10.2019 Bug beim Dateiupload entfernt, HC und VZ enable eingefhrt (=> V.3.2)
07.10.2019 MQTT eingefhrt (=> V.3.3)
08.10.2019 Verbesserung des Webservers (Zeilenweise auslieferung)
HTML-Vorlagen auf eine Datei reduziert.
JSON Abfrage entfernt.
09.10.2019 Last will and testament und Version hinzugefgt.
*/
String version = "3.3 09.10.2019";
#include <PZEM004T.h> // Digitale Multifunktions Meter PZEM-004T
#include <Time.h> // Timekeeping functionality
#include <TimeLib.h>
#include <DS3231.h> // DS3231 - Version: Latest
#include <Wire.h> // IC bus (Mega2560: 20 (SDA), 21 (SCL))
#include <LiquidCrystal.h> // LC-Display
#include <OneWire.h> // Access 1-wire Bus (Pin 22)
#include <DallasTemperature.h> // OneWire Temperatursensor
#include <SPI.h> // Serial Peripheral Interface
#include <Ethernet.h> // Ethernet library
#include <EthernetUdp.h> // UDP library (for Internet-Time-Sync)
#include <SD.h> // Access SD-Card
#include <IniFile.h> // INI-File Acces Routines
#include <Dns.h> // getHostByName
#include <avr/pgmspace.h> // Um Strings in den Flash-Speicher zu verschieben
#include <MQTT.h>
//#define BUFSIZ 100
File sdFile;
const char *configFile = "/config.ini";
// The select pin used for the SD card
#define SD_SELECT 4
#define ETHERNET_SELECT 53
// Settings Konfiguration
const size_t bufferLen = 80; // Pufferlnge fr INI-Datei = Maximale Zeilenlnge
char buffer[bufferLen];
byte lostmain;
byte backmain;
byte glowtime;
byte ignitiontime;
byte starttrying;
byte genstartdelay;
byte genstoppdelay;
byte switchdelay;
float minbatvoltage; // Minimale Batteriespannung bevor Warnung
char domain[20];
char host[20];
byte ip[4];
byte dns[4];
byte gateway[4];
byte subnet[4];
char ntp[bufferLen] = ""; // Aus INI-Datei, kann IP oder Hostname sein
char m_server[20] = "";
String m_to;
String m_to_rn;
String m_from;
String m_from_rn;
bool VZenable = false;
char UUID[bufferLen] = ""; // Daten zum Volkserver puschen
char VZServer[bufferLen] = "";
char VZRequest[bufferLen] = "";
unsigned long VZTime;
char Mail_File[20] = {}; // HTML-Seiten fr das Webfrontend
char ATS_File[20] = {};
char Config_File[20] = {};
char CSS_File[20] = {};
bool HCenable = false;
char HCServer[bufferLen] = "";
char HCRequest[bufferLen] = "";
byte HCStart;
byte HCStop;
byte HCError;
byte HCLoss;
byte HCBack;
bool MQTTenable = false;
char MQTTServer[bufferLen] = "";
char MQTTTopic[bufferLen] = "";
char MQTTMessage[bufferLen] = "";
char MQTTUsername[bufferLen] = "";
char MQTTPassword[bufferLen] = "";
// Konfiguration Netzwerk und Webserver
EthernetServer server(80);
byte mac[] = { 0xA8, 0x61, 0x0A, 0xAE, 0x3E, 0x54 };
IPAddress timeServer; // TimeServer (IP-Adresse) fr NTP-Sync
// Konfiguration E-Mail-Server
EthernetClient mailclient;
// Konfiguration MQTT
EthernetClient MQTTnet;
MQTTClient MQTT;
// Konfiguration NTP-Server
EthernetUDP Udp;
unsigned int localPort = 8888;
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
// Konfiguration DS3231 RTC
DS3231 Clock;
bool Century = false;
bool h12;
bool PM;
// Konfiguration One-Wire
#define ONE_WIRE_BUS 22
OneWire oneWire(ONE_WIRE_BUS);
// 1-Wire (Dallas) Temperatursensoren
DallasTemperature sensors(&oneWire);
// Konfiguration LCD
#define LCD_RS 37
#define LCD_E 36
#define LCD_D4 35
#define LCD_D5 34
#define LCD_D6 33
#define LCD_D7 32
#define LCD_Rows 4
#define LCD_Cols 20
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
String lcdHead = "ATS (c) 2019 J. Mahn";
// Konfiguration PZEM004T Power-Meter
PZEM004T* pzem1;
PZEM004T* pzem2;
PZEM004T* pzem3;
IPAddress pip1(192, 168, 1, 1);
IPAddress pip2(192, 168, 1, 2);
IPAddress pip3(192, 168, 1, 3);
//LCD Sonderzeichen
#define dg 0xDF //
// Ausgnge fr das Zndschloss
#define PIN_BUTTON 2 // Mentaste
#define PIN_BUZZER 3 // Pieper
#define PIN_OEL 5 // Eingang ldruck LED grn
#define PIN_PHASE_NETZ 24 // Phasenkontrolle Netzversorger
#define PIN_PHASE_GEN 25 // Phasenkontrolle Generator
#define PIN_Z_EIN 28 // Zndschloss "Ein"
#define PIN_Z_START 29 // Zndschloss "Start"
#define REL_NETZ 30 // Schtz Netzversorger
#define REL_GEN 31 // Schtz Generator
#define BAT_INPUT 0 // Analogeingang fr Generator-Batteriespannung
#define ZUENDSCHLOSS 1 // Analogeingang fr Zuendschloss (Off / Standby)
#define ON 2
#define OFF 1
byte page = 1; // Start-Menseite
bool batlow = false; // Generatorbatteriespannung zu gering
const byte maxPage = 5; // Max. Anzahl Menseiten
long time = 0; // used for debounce
long debounce = 50; // how many ms to "debounce" between presses
bool etherStatus; // Zustand der Ethernet-Verbindung
char weekDay[7][2] = {"So", "Mo", "Di", "Mi", "Do", "Fr", "Sa" };
byte Status = 10; // Index fr Betriebsart (Initalisierung)
byte timeZone = 1; // Zeitzone
float Leistung = 0; // Gesammt-Leistung
byte byte_array[4]; // Puffer fr IP-Adressen aus der INI-Datei / Char2IP()
bool zuendung = false; // Zndschloss (Off / Standby)
int ram;
char timestamp[30];
// Status-Strings (Max 20 Chars)
const char status_0[] PROGMEM = "Netzbetrieb";
const char status_1[] PROGMEM = "Generatorbetrieb";
const char status_2[] PROGMEM = "Manueller Betrieb";
const char status_3[] PROGMEM = "Strung Generator";
const char status_4[] PROGMEM = "Strung Manuell";
const char status_5[] PROGMEM = "Stromausfall";
const char status_6[] PROGMEM = "Generator Start";
const char status_7[] PROGMEM = "Kein Netzwerk";
const char status_8[] PROGMEM = "Netztrennung";
const char status_9[] PROGMEM = "Netzwiederkehr";
const char status_10[] PROGMEM = "Initialisierung";
const char status_11[] PROGMEM = "SD-Card Fehler";
const char status_12[] PROGMEM = "Umschaltung";
const char status_13[] PROGMEM = "Vorglhen";
const char status_14[] PROGMEM = "Zndung";
const char status_15[] PROGMEM = "Drehzal berprfen";
const char status_16[] PROGMEM = "Generator stoppen";
const char status_17[] PROGMEM = "Fehler INI-File";
const char status_18[] PROGMEM = "Generator luft";
byte MaxStatus = 18;
const char *const status_table[] PROGMEM = {
status_0,
status_1,
status_2,
status_3,
status_4,
status_5,
status_6,
status_7,
status_8,
status_9,
status_10,
status_11,
status_12,
status_13,
status_14,
status_15,
status_16,
status_17,
status_18
};
// INI-Strings
const char ini_0[] PROGMEM = "Generator Einstellungen";
const char ini_1[] PROGMEM = "lostmain";
const char ini_2[] PROGMEM = "backmain";
const char ini_3[] PROGMEM = "glowtime";
const char ini_4[] PROGMEM = "ignitiontime";
const char ini_5[] PROGMEM = "starttrying";
const char ini_6[] PROGMEM = "genstartdelay";
const char ini_7[] PROGMEM = "genstoppdelay";
const char ini_8[] PROGMEM = "switchdelay";
const char ini_9[] PROGMEM = "minbatvoltage";
const char ini_10[] PROGMEM = "Network";
const char ini_11[] PROGMEM = "IP";
const char ini_12[] PROGMEM = "DNS";
const char ini_13[] PROGMEM = "GW";
const char ini_14[] PROGMEM = "SUB";
const char ini_15[] PROGMEM = "NTP";
const char ini_16[] PROGMEM = "Mail";
const char ini_17[] PROGMEM = "Server";
const char ini_18[] PROGMEM = "From";
const char ini_19[] PROGMEM = "To";
const char ini_20[] PROGMEM = "From_RN";
const char ini_21[] PROGMEM = "To_RN";
const char ini_22[] PROGMEM = "Webserver";
const char ini_23[] PROGMEM = "Files";
const char ini_24[] PROGMEM = "HC";
const char ini_25[] PROGMEM = "Enabled";
const char ini_26[] PROGMEM = "Server";
const char ini_27[] PROGMEM = "Request";
const char ini_28[] PROGMEM = "Start";
const char ini_29[] PROGMEM = "Stop";
const char ini_30[] PROGMEM = "Error";
const char ini_31[] PROGMEM = "Loss";
const char ini_32[] PROGMEM = "Back";
const char *const ini_table[] PROGMEM = {
ini_0,
ini_1,
ini_2,
ini_3,
ini_4,
ini_5,
ini_6,
ini_7,
ini_8,
ini_9,
ini_10,
ini_11,
ini_12,
ini_13,
ini_14,
ini_15,
ini_16,
ini_17,
ini_18,
ini_19,
ini_20,
ini_21,
ini_22,
ini_23,
ini_24,
ini_25,
ini_26,
ini_27,
ini_28,
ini_29,
ini_30,
ini_31,
ini_32
};
// Mail-Strings (Max 80 Chars)
const char mail_0[] PROGMEM = "Anforderung zum Generatorbetrieb, Generator gestartet";
const char mail_1[] PROGMEM = "Anforderung zum Generatorbetrieb, Generator nicht gestartet";
const char mail_2[] PROGMEM = "Anforderung zum Netzbetrieb, Generator gestoppt";
const char mail_3[] PROGMEM = "Anforderung zum Netzbetrieb, Generator nicht gestoppt";
const char mail_4[] PROGMEM = "Anforderung zum Generatorstart, Generator gestartet";
const char mail_5[] PROGMEM = "Anforderung zum Generatorstart, Generator nicht gestartet";
const char mail_6[] PROGMEM = "Anforderung zum Generatorstopp, Generator gestoppt";
const char mail_7[] PROGMEM = "Anforderung zum Generatorstopp, Generator nicht gestoppt";
const char mail_8[] PROGMEM = "Netzausfall";
const char mail_9[] PROGMEM = "Netzwiederkehr";
const char mail_10[] PROGMEM = "Generator gestartet";
const char mail_11[] PROGMEM = "Generator gestoppt";
const char mail_12[] PROGMEM = "Strung Generator";
const char mail_13[] PROGMEM = "Neustart";
const char mail_14[] PROGMEM = "Status";
const char mail_15[] PROGMEM = "Spannung Starterbatterie zu gering";
const char mail_16[] PROGMEM = "Anforderung zum Reboot";
const char mail_17[] PROGMEM = "Generator ist Standby";
const char mail_18[] PROGMEM = "Generator ist aus";
const char *const mail_table[] PROGMEM = {
mail_0,
mail_1,
mail_2,
mail_3,
mail_4,
mail_5,
mail_6,
mail_7,
mail_8,
mail_9,
mail_10,
mail_11,
mail_12,
mail_13,
mail_14,
mail_15,
mail_16,
mail_17,
mail_18
};
//MQTT-Strings
const char mqtt_0[25] PROGMEM = "/set/reset";
const char mqtt_1[25] PROGMEM = "/set/betrieb";
const char mqtt_2[25] PROGMEM = "/set/email";
const char mqtt_3[25] PROGMEM = "/set/generator";
const char mqtt_4[25] PROGMEM = "/leistung";
const char mqtt_5[25] PROGMEM = "/status";
const char mqtt_6[25] PROGMEM = "/ram";
const char mqtt_7[25] PROGMEM = "/phase1/leistung";
const char mqtt_8[25] PROGMEM = "/phase1/strom";
const char mqtt_9[25] PROGMEM = "/phase1/spannung";
const char mqtt_10[25] PROGMEM = "/phase2/leistung";
const char mqtt_11[25] PROGMEM = "/phase2/strom";
const char mqtt_12[25] PROGMEM = "/phase2/spannung";
const char mqtt_13[25] PROGMEM = "/phase3/leistung";
const char mqtt_14[25] PROGMEM = "/phase3/strom";
const char mqtt_15[25] PROGMEM = "/phase3/spannung";
const char mqtt_16[25] PROGMEM = "/temperatur/system";
const char mqtt_17[25] PROGMEM = "/temperatur/motor";
const char mqtt_18[25] PROGMEM = "/temperatur/aussen";
const char mqtt_19[25] PROGMEM = "/get/betrieb";
const char mqtt_20[25] PROGMEM = "/get/generator";
const char mqtt_21[25] PROGMEM = "/get/status";
const char mqtt_22[25] PROGMEM = "/get/version";
const char mqtt_23[25] PROGMEM = "/get/batterie";
const char *const mqtt_table[] PROGMEM = {
mqtt_0,
mqtt_1,
mqtt_2,
mqtt_3,
mqtt_4,
mqtt_5,
mqtt_6,
mqtt_7,
mqtt_8,
mqtt_9,
mqtt_10,
mqtt_11,
mqtt_12,
mqtt_13,
mqtt_14,
mqtt_15,
mqtt_16,
mqtt_17,
mqtt_18,
mqtt_19,
mqtt_20,
mqtt_21,
mqtt_22,
mqtt_23
};
byte letztertag = 0; // Fr die tgliche Zeitsynchronosation
typedef struct _Temperaturen_ {
String SysTemperatur; // Temperatur auf dem RTC
String MotorTemperatur; // One-Wire Temperatursensor Motor
String AussenTemperatur; // One-Wire Temperatursensor Generator
} Temperaturen;
Temperaturen t_data;
String StrLeistung; // Gesamtleistung
float genbat;
String GeneratorBatterie;
int timeout = 0;
// Deklarierung fr die Pausen
unsigned long previousMillis = 0;
typedef struct _Powermeter_ {
float L1_Spannung = 0;
float L2_Spannung = 0;
float L3_Spannung = 0;
float L1_Strom = 0;
float L2_Strom = 0;
float L3_Strom = 0;
float L1_Leistung = 0;
float L2_Leistung = 0;
float L3_Leistung = 0;
} Powermeter;
Powermeter p_data;
void MQTTconnect() {
Serial.print("Connecting to MQTT...");
while (!MQTT.connect("ats", MQTTUsername, MQTTPassword)) {
Serial.print(".");
delay(1000);
}
Serial.println("\nConnected!");
MQTT.subscribe(String(MQTTTopic)+"/set/#");
}
void LCD_Start() {
LCD_Head();
lcd.setCursor(0, 1);
delay(2);
lcd.print(showDate());
lcd.print(" ");
lcd.print(showTime());
LCD_Status(Status);
Link_Gen();
}
void LCD_Verbrauch() {
String str;
LCD_Head();
lcd.setCursor(0, 1);
delay(2);
str = form(String(p_data.L1_Spannung, 0) + "V", 6);
str += form(String(p_data.L2_Spannung, 0) + "V", 6);
str += form(String(p_data.L3_Spannung, 0) + "V", 6);
lcd.print(fillup(str));
lcd.setCursor(0, 2);
delay(2);
str = form(String(p_data.L1_Strom, 1) + "A", 6);
str += form(String(p_data.L2_Strom, 1) + "A", 6);
str += form(String(p_data.L3_Strom, 1) + "A", 6);
lcd.print(fillup(str));
lcd.setCursor(0, 3);
delay(2);
if (p_data.L1_Leistung > 1000) {
p_data.L1_Leistung = p_data.L1_Leistung / 1000;
str = form(String(p_data.L1_Leistung, 1) + "KW", 6);
} else {
str = form(String(p_data.L1_Leistung, 0) + "W", 6);
}
if (p_data.L2_Leistung > 1000) {
p_data.L2_Leistung = p_data.L2_Leistung / 1000;
str += form(String(p_data.L2_Leistung, 1) + "KW", 6);
} else {
str += form(String(p_data.L2_Leistung, 0) + "W", 6);
}
if (p_data.L3_Leistung > 1000) {
p_data.L3_Leistung = p_data.L3_Leistung / 1000;
str += form(String(p_data.L3_Leistung, 1) + "KW", 6);
} else {
str += form(String(p_data.L3_Leistung, 0) + "W", 6);
}
lcd.print(fillup(str));
}
void LCD_Leistungen() {
String str;
Leistung = p_data.L1_Leistung + p_data.L2_Leistung + p_data.L3_Leistung;
LCD_Head();
lcd.setCursor(0, 1);
delay(2);
str = "Leistung: ";
str += form(String(Leistung, 0) + " W", 8);
lcd.print(fillup(str));
lcd.setCursor(0, 2);
delay(2);
str = "Batterie: ";
str += form(ConvSeperator(GeneratorBatterie) + " V", 8);
lcd.print(fillup(str));
lcd.setCursor(0, 3);
delay(2);
str = " ";
lcd.print(fillup(str));
}
void LCD_Temperaturen() {
String str;
LCD_Head();
lcd.setCursor(0, 1);
delay(2);
str = "Motor: ";
str += form(t_data.MotorTemperatur + " " + (char)dg + "C", 8);
lcd.print(fillup(str));
lcd.setCursor(0, 2);
delay(2);
str = "Aussen: ";
str += form(t_data.AussenTemperatur + " " + (char)dg + "C", 8);
lcd.print(fillup(str));
lcd.setCursor(0, 3);
delay(2);
str = "System: ";
str += form(t_data.SysTemperatur + " " + (char)dg + "C", 8);
lcd.print(fillup(str));
}
void LCD_Infos() {
LCD_Head();
lcd.setCursor(0, 1);
delay(2);
lcd.print(fillup((String) "Ver. " + version));
lcd.setCursor(0, 2);
delay(2);
testLink();
if (etherStatus) {
lcd.print(fillup("IP " + DisplayAddress(Ethernet.localIP()) + "*"));
} else {
lcd.print(fillup("IP " + DisplayAddress(Ethernet.localIP())));
}
lcd.setCursor(0, 3);
delay(2);
lcd.print(fillup((String) ram + " Bytes RAM free"));
}
void loop(void) {
CheckState();
if (MQTTenable) {
MQTT.loop();
if (!MQTT.connected()) {
MQTTconnect();
}
}
Webserver();
switch (page) {
case 1:
LCD_Start();
break;
case 2:
LCD_Verbrauch();
break;
case 3:
LCD_Leistungen();
break;
case 4:
LCD_Temperaturen();
break;
case 5:
LCD_Infos();
break;
}
}
void setup() {
while (!Serial) { } // wait for serial port to connect. Needed for native USB port only
Serial.begin(9600);
PgmPrintln("ATS Version ");
Serial.println(version);
// LCD Initalisieren
lcd.begin(LCD_Cols, LCD_Rows);
delay(2);
LCD_Head();
LCD_Status(Status);
// Initalisieren der drei Power Meter
while (!Serial1) { }
pzem1 = new PZEM004T(&Serial1);
pzem1->setAddress(pip1);
while (!Serial2) { }
pzem2 = new PZEM004T(&Serial2);
pzem2->setAddress(pip2);
while (!Serial3) { }
pzem3 = new PZEM004T(&Serial3);
pzem3->setAddress(pip3);
// Digital-Eingnge
pinMode(PIN_BUTTON, INPUT_PULLUP); // Menbutton
attachInterrupt(digitalPinToInterrupt(PIN_BUTTON), InterruptButton, LOW);
pinMode(PIN_PHASE_NETZ, INPUT_PULLUP);// Phasenkontrolle Netzversorger
pinMode(PIN_PHASE_GEN, INPUT_PULLUP); // Phasenkontrolle Generator
pinMode(PIN_OEL, INPUT); // Digital-Eingang Kontrolle ldruck LED grn
// Analog-Eingnge
pinMode(BAT_INPUT, INPUT); // Batteriespannungsmessung (Spannungsteiler (10K - 100k)
pinMode(ZUENDSCHLOSS, INPUT); // Spannungsmessung Zndschloss (Off / Standby) (10K - 100k)
// Digital Ausgnge
pinMode(PIN_BUZZER, OUTPUT); // Digitalausgang Pieper
pinMode(SD_SELECT, OUTPUT);
digitalWrite(SD_SELECT, HIGH); // disable SD card
pinMode(ETHERNET_SELECT, OUTPUT);
digitalWrite(ETHERNET_SELECT, HIGH); // disable Ethernet
pinMode(10, OUTPUT); // set the SS pin as an output (necessary!)
digitalWrite(10, HIGH); // but turn off the W5100 chip!
// Umschalt Relais sind LOW-Active!
pinMode(REL_NETZ, OUTPUT); // Relais Netzversorger
digitalWrite(REL_NETZ, HIGH);
pinMode(REL_GEN, OUTPUT); // Relais Generator
digitalWrite(REL_GEN, HIGH);
// Generator Relais sind HIGH-Active!
pinMode(PIN_Z_EIN, OUTPUT); // Zndschloss "Ein"
digitalWrite(PIN_Z_EIN, LOW);
pinMode(PIN_Z_START, OUTPUT); // Zndschloss "Start"
digitalWrite(PIN_Z_START, LOW);
SPI.begin();
if (!SD.begin(SD_SELECT)) {
while (1) {
PgmPrintln("SD.begin() failed");
LCD_Status(11); // "SD-Card Fehler"
}
}
delay(1);
// INI-File von SD-Card einlesen und Variabeln setzen.
ReadIniFile();
// Start the I2C interface
Wire.begin();
delay(1);
// Start the Ethernet connection and the server:
Ethernet.begin(mac, ip, dns, gateway, subnet);
server.begin();
delay(1000); // Ethernet Start abwarten.
// Check for Ethernet hardware present
PgmPrintln("Waiting for Network");
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
PgmPrintln("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
LCD_Status(7); // "Kein Netzwerk"
delay(1); // do nothing, no point running without Ethernet hardware
}
}
// Teste Ethernet Kabel Verbindung
testLink();
while (!etherStatus) {
LCD_Status(7); // Kein Netzwerk
testLink();
}
// verbinde zum MQTT Brooker
if (MQTTenable) {
MQTT.begin(MQTTServer, MQTTnet);
MQTT.onMessage(MQTTmessageReceived);
// Last will and testament
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[21]))); // /get/status
char topic[bufferLen] = ""; strcat(topic, MQTTTopic); strcat(topic, buffer);
MQTT.setWill(topic, "Offline", true, 0);
MQTTconnect();
}
LCD_Status(10); // LCD-Status: Initalisierung
// Webserver starten
PgmPrintln("Webserver starting at: ");
Serial.print(Ethernet.localIP());
PgmPrintln(":80");
// NTP-Eintrag aus INI-Datei in IP-Adresse auflsen (Egal ob IP oder Hostname)
DNSClient dns;
dns.begin(Ethernet.dnsServerIP());
dns.getHostByName(ntp, timeServer);
// Zeit mit Internetzeit Synchronisieren
Udp.begin(localPort);
PgmPrintln("Waiting for Timesync...");
delay(1000); // Ethernet Start abwarten.
syncTime();
Serial.println(showDate());
Serial.println(showTime());
// set date time callback function
SdFile::dateTimeCallback(dateTime);
// Freien RAM ermitteln
// ram = (String) FreeRam();
ram = FreeRam();
Serial.print(ram);
PgmPrintln(" Bytes RAM free");
// Auf Netzbetrieb schalten
digitalWrite(REL_GEN, HIGH);
digitalWrite(REL_NETZ, LOW);
delay(switchdelay * 1000);
digitalWrite(REL_NETZ, HIGH);
page = 1;
// Zndung an?
int value = 0;
value = analogRead(ZUENDSCHLOSS);
if (value > 0) {
zuendung = true;
} else {
zuendung = false;
}
if (HCenable) { Serial.println(F("Home-Controll: aktive")); } else { Serial.println(F("Home-Controll: not aktive")); }
if (VZenable) { Serial.println(F("Volkzaehler: aktive")); } else { Serial.println(F("Volkzaehler: not aktive")); }
if (MQTTenable) { Serial.println(F("MQTT: aktive")); } else { Serial.println(F("MQTT: not aktive")); }
CheckState();
Webserver();
sendEmail(13);
piep();
Status = 0;
PgmPrintln("READY");
}
void CheckState() {
unsigned long currentMillis = millis();
noInterrupts();
// Freien Speicher berechnen
ram = FreeRam();
// Temepraturwerte holen
GetTemp(&t_data);
// Stromwerte holen
GetCurrent(&p_data);
Leistung = p_data.L1_Leistung + p_data.L2_Leistung + p_data.L3_Leistung;
// Zum VZ puschen
unsigned long interval = 1000; // 1sec "delay"
if (millis() - VZTime > interval ) {
PushVZ(VZServer, VZRequest, UUID, Leistung);
VZTime = millis();
}
if (Leistung > 999) {
Leistung = Leistung / 1000;
StrLeistung = form(String(Leistung, 2) + " KW", 6);
} else {
StrLeistung = form(String(Leistung, 0) + " W", 6);
}
// zum MQTT Broker pushen
if (MQTTenable) {
// mqtt_table[0-3] = subsriber Topics
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[4])));
MQTT.publish(String(MQTTTopic) + (String) buffer, StrLeistung);
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[5])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(Status));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[6])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(ram));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[7])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L1_Leistung));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[8])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L1_Strom));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[9])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L1_Spannung));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[10])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L2_Leistung));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[11])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L2_Strom));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[12])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L2_Spannung));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[13])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L3_Leistung));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[14])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L3_Strom));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[15])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(p_data.L3_Spannung));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[16])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(t_data.SysTemperatur));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[17])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(t_data.MotorTemperatur));
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[18])));
MQTT.publish(String(MQTTTopic) + (String) buffer, String(t_data.AussenTemperatur));
// Netzbetrieb
if (Status==0) {
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[19]))); // /get/betrieb
MQTT.publish(String(MQTTTopic) + (String) buffer, "netz");
}
// Generatorbetrieb
if (Status==1) {
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[19]))); // /get/betrieb
MQTT.publish(String(MQTTTopic) + (String) buffer, "generator");
}
// Generator luft
if (Status==1) { // Generatorbetrieb
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[20]))); // /get/generator
MQTT.publish(String(MQTTTopic) + (String) buffer, F("start"));
}
if (Status==18) { // Generator luft
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[20]))); // /get/generator
MQTT.publish(String(MQTTTopic) + (String) buffer, F("start"));
}
// Generator luft nicht
if (Status==0) { // Netzbetrieb
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[20]))); // /get/generator
MQTT.publish(String(MQTTTopic) + (String) buffer, F("stop"));
}
if (Status==3) { // Strung Generator
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[20]))); // /get/generator
MQTT.publish(String(MQTTTopic) + (String) buffer, F("stop"));
}
if (Status==4) { // Strung Manuell
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[20]))); // /get/generator
MQTT.publish(String(MQTTTopic) + (String) buffer, F("stop"));
}
for (int i=0; i<=MaxStatus; i++) {
if (i==Status) {
strcpy_P(buffer, (char *)pgm_read_word(&(status_table[i]))); // Lesbaren Status pushen
MQTT.publish(String(MQTTTopic) + F("/get/status"), (String) buffer);
}
}
if (zuendung) {
if (Status == 1 || Status == 18) {
MQTT.publish(String(MQTTTopic) + F("/get/generator"), F("Ein"));
} else {
MQTT.publish(String(MQTTTopic) + F("/get/generator"), F("Standby"));
}
} else {
MQTT.publish(String(MQTTTopic) + F("/get/generator"), F("Aus"));
}
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[22]))); // /get/version
MQTT.publish(String(MQTTTopic) + (String) buffer, version);
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[23]))); // /get/batterie
MQTT.publish(String(MQTTTopic) + (String) buffer, ConvSeperator(GeneratorBatterie));
}
// Sende keine E-Mail whrend der Initalisierung
// Zndung an?
int value = 0;
value = analogRead(ZUENDSCHLOSS);
if (value > 0) {
if (!zuendung) {
zuendung = true;
sendEmail(17);
}
} else {
if (zuendung) {
zuendung = false;
sendEmail(18);
}
}
//Batteriespannung holen
genbat = GetBatteryVoltage();
GeneratorBatterie = String(genbat, 1);
if (genbat < minbatvoltage) {
if (!batlow) {
sendEmail(15);
}
batlow = true;
} else {
batlow = false;
}
// Alles LOW-Active!
// HIGH = Strom ausgefallen
// Netzwiederkehr bevor Timeout Netzausfall
if (!digitalRead(PIN_PHASE_NETZ) && Status == 8) {
piep();
page = 1;
Status = 0; // "Netzbetrieb"
timeout = 0;
LCD_Status(Status);
previousMillis = 0;
sendEmail(9);
PgmPrintln("Netzwiederkehr bevor Timeout Netzausfall");
}
// Netzausfall bevor Timeout Netzwiederkehr
if (digitalRead(PIN_PHASE_NETZ) && Status == 9) {
piep();
page = 1;
Status = 1; // "Generatorbetrieb"
timeout = 0;
LCD_Status(Status);
previousMillis = 0;
sendEmail(8);
PgmPrintln("Netzausfall bevor Timeout Netzwiederkehr");
}
// Netzausfall im Status "Netzbetrieb" oder "Netztrennung in..."
if (digitalRead(PIN_PHASE_NETZ) && (Status == 0 || Status == 8)) {
if (previousMillis == 0) previousMillis = currentMillis;
if (Status == 0) {
page = 1;
Status = 8; // "Netztrennung"
sendEmail(Status);
Alert(HCServer, HCRequest, HCLoss);
PgmPrintln("Netzausfall im Status Netzbetrieb, Countdown gestartet.");
piep();
}
timeout = lostmain * 60 - ((currentMillis - previousMillis) / 1000);
LCD_Status(Status);
if (timeout <= 0) {
previousMillis = 0;
piep();
PgmPrintln("Netzausfall!");
Alert(HCServer, HCRequest, HCStart);
if (Generator(ON)) { // Wenn Generator gestartet...
if (!digitalRead(PIN_PHASE_GEN)) { // ...und alle drei Phasen vorhanden...
Status = 1; // "Generatorbetrieb"
LCD_Status(Status);
SwitchGeneratorbetrieb(); // ...dann Umschalten auf Generatorbetrieb.
sendEmail(10);
PgmPrintln("Generator gestartet, Umschalten auf Generatorbetrieb.");
} else { // Generator liefert keine Spannungen
Status = 3; // "Strung Generator"
Alert(HCServer, HCRequest, HCError);
LCD_Status(Status);
Generator(OFF); // Generator ausschalten
sendEmail(12);
PgmPrintln("Generator gestartet, aber Phasen liefern keine Spannung!");
}
} else { // Generator konnte nicht gestartet werden
Status = 3; // "Strung Generator"
Alert(HCServer, HCRequest, HCError);
LCD_Status(Status);
Generator(OFF);
sendEmail(12);
PgmPrintln("Generator konnte nicht gestartet werden!");
}
}
}
// Netzwiederkehr im Status "Generatorbetrieb" und "Netzwiederkehr in..."
if (!digitalRead(PIN_PHASE_NETZ) && (Status == 1 || Status == 9)) {
if (previousMillis == 0) previousMillis = currentMillis;
if (Status == 1) {
page = 1;
Status = 9; // "Netzwiederkehr"
Alert(HCServer, HCRequest, HCBack);
PgmPrintln("Netzwiederkehr im Status Generatorbetrieb, Countdown gestartet.");
sendEmail(Status);
piep();
}
timeout = (backmain * 60) - ((currentMillis - previousMillis) / 1000);
LCD_Status(Status);
if (timeout <= 0) {
previousMillis = 0;
PgmPrintln("Netzwiederkehr im Status Generatorbetrieb");
Status = 0; // "Netzbetrieb"
Alert(HCServer, HCRequest, HCStop);
piep();
Generator(OFF);
LCD_Status(Status);
SwitchNetzbetrieb(); // Umschalten auf Netzbetrieb
sendEmail(11);
}
}
// Netzwiederkehr im Status "Strung Generator"
if (!digitalRead(PIN_PHASE_NETZ) && Status == 3) {
PgmPrintln("Netzwiederkehr im Status Strung");
page = 1;
Status = 0; // "Netzbetrieb";
Alert(HCServer, HCRequest, HCBack);
piep();
Generator(OFF);
LCD_Status(Status);
SwitchNetzbetrieb();
sendEmail(9);
}
// Generatorwiederkehr im Status "Strung Generator"
if (!digitalRead(PIN_PHASE_GEN) && Status == 3) {
PgmPrintln("Generatorwiederkehr im Status Strung Generator");
page = 1;
Status = 1; // "Generatorbetrieb"
piep();
//Generator(ON);
LCD_Status(Status);
SwitchGeneratorbetrieb(); // Umschalten auf Generatorbetrieb
sendEmail(10);
}
// Generatorausfall im Status "Generatorbetrieb"
if (digitalRead(PIN_PHASE_GEN) && Status == 1) {
PgmPrintln("Generatorausfall im Status Generatorbetrieb");
Generator(OFF);
SwitchNetzbetrieb(); // Umschalten auf Netzbetrieb und hoffen, dass der Strom bald wieder kommt!
page = 1;
Status = 3; // "Strung Generator"
Alert(HCServer, HCRequest, HCError);
piep();
LCD_Status(Status);
sendEmail(12);
}
// Generatorausfall im Status "Manueller Betrieb"
if (digitalRead(PIN_PHASE_GEN) && Status == 2) {
PgmPrintln("Generatorausfall im Status Manuellerbetrieb");
Generator(OFF);
page = 1;
Status = 4; // "Strung Manuell"
Alert(HCServer, HCRequest, HCError);
piep();
LCD_Status(Status);
sendEmail(12);
}
// Generatorwiederkehr im Status "Strung Manuell"
if (!digitalRead(PIN_PHASE_GEN) && Status == 4) {
PgmPrintln("Generatorwiederkehr im Status Strung Manuell");
page = 1;
Status = 2; // "Manueller Betrieb"
piep();
Generator(ON);
LCD_Status(Status);
// SwitchGeneratorbetrieb(); // Umschalten auf Generatorbetrieb
sendEmail(10);
}
interrupts();
}
void Webserver() {
String readString = "";
char c;
char br[5] = "<br>";
char dp[3] = ": ";
EthernetClient client = server.available();
if (client) {
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
c = client.read();
readString += c;
if (c == '\n' && currentLineIsBlank) {
// Datei Upload
if (readString.indexOf("multipart/form-data;") > 0) {
readString = "";
currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {
char w = client.read();
readString += w;
if (w == '\n' && currentLineIsBlank) {
// Dateiname extrahieren, Datei anlegen
int a = readString.indexOf("filename=") + 10;
readString = readString.substring(a);
a = readString.indexOf("\"");
readString.remove(a);
if (SD.exists(readString)) {
Serial.print(F("Upload: ")); Serial.print(readString); Serial.println(F(" ist bereits vorhanden, wird berschrieben."));
SD.remove(readString);
}
sdFile = SD.open(readString, FILE_WRITE);
int i = 0;
// stay in this loop until the file has been received
while (client.connected()) {
if (client.available()) {
w = client.read(); // get file byte
if (w == 0x2D) { // 1.
w = client.read();
if (w == 0x2D) { // 2.
w = client.read();
if (w == 0x2D) { // 3.
w = client.read();
if (w == 0x2D) { // 4.
w = client.read();
if (w == 0x2D) { // 5.
w = client.read();
if (w == 0x2D) { // 6.
sdFile.close();
if (SD.exists(readString)) {
Serial.print(F("Upload: "));
Serial.print(readString);
Serial.println(F(" OK"));
} else {
Serial.print(F("Upload: "));
Serial.print(readString);
Serial.println(F(" Fehlgeschlagen"));
}
goto Configuration;
//client.stop();
//break;
} else {
sdFile.print("-");
sdFile.print("-");
sdFile.print("-");
sdFile.print("-");
sdFile.print("-");
sdFile.print(w);
}
} else {
sdFile.print("-");
sdFile.print("-");
sdFile.print("-");
sdFile.print("-");
sdFile.print(w);
}
} else {
sdFile.print("-");
sdFile.print("-");
sdFile.print("-");
sdFile.print(w);
}
} else {
sdFile.print("-");
sdFile.print("-");
sdFile.print(w);
}
} else {
sdFile.print("-");
sdFile.print(w);
}
} else {
sdFile.print(w);
}
}
}
}
// detect the end of the incoming HTTP header
if (w == '\n') {
// starting a new line
currentLineIsBlank = true;
}
else if (w != '\r') {
// got a character on the current line
currentLineIsBlank = false;
}
}
}
}
// Download von Dateien von der SD-Karte
if (readString.indexOf("?download") > 0 ) {
int a = readString.indexOf("?download") + 10;
int b = readString.indexOf(" HTTP/");
readString = readString.substring(a, b);
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: text/html"));
client.println();
client.println(F("<!DOCTYPE html>"));
client.println(F("<html>"));
client.println(F("<head>"));
client.print(F("<title>ATS-Konfiguration (")); client.print(readString); client.println(F(")</title>"));
client.println(F("<meta name=\"referrer\" content=\"no-referrer\" />"));
client.println(F("</head><body><pre>"));
readString.toUpperCase();
sdFile = SD.open(readString);
if (!sdFile) {
client.println(F("<h1>File not Found!</h1>"));
Serial.println(F("Download: ")); Serial.print(readString); Serial.println(F(" nicht gefunden!"));
client.stop();
break;
} else {
Serial.print(F("Download: ")); Serial.println(readString);
while (sdFile.available()) {
int x = sdFile.read();
if (x == 60) client.print("<");
if (x == 62) client.print(">");
if (x != 60 && x != 62) client.print(char(x));
}
sdFile.close();
}
client.println(F("</pre></body></html>"));
client.stop();
break;
}
// Reboot Arduino
if (readString.indexOf("?reset") > 0 ) {
PgmPrintln("Anforderung per Webfrontend zum Reset.");
sendEmail(16);
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: text/html"));
client.println();
client.print(F("<!DOCTYPE html><html><head><title>ATS</title><meta http-equiv=\"refresh\" content=\"10; URL=http://"));
client.print(host); client.print(F(".")); client.print(domain);
client.print(F("\"><meta name=\"referrer\" content=\"no-referrer\" /></head><body><h1>Rebooting...</h1></body></html>"));
delay(1);
client.stop();
delay(2000);
softReset();
break;
}
// Datei von SD-Karte lschen
if (readString.indexOf("?deletefile") > 0 ) {
//Serial.print(readString);
int a = readString.indexOf("?deletefile") + 12;
int b = readString.indexOf("&");
int c = readString.indexOf(" HTTP");
String filename = readString.substring(a, b);
if (readString.substring(b + 1, c) == "delete=OK") {
filename.toUpperCase();
if (filename != "CONFIG.INI") {
if (SD.exists(filename)) {
SD.remove(filename);
if (!SD.exists(filename)) {
Serial.print(F("Delete: ")); Serial.print(filename); Serial.println(F(" OK"));
} else {
Serial.print(F("Delete: ")); Serial.print(filename); Serial.println(F(" Fehlgeschlagen"));
}
} else {
Serial.println(F("Delete: ")); Serial.print(filename); Serial.println(F(" nicht gefunden!"));
}
} else {
Serial.println(F("Delete: config.ini darf nicht gelscht werden!"));
}
}
goto Configuration;
break;
}
// Datei lschen / Sicherheitsabfrage
if (readString.indexOf("?askdelete") > 0 ) {
int a = readString.indexOf("?askdelete") + 11;
int b = readString.indexOf(" HTTP/");
readString = readString.substring(a, b);
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: text/html"));
client.println();
client.print(F("<!DOCTYPE html><html><head><title>ATS-Konfiguration</title>"));
client.print(F("<meta name=\"referrer\" content=\"no-referrer\" /></head><style>"));
client.print(F("table {background-color: #D8D8D8;border-collapse: collapse;border: none;}"));
client.print(F("thead {background-color: #DF0101;}"));
client.print(F("input[type=submit] { padding:15px 15px; background:#ccc; border:0 none; cursor:pointer; -webkit-border-radius: 5px; border-radius: 5px; }"));
client.print(F("td, th {text-align: center;padding: 0.5em 1em;}</style><body>"));
client.print(F("<form method=\"get\" name=\"ats\">"));
client.print(F("<input type=\"hidden\" name=\"deletefile\" value=\""));
client.print(readString);
client.print(F("\">"));
client.print(F("<table>"));
client.print(F("<thead><tr><th colspan=\"2\"><h1><font face=\"verdana\" color=\"white\">Achtung!</font></h1></th><thead></tr>"));
client.print(F("<tbody><tr><td colspan=\"2\"><h2><font face=\"verdana\">Soll ")); client.print(readString); client.print(F(" wirklich gelöscht werden?</font></h2></td></tr></tbody>"));
client.print(F("<tbody><tr>"));
client.print(F("<td><input type=\"submit\" name=\"delete\" value=\"OK\"></td>"));
client.print(F("<td><input type=\"submit\" name=\"delete\" value=\"Cancel\"></td>"));
client.print(F("</tr></tbody></table></form>"));
client.print(F("</body></html>"));
delay(1);
client.stop();
break;
}
// Konfiguration anzeigen
if (readString.indexOf("?config") > 0 ) {
Configuration:
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: text/html"));
client.println();
client.println(F("<!DOCTYPE html>"));
client.println(F("<style>"));
DeliverContent(&client, CSS_File);
client.println(F("</style>"));
ParseContent(&client, Config_File);
break;
}
// Notfall Formular um Dateien hoch zu laden
if (readString.indexOf("?upload") > 0 ) {
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: text/html"));
client.println();
client.print(F("<!DOCTYPE html><html><head><title>ATS</title><meta name=\"referrer\" content=\"no-referrer\" /></head><body><h1>Datei auf SD-Karte speichern</h1></body></html>"));
client.print(F("<form method=\"post\" enctype=\"multipart/form-data\">"));
client.print(F("<input type=\"button\" value=\"Zurück\" onclick=\"window.location.href='http://"));
client.print(host); client.print(F(".")); client.print(domain);
client.print(F("'\" /> "));
client.print(F("<input type=\"file\" name=\"datei\" accept=\"text/*\"><input class=\"button\" type=\"submit\" value=\"Upload\"> "));
client.print(F("<input type=\"button\" value=\"Reboot\" onclick=\"window.location.href='http://"));
client.print(host); client.print(F(".")); client.print(domain);
client.print(F("/?reset'\" /> "));
client.print(F("</form></body></html>"));
delay(1);
client.stop();
break;
}
if (readString.indexOf("?power") > 0 ) { // Abfrage der aktuellen Leistung
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: text/plain"));
client.println();
client.println(ConvSeperator(StrLeistung));
delay(1);
client.stop();
break;
}
if (readString.indexOf("?mainbet") > 0 ) { // Netzbetrieb
PgmPrintln("Anforderung per Webfrontend zum Netzbetrieb");
Status = 0; // "Netzbetrieb"
Alert(HCServer, HCRequest, HCStop);
if (!Generator(OFF)) {
SwitchNetzbetrieb();
sendEmail(2);
} else {
Status = 4; // "Strung Manuell"
sendEmail(3);
}
Print_Status[Status];
}
if (readString.indexOf("?genbet") > 0 ) { // Generatorbetrieb, Stromausfall simulieren
PgmPrintln("Anforderung per Webfrontend zum Generatorbetrieb");
Status = 2; // Manueller Betrieb
Alert(HCServer, HCRequest, HCStart);
if (Generator(ON)) {
SwitchGeneratorbetrieb();
sendEmail(0);
} else {
Status = 4; // "Strung Manuell"
Alert(HCServer, HCRequest, HCError);
sendEmail(1);
}
Print_Status[Status];
}
if (readString.indexOf("?genstart") > 0 ) { // Generator starten
PgmPrintln("Anforderung per Webfrontend zum Generatorstart");
Alert(HCServer, HCRequest, HCStart);
if (Generator(ON)) {
Status = 18; // "Generator luft"
sendEmail(4);
} else {
Status = 4; // "Strung Manuell"
Alert(HCServer, HCRequest, HCError);
sendEmail(5);
}
Print_Status[Status];
}
if (readString.indexOf("?genstop") > 0 ) { // Generator stoppen
PgmPrintln("Anforderung per Webfrontend zum Generatorstopp");
Alert(HCServer, HCRequest, HCStop);
if (!Generator(OFF)) {
Status = 0; // "Netzbetrieb"
sendEmail(6);
} else {
Status = 4; // Strung Manuell
sendEmail(7);
}
Print_Status[Status];
}
if (readString.indexOf("?sendmail") > 0 ) { // Status-Seite als E-Mail versenden
PgmPrintln("Anforderung per Webfrontend zum Status-Mail-Versand");
sendEmail(14);
}
// HTTP-Request lschen um Speicher frei zu geben.
readString = "";
// Webseite an Client ausliefern
client.println(F("HTTP/1.1 200 OK"));
client.println(F("Content-Type: text/html"));
client.println();
client.println(F("<!DOCTYPE html>"));
client.println(F("<style>"));
DeliverContent(&client, CSS_File);
client.println(F("</style>"));
ParseContent(&client, ATS_File);
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
delay(1);
client.stop();
}
}
void DeliverSDRoot(EthernetClient *client) {
File root;
root = SD.open("/");
while (true) {
File entry = root.openNextFile();
if (! entry) { // no more files
break;
}
if (!entry.isDirectory()) {
client->print(F("<tr><td id=\"a\">"));
String filename = String(entry.name());
filename.toLowerCase();
client->print(F("<a href=\"http://"));
client->print(host); client->print(F(".")); client->print(domain);
client->print(F("/?download="));
client->print(filename);
client->print(F("\"><font class=\"n\">"));
client->print(filename);
client->print(F("</font></a>"));
client->print(F("</td><td id=\"a\"><font class=\"n\">"));
client->print(entry.size(), DEC);
client->print(F("</font></td><td id=\"a\">"));
client->print(F("<a href=\"http://"));
client->print(host); client->print(F(".")); client->print(domain);
client->print(F("/?askdelete="));
client->print(filename);
if (filename == "config.ini") {
client->print(F("\"><font class=\"n\">"));
} else {
client->print(F("\"><font class=\"n\">X"));
}
client->print(F("</font></a></td>"));
client->println(F("</tr>"));
}
entry.close();
}
delay(1);
}
void DeliverContent(EthernetClient *client, char *filename) {
char c;
char temp[200] = {};
int i = 0;
sdFile = SD.open(filename);
if (!sdFile) {
Serial.println(F("DeliverContent"));
Serial.print(filename); Serial.println(F(" nicht gefunden!"));
return false;
}
while (sdFile.available()) {
c = sdFile.read();
if ((c == '\n')) { // Zeile fertig eingelesen
int len = strlen(temp);
temp[len] = '\0';
client->print(temp); // ... und an Webclient ausliefern
i = 0;
memset(temp, '\0', sizeof(temp));
} else {
temp[i] = c;
i++;
}
}
sdFile.close();
}
void ParseContent(EthernetClient *client, char *filename) {
char c;
char temp[200] = {};
String strLine = "";
int i = 0;
// Ist die Datei berhaupt da?
sdFile = SD.open(filename);
if (!sdFile) {
Serial.println(F("ParseWebContent"));
Serial.print(filename); Serial.println(F(" nicht gefunden!"));
return false;
}
while (sdFile.available()) {
c = sdFile.read();
if ((c == '\n')) { // Zeile fertig eingelesen
int len = strlen(temp);
temp[len] = '\0';
strLine = String(temp);
// SD-Karteninhalt ausgeben
if (strLine.indexOf("<sdcontent>") > 0) {
DeliverSDRoot(client);
}
strLine.replace("%ram%", String(ram));
strLine.replace("%ver%", version);
strLine.replace("%z%", showTime());
strLine.replace("%d%", showDate());
strLine.replace("%sta%", Web_Status(Status));
if (zuendung) {
if (Status == 1 || Status == 18) {
strLine.replace("%gen%", "Ein");
} else {
strLine.replace("%gen%", "Standby");
}
} else {
strLine.replace("%gen%", "Aus");
}
strLine.replace("%sys%", ConvSeperator(t_data.SysTemperatur));
strLine.replace("%mot%", ConvSeperator(t_data.MotorTemperatur));
strLine.replace("%aut%", ConvSeperator(t_data.AussenTemperatur));
strLine.replace("%bat%", ConvSeperator(GeneratorBatterie));
strLine.replace("%p%", ConvSeperator(StrLeistung));
strLine.replace("%1v%", String(p_data.L1_Spannung, 0));
strLine.replace("%2v%", String(p_data.L2_Spannung, 0));
strLine.replace("%3v%", String(p_data.L3_Spannung, 0));
strLine.replace("%1c%", ConvSeperator(String(p_data.L1_Strom, 1)));
strLine.replace("%2c%", ConvSeperator(String(p_data.L2_Strom, 1)));
strLine.replace("%3c%", ConvSeperator(String(p_data.L3_Strom, 1)));
strLine.replace("%lm%", String(lostmain));
strLine.replace("%bm%", String(backmain));
strLine.replace("%gt%", String(glowtime));
strLine.replace("%it%", String(ignitiontime * 100));
strLine.replace("%st%", String(starttrying));
strLine.replace("%startd%", String(genstartdelay));
strLine.replace("%stopd%", String(genstoppdelay));
strLine.replace("%sd%", String(switchdelay));
strLine.replace("%mv%", ConvSeperator(String(minbatvoltage, 1)));
strLine.replace("", "ö");
strLine.replace("", "ä");
strLine.replace("", "ü");
strLine.trim();
client->println(strLine); // ... und an Webclient ausliefern
i = 0;
memset(temp, '\0', sizeof(temp));
} else {
temp[i] = c;
i++;
}
}
sdFile.close();
}
///////////////////////////////////////////////////////////////////////////////
// functions.ino
///////////////////////////////////////////////////////////////////////////////
void GetCurrent(Powermeter *data) {
float v = pzem1->voltage(pip1);
if (v < 0.0) v = 0.0;
float p = pzem1->power(pip1);
if (p < 0.0) p = 0.0;
float i = pzem1->current(pip1);
if (i < 0.0) i = 0.0;
data->L1_Spannung = v;
data->L1_Strom = i;
data->L1_Leistung = p;
v = pzem2->voltage(pip2);
if (v < 0.0) v = 0.0;
p = pzem2->power(pip2);
if (p < 0.0) p = 0.0;
i = pzem2->current(pip2);
if (i < 0.0) i = 0.0;
data->L2_Spannung = v;
data->L2_Strom = i;
data->L2_Leistung = p;
v = pzem3->voltage(pip3);
if (v < 0.0) v = 0.0;
p = pzem3->power(pip3);
if (p < 0.0) p = 0.0;
i = pzem3->current(pip3);
if (i < 0.0) i = 0.0;
data->L3_Spannung = v;
data->L3_Strom = i;
data->L3_Leistung = p;
}
void GetTemp(Temperaturen *data) {
//Temepraturwerte holen
sensors.requestTemperatures(); // Send the command to get temperature readings
data->AussenTemperatur = String(sensors.getTempCByIndex(0), 1);
data->MotorTemperatur = String(sensors.getTempCByIndex(1), 1);
data->SysTemperatur = String(Clock.getTemperature(), 1); // Fragt die Temperatur auf dem RTC ab
}
void testLink() {
if (Ethernet.linkStatus() == Unknown) {
etherStatus = false;
}
else if (Ethernet.linkStatus() == LinkON) {
etherStatus = true;
}
else if (Ethernet.linkStatus() == LinkOFF) {
etherStatus = false;
}
}
void Link_Gen() {
#define on 0xFF
#define off 0x20
testLink();
lcd.setCursor(0, 3);
delay(2);
lcd.print(" LAN");
if (etherStatus) {
lcd.write(on);
} else {
lcd.write(off);
}
lcd.print(" NETZ");
if (!digitalRead(PIN_PHASE_NETZ)) {
lcd.write(on);
} else {
lcd.write(off);
}
lcd.print(" GEN");
if (!digitalRead(PIN_PHASE_GEN)) {
lcd.write(on);
} else {
lcd.write(off);
}
lcd.print(" ");
}
String DisplayAddress(IPAddress address) {
return String(address[0]) + "." +
String(address[1]) + "." +
String(address[2]) + "." +
String(address[3]);
}
String fillup(String str) {
str = str.substring(0, (LCD_Cols));
while (str.length() < LCD_Cols) {
str = str + " ";
}
return str;
}
void SwitchNetzbetrieb() {
piep();
LCD_Status(12);
switchREL(2, HIGH);
switchREL(1, LOW);
delay(switchdelay * 1000);
switchREL(1, HIGH);
}
void SwitchGeneratorbetrieb() {
piep();
LCD_Status(12); //"Umschaltung"
switchREL(1, HIGH);
switchREL(2, LOW);
delay(switchdelay * 1000);
switchREL(2, HIGH);
}
void switchREL(int i, bool status) {
// Releais sind LOW-Active!
if (i == 1) { // Netz-Schtz
if (digitalRead(REL_GEN)) {
digitalWrite(REL_NETZ, status);
}
}
if (i == 2) { // Generator-Schtz
if (digitalRead(REL_NETZ)) {
digitalWrite(REL_GEN, status);
}
}
}
void InterruptButton() { // Menbutton
if (millis() - time > debounce) {
piep();
page++;
if (page > maxPage) {
page = 1;
}
if (page < 1) {
page = 1;
}
// lcd.clear();
}
time = millis();
delay(10);
}
String zero(String str) {
if (str.length() < 2) {
str = "0" + str;
}
return str;
}
String showDate() {
return zero(String(Clock.getDate())) + "." + zero(String(Clock.getMonth(Century))) + ".20" + String(Clock.getYear());
}
String showTime() {
return zero(String(Clock.getHour(h12, PM))) + ":" + zero(String(Clock.getMinute())) + ":" + zero(String(Clock.getSecond()));
}
void syncTime() {
Clock.setClockMode(false); // set to 24h
// Internetzeit holen und RTC syncen
setSyncProvider(getNtpTime);
while (timeStatus() == timeNotSet); // wait until the time is set by the sync provider
Clock.setYear(year() - 2000);
Clock.setMonth(month());
Clock.setDate(day());
Clock.setDoW(weekday());
Clock.setHour(hour());
Clock.setMinute(minute());
Clock.setSecond(second());
if (sommerzeit(year(), month(), day(), hour(), 1)) {
PgmPrintln("Sommerzeit");
Clock.setHour(hour() + 1);
if (hour() >= 24) {
Clock.setHour(0);
Clock.setDate(day() + 1);
}
}
}
/*-------- NTP code ----------*/
unsigned long getNtpTime()
{
// IPAddress timeServer(arr_ntp);
sendNTPpacket(timeServer); // send an NTP packet to a time server
delay(1000);
if ( Udp.parsePacket() ) {
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into buffer
//the timestamp starts at byte 40, convert four bytes into a long integer
unsigned long hi = word(packetBuffer[40], packetBuffer[41]);
unsigned long low = word(packetBuffer[42], packetBuffer[43]);
// this is NTP time (seconds since Jan 1 1900
////unsigned long secsSince1900 = hi << 16 | low;
// Unix time starts on Jan 1 1970
////const unsigned long seventyYears = 2208988800UL;
////unsigned long epoch = secsSince1900 - seventyYears; // subtract 70 years
////return epoch;
unsigned long secsSince1900;
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress address)
{
memset(packetBuffer, 0, NTP_PACKET_SIZE); // set all bytes in the buffer to 0
// Initialize values needed to form NTP request
packetBuffer[0] = B11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum
packetBuffer[2] = 6; // Max Interval between messages in seconds
packetBuffer[3] = 0xEC; // Clock Precision
// bytes 4 - 11 are for Root Delay and Dispersion and were set to 0 by memset
packetBuffer[12] = 49; // four-byte reference ID identifying
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// send the packet requesting a timestamp:
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
byte sendEmail(int sub) {
String subject;
char buffer[81];
//Serial.print(F("Mailserver: "));Serial.println(m_server);
if (mailclient.connect(m_server, 25)) {
PgmPrintln("Sending email");
} else {
PgmPrintln("Connection to mailserver failed");
return 0;
}
strcpy_P(buffer, (char *)pgm_read_word(&(mail_table[sub])));
subject = (String) buffer;
subject.trim();
subject.replace("", "\xF6");
if (!eRcv()) return 0;
mailclient.print(F("HELO "));
mailclient.println(DisplayAddress(Ethernet.localIP()));
if (!eRcv()) return 0;
mailclient.print(F("MAIL From: <"));
mailclient.print(m_from);
mailclient.println(F(">"));
if (!eRcv()) return 0;
mailclient.print(F("RCPT To: <"));
mailclient.print(m_to);
mailclient.println(F(">"));
if (!eRcv()) return 0;
mailclient.println(F("DATA"));
if (!eRcv()) return 0;
mailclient.print(F("To: "));
mailclient.print(m_to_rn);
mailclient.print(F("<"));
mailclient.print(m_to);
mailclient.println(F(">"));
mailclient.print(F("From: "));
mailclient.print(m_from_rn);
mailclient.print(F("<"));
mailclient.print(m_from);
mailclient.println(F(">"));
mailclient.print(F("Subject: "));
mailclient.println(subject);
mailclient.println(F("MIME-Version: 1.0"));
mailclient.println(F("Content-type: text/html"));
ParseContent(&mailclient, Mail_File);
mailclient.println(F("."));
if (!eRcv()) return 0;
mailclient.println(F("QUIT"));
if (!eRcv()) return 0;
delay(1);
mailclient.stop();
return 1;
}
byte eRcv() {
byte respCode;
byte thisByte;
int loopCount = 0;
while (!mailclient.available()) {
delay(1);
loopCount++;
// if nothing received for 10 seconds, timeout
if (loopCount > 10000) {
mailclient.stop();
PgmPrintln("\r\nTimeout");
return 0;
}
}
respCode = mailclient.peek();
/*
while (mailclient.available()) {
thisByte = mailclient.read();
Serial.write(thisByte);
}
*/
if (respCode >= '4') {
efail();
return 0;
}
return 1;
}
void efail() {
byte thisByte = 0;
int loopCount = 0;
mailclient.println(F("QUIT"));
while (!mailclient.available()) {
delay(1);
loopCount++;
// if nothing received for 10 seconds, timeout
if (loopCount > 10000) {
mailclient.stop();
PgmPrintln("\r\nTimeout");
return;
}
}
while (mailclient.available()) {
thisByte = mailclient.read();
Serial.write(thisByte);
}
mailclient.stop();
PgmPrintln("disconnected");
}
boolean sommerzeit(int s_jahr, byte s_monat, byte s_tag, byte s_stunde, byte s_zeitzone) {
if (s_monat < 3 || s_monat > 10) return false;
if (s_monat > 3 && s_monat < 10) return true;
if (s_monat == 3 && (s_stunde + 24 * s_tag) >= (1 + s_zeitzone + 24 * (31 - (5 * s_jahr / 4 + 4) % 7)) || s_monat == 10 && (s_stunde + 24 * s_tag) < (1 + s_zeitzone + 24 * (31 - (5 * s_jahr / 4 + 1) % 7))) {
return true;
} else {
return false;
}
}
/*
void error_P(const char* str) {
PgmPrint("error: ");
SerialPrintln_P(str);
if (card.errorCode()) {
PgmPrint("SD error: ");
Serial.print(card.errorCode(), HEX);
PgmPrintln(",");
Serial.println(card.errorData(), HEX);
}
while (1);
}
*/
boolean Generator(byte start) {
bool running = false;
lcd.clear();
LCD_Head();
// Generator Relais sind HIGH-Active!
switch (start) {
case 1: // OFF - Ausschalten
if (!digitalRead(PIN_OEL)) {
PgmPrintln("Generator luft nicht.\r\nKein Aktion durchgefhrt.");
} else {
digitalWrite(PIN_Z_EIN, LOW);
PgmPrintln("Generator stoppen");
LCD_Status(16);
delay(genstoppdelay * 1000);
if (digitalRead(PIN_OEL)) {
// if (!digitalRead(PIN_PHASE_GEN)) {
running = true;
} else {
PgmPrintln("Generator gestoppt");
}
}
break;
case 2: // ON - Einschalten
if (digitalRead(PIN_OEL)) {
// if (!digitalRead(PIN_PHASE_GEN)) {
running = true;
PgmPrintln("Generator luft bereits.\r\nKein Aktion durchgefhrt.");
} else {
byte i = 1;
while (i <= starttrying && !running) {
PgmPrintln("Generator starten");
LCD_Status(13); // "Vorglhen"
PgmPrintln("Vorglhen");
digitalWrite(PIN_Z_EIN, HIGH);
delay(glowtime * 1000);
digitalWrite(PIN_Z_START, HIGH);
LCD_Status(14); // "Zndung"
Serial.print(i);
PgmPrintln(". Zndung");
// Ab der zweiten Zndung wird um 100ms lnger gestartet
delay((ignitiontime + i - 1) * 100);
digitalWrite(PIN_Z_START, LOW);
LCD_Status(15); // "Drehzal berprfen"
delay(genstartdelay * 1000);
// if (!digitalRead(PIN_PHASE_GEN)) {
if (digitalRead(PIN_OEL)) {
running = true;
PgmPrintln("Generator gestartet");
} else {
PgmPrintln("Generator Start fehlgeschlagen");
digitalWrite(PIN_Z_EIN, LOW);
delay(5000);
}
i++;
}
}
break;
}
return running;
}
void Print_Status(byte stat) {
char buffer[21];
strcpy_P(buffer, (char *)pgm_read_word(&(status_table[stat])));
Serial.println(buffer);
}
String Web_Status(byte stat) {
String temp_str;
char buffer[21];
strcpy_P(buffer, (char *)pgm_read_word(&(status_table[stat])));
temp_str = (String) buffer;
while (temp_str.endsWith(" ")) {
temp_str.remove(temp_str.length() - 1);
}
temp_str.replace("", "ö");
temp_str.replace("", "ü");
return temp_str;
}
void LCD_Status(byte stat) {
String temp_str;
char buffer[21];
int offset;
strcpy_P(buffer, (char *)pgm_read_word(&(status_table[stat])));
temp_str = (String) buffer;
while (temp_str.endsWith(" ")) {
temp_str.remove(temp_str.length() - 1);
}
if (timeout > 0) {
temp_str = temp_str + " in " + String (timeout);
}
temp_str.replace("", "\xEF");
temp_str.replace("", "\xF5");
temp_str.replace("", "\xE1");
bool vorne = false;
while (temp_str.length() < 20) {
if (vorne) {
temp_str = " " + temp_str;
} else {
temp_str = temp_str + " ";
}
vorne = !vorne;
}
lcd.setCursor(0, 2);
delay(2);
//lcd.print(fillup(temp_str));
lcd.print(temp_str);
}
void LCD_Head() {
lcd.setCursor(0, 0);
delay(2);
lcd.print(fillup(lcdHead));
}
void Char2IP(char* str) {
char *ptr;
char delimiter[] = ".";
ptr = strtok(str, delimiter);
byte_array[0] = byte(atoi(ptr));
ptr = strtok(NULL, delimiter);
byte_array[1] = byte(atoi(ptr));
ptr = strtok(NULL, delimiter);
byte_array[2] = byte(atoi(ptr));
ptr = strtok(NULL, delimiter);
byte_array[3] = byte(atoi(ptr));
}
void printErrorMessage(uint8_t e, bool eol = true) // INI-File
{
switch (e) {
case IniFile::errorNoError:
PgmPrintln("no error");
break;
case IniFile::errorFileNotFound:
PgmPrintln("file not found");
break;
case IniFile::errorFileNotOpen:
PgmPrintln("file not open");
break;
case IniFile::errorBufferTooSmall:
PgmPrintln("buffer too small");
break;
case IniFile::errorSeekError:
PgmPrintln("seek error");
break;
case IniFile::errorSectionNotFound:
PgmPrintln("section not found");
break;
case IniFile::errorKeyNotFound:
PgmPrintln("key not found");
break;
case IniFile::errorEndOfFile:
PgmPrintln("end of file");
break;
case IniFile::errorUnknownError:
PgmPrintln("unknown error");
break;
default:
PgmPrintln("unknown error value");
break;
}
if (eol)
PgmPrintln("");
}
void ReadIniFile() {
IniFile ini(configFile);
if (!ini.open()) {
PgmPrintln("Ini file ");
Serial.print(configFile);
PgmPrintln(" does not exist");
// Cannot do anything else
LCD_Status(17);
while (1)
;
}
PgmPrintln("Ini file found");
// Check the file is valid. This can be used to warn if any lines
// are longer than the buffer.
if (!ini.validate(buffer, bufferLen)) {
PgmPrintln("ini file ");
Serial.print(ini.getFilename());
PgmPrint(" not valid: ");
printErrorMessage(ini.getError());
// Cannot do anything else
LCD_Status(17);
while (1)
;
}
if (ini.getValue("Generator", "lostmain", buffer, bufferLen)) {
lostmain = int(atoi(buffer));
}
if (ini.getValue("Generator", "backmain", buffer, bufferLen)) {
backmain = int(atoi(buffer));
}
if (ini.getValue("Generator", "glowtime", buffer, bufferLen)) {
glowtime = int(atoi(buffer));
}
if (ini.getValue("Generator", "ignitiontime", buffer, bufferLen)) {
ignitiontime = int(atoi(buffer));
}
if (ini.getValue("Generator", "starttrying", buffer, bufferLen)) {
starttrying = int(atoi(buffer));
}
if (ini.getValue("Generator", "genstartdelay", buffer, bufferLen)) {
genstartdelay = int(atoi(buffer));
}
if (ini.getValue("Generator", "genstoppdelay", buffer, bufferLen)) {
genstoppdelay = int(atoi(buffer));
}
if (ini.getValue("Generator", "switchdelay", buffer, bufferLen)) {
switchdelay = int(atoi(buffer));
}
if (ini.getValue("Generator", "minbatvoltage", buffer, bufferLen)) {
minbatvoltage = float(atof(buffer));
}
if (ini.getValue("Network", "Host", buffer, bufferLen)) {
strcpy(host,buffer);
}
if (ini.getValue("Network", "Domain", buffer, bufferLen)) {
strcpy(domain,buffer);
}
if (ini.getValue("Network", "IP", buffer, bufferLen)) {
Char2IP(buffer);
for (int i = 0; i < 4; i++) {
ip[i] = byte_array[i];
}
}
if (ini.getValue("Network", "DNS", buffer, bufferLen)) {
Char2IP(buffer);
for (int i = 0; i < 4; i++) {
dns[i] = byte_array[i];
}
}
if (ini.getValue("Network", "GW", buffer, bufferLen)) {
Char2IP(buffer);
for (int i = 0; i < 4; i++) {
gateway[i] = byte_array[i];
}
}
if (ini.getValue("Network", "SUB", buffer, bufferLen)) {
Char2IP(buffer);
for (int i = 0; i < 4; i++) {
subnet[i] = byte_array[i];
}
}
if (ini.getValue("Network", "NTP", buffer, bufferLen)) {
strcpy(ntp,buffer);
}
if (ini.getValue("Mail", "Server", buffer, bufferLen)) {
strcpy(m_server, buffer);
}
if (ini.getValue("Mail", "From", buffer, bufferLen)) {
m_from = (String) buffer;
}
if (ini.getValue("Mail", "To", buffer, bufferLen)) {
m_to = (String) buffer;
}
if (ini.getValue("Mail", "To_RN", buffer, bufferLen)) {
m_to_rn = (String) buffer;
}
if (ini.getValue("Mail", "From_RN", buffer, bufferLen)) {
m_from_rn = (String) buffer;
}
if (ini.getValue("Webserver", "Mail", buffer, bufferLen)) {
strcpy(Mail_File, buffer);
}
if (ini.getValue("Webserver", "ATS", buffer, bufferLen)) {
strcpy(ATS_File, buffer);
}
if (ini.getValue("Webserver", "Config", buffer, bufferLen)) {
strcpy(Config_File, buffer);
}
if (ini.getValue("Webserver", "CSS", buffer, bufferLen)) {
strcpy(CSS_File, buffer);
}
bool found = ini.getValue("HC", "enable", buffer, bufferLen, HCenable);
if (ini.getValue("HC", "Server", buffer, bufferLen)) {
strcpy(HCServer, buffer);
}
if (ini.getValue("HC", "Request", buffer, bufferLen)) {
strcpy(HCRequest, buffer);
}
if (ini.getValue("HC", "Start", buffer, bufferLen)) {
HCStart = int(atoi(buffer));
}
if (ini.getValue("HC", "Stop", buffer, bufferLen)) {
HCStop = int(atoi(buffer));
}
if (ini.getValue("HC", "Error", buffer, bufferLen)) {
HCError = int(atoi(buffer));
}
if (ini.getValue("HC", "Loss", buffer, bufferLen)) {
HCLoss = int(atoi(buffer));
}
if (ini.getValue("HC", "Back", buffer, bufferLen)) {
HCBack = int(atoi(buffer));
}
found = ini.getValue("VZ", "enable", buffer, bufferLen, VZenable);
if (ini.getValue("VZ", "Server", buffer, bufferLen)) {
strcpy(VZServer, buffer);
}
if (ini.getValue("VZ", "Request", buffer, bufferLen)) {
strcpy(VZRequest, buffer);
}
if (ini.getValue("VZ", "UUID", buffer, bufferLen)) {
strcpy(UUID, buffer);
}
found = ini.getValue("MQTT", "enable", buffer, bufferLen, MQTTenable);
if (ini.getValue("MQTT", "Server", buffer, bufferLen)) {
strcpy(MQTTServer, buffer);
}
if (ini.getValue("MQTT", "Topic", buffer, bufferLen)) {
strcpy(MQTTTopic, buffer);
}
if (ini.getValue("MQTT", "Username", buffer, bufferLen)) {
strcpy(MQTTUsername, buffer);
}
if (ini.getValue("MQTT", "Password", buffer, bufferLen)) {
strcpy(MQTTPassword, buffer);
}
}
void piep() {
digitalWrite(PIN_BUZZER, HIGH);
delay(100);
digitalWrite(PIN_BUZZER, LOW);
}
String form(String str, int c) {
while (str.length() < c) {
str = " " + str;
}
return str;
}
float GetBatteryVoltage() {
float vout = 0.0;
float vin = 0.0;
float R1 = 99600.0;
// float R1 = 100000.0; // resistance of R1 (100K)
float R2 = 10015.0;
// float R2 = 10000.0; // resistance of R2 (10K)
int value = 0;
value = analogRead(BAT_INPUT);
vout = (value * 5.0) / 1024.0;
vin = vout / (R2 / (R1 + R2));
if (vin < 0.09) {
vin = 0.0; //statement to quash undesired reading !
}
return vin;
}
String ConvSeperator(String str) {
str.replace(".", ",");
return str;
}
void softReset() {
asm volatile (" jmp 0");
}
void Alert(char* server, char* request, byte message) {
if (HCenable) {
unsigned long byteCount = 0;
EthernetClient client;
if (client.connect(server, 80)) {
client.print(request);
client.print(message);
client.println(F(" HTTP/1.1"));
client.print(F("Host: "));
client.println(server);
client.println(F("Connection: close"));
client.println();
delay(1);
client.stop();
} else {
Serial.print(F("Connection to "));
Serial.print(server);
Serial.println(F(" failed!"));
}
}
}
// call back for file timestamps
void dateTime(uint16_t* date, uint16_t* time) {
*date = FAT_DATE(Clock.getYear()+2000, Clock.getMonth(Century), Clock.getDate());
*time = FAT_TIME(Clock.getHour(h12, PM), Clock.getMinute(), Clock.getSecond());
}
void PushVZ(char* server, char* request, char* uuid, float wert) {
if (VZenable) {
EthernetClient client;
char w;
if (client.connect(server, 80)) {
client.print(request);
client.print(uuid);
client.print(F(".json?operation=add&value="));
client.print((int) wert);
client.println(F(" HTTP/1.1"));
client.print(F("Host: "));
client.println(server);
client.println(F("Connection: close"));
client.println();
delay(10);
client.stop();
} else {
Serial.print(F("Connection to "));
Serial.print(server);
Serial.println(F(" failed!"));
}
}
}
// MQTT Callback
void MQTTmessageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Reset
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[0])));
if (topic == MQTTTopic + (String) buffer && payload == "true") {
PgmPrintln("Anforderung per MQTT zum Reset");
sendEmail(16);
softReset();
}
// Betrieb (netz)
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[1])));
if (topic == MQTTTopic + (String) buffer && payload == "netz") {
PgmPrintln("Anforderung per NQTT zum Netzbetrieb");
Status = 0; // "Netzbetrieb"
Alert(HCServer, HCRequest, HCStop);
if (!Generator(OFF)) {
SwitchNetzbetrieb();
sendEmail(2);
} else {
Status = 4; // "Strung Manuell"
sendEmail(3);
}
Print_Status[Status];
}
// Betrieb (generator)
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[1])));
if (topic == MQTTTopic + (String) buffer && payload == "generator") {
PgmPrintln("Anforderung per MQTT zum Generatorbetrieb");
Status = 2; // "Generatorbetrieb"
Alert(HCServer, HCRequest, HCStop);
if (!Generator(OFF)) {
SwitchNetzbetrieb();
sendEmail(2);
} else {
Status = 4; // "Strung Manuell"
sendEmail(3);
}
Print_Status[Status];
}
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[2])));
if (topic == MQTTTopic + (String) buffer && payload == "send") {
PgmPrintln("Anforderung per MQTT zum Status-Mail-Versand");
sendEmail(14);
}
// Generator start
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[3])));
if (topic == MQTTTopic + (String) buffer && payload == "start") {
PgmPrintln("Anforderung per MQTT zum Generatorstart");
Alert(HCServer, HCRequest, HCStart);
if (Generator(ON)) {
Status = 18; // "Generator luft"
sendEmail(4);
} else {
Status = 4; // "Strung Manuell"
Alert(HCServer, HCRequest, HCError);
sendEmail(5);
}
Print_Status[Status];
}
// Generator stop
strcpy_P(buffer, (char *)pgm_read_word(&(mqtt_table[3])));
if (topic == MQTTTopic + (String) buffer && payload == "stop") {
PgmPrintln("Anforderung per MQTT zum Generatorstopp");
Alert(HCServer, HCRequest, HCStop);
if (!Generator(OFF)) {
Status = 0; // "Netzbetrieb"
sendEmail(6);
} else {
Status = 4; // Strung Manuell
sendEmail(7);
}
Print_Status[Status];
}
}
body {
background-color: #151515;
font-family: Helvetica, Arial, Geneva, sans-serif;
color: yellow;
font-size: 3vmin;
}
.hl {
font-size: 6vmin;
font-weight: bold;
}
a {
text-decoration: none;
}
table#a, td#a {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: left;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#b, td#b {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: center;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#c, td#c{
width: 80%;
table-layout:fixed;
text-align: center;
border: none;
/*border: 1px solid white;*/
padding: 10px;
}
.e {
font-size: 5vmin;
font-weight: bold;
}
.n {
font-size: 4vmin;
color: lightyellow;
}
.v {
font-size: 5vmin;
color: lightyellow;
}
.gn {
background-color: #58FA58;
color: black;
}
.rt {
background-color: #FA5858;
color: black;
}
.gr {
/*background-color: #BDBDBD;*/
background-color: lightyellow;
color: black;
}
.bl {
background-color: #5882FA;
color: black;
}
.ge {
background-color: #F4FA58;
color: black;
}
input[type="submit"], input[type="button"]{
width: 35vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
input[type="file"]{
width: 68vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
<html><head>
<title>ATS</title>
<link rel="shortcut icon" type="image/x-icon" href="http://webserver.com/favicon.ico">
<meta name="referrer" content="no-referrer" />
<meta http-equiv="refresh" content="10; URL=http://ats.webserver.com">
</head><body><center>
<div class="hl">Automatic-Transfer-Switch</div>
<div class="h2">Version %ver% (c) 2019 by Jörg Mahn</div><br>
<table id="a">
<tr><td id="a"><font class="e">Status:</font></td><td id="a"><font class="v">%sta%</font></td></tr>
<tr><td id="a"><font class="e">Generator:</font></td><td id="a"><font class="v">%gen%</font></td></tr>
<tr><td id="a"><font class="e">Zeit:</font></td><td id="a"><font class="v">%d% %z%</font></td></tr>
<tr><td id="a"><font class="e">RAM:</font></td><td id="a"><font class="v">%ram% Bytes frei</font></td></tr>
<tr><td id="a"><font class="e">ATS:</font></td><td id="a"><font class="v">%sys% °C</font></td></tr>
<tr><td id="a"><font class="e">Halle:</font></td><td id="a"><font class="v">%aut% °C</font></td></tr>
<tr><td id="a"><font class="e">Motor:</font></td><td id="a"><font class="v">%mot% °C</font></td></tr>
<tr><td id="a"><font class="e">Batterie:</font></td><td id="a"><font class="v">%bat% V</font></td></tr>
<tr><td id="a"><font class="e">Leistung:</font></td><td id="a"><font class="v">%p%</font></td></tr>
</table><br>
<table id="b">
<tr><td id="b"><font class="e">L1</font></td><td id="b"><font class="e">L2</font></td><td id="b"><font class="e">L3</font></td></tr>
<tr><td id="b"><font class="v">%1v% V</font></td><td id="b"><font class="v">%2v% V</font></td><td id="b"><font class="v">%3v% V</font></td></tr>
<tr><td id="b"><font class="v">%1c% A</font></td><td id="b"><font class="v">%2c% A</font></td><td id="b"><font class="v">%3c% A</font></td></tr>
</table><br>
<form method="get" name="ats">
<table id="c">
<tr><td id="c"><input type="submit" class="gr" name="refresh" value="Aktualisieren"></td>
<td id="c"><input type="submit" class="gr" name="sendmail" value="EMail senden"></td></tr>
<tr><td id="c"><input type="submit" class="gr" name="config" value="Konfiguration"></td>
<td id="c"><input type="submit" class="ge" name="reset" value="System Reset"></td></tr>
<tr><td id="c"><input type="submit" class="gr" name="genstop" value="Generator stoppen"></td>
<td id="c"><input type="submit" class="bl" name="genstart" value="Generator starten"></td></tr>
<tr><td id="c"><input type="submit" class="gn" name="mainbet" value="Netzbetrieb"></td>
<td id="c"><input type="submit" class="rt" name="genbet" value="Generatorbetrieb"></td></tr>
</table>
</form>
</center></body></html>
<html><head>
<title>ATS-Konfiguration</title>
<link rel="shortcut icon" type="image/x-icon" href="http://webserver.com/favicon.ico">
<meta name="referrer" content="no-referrer" />
</head><body><center>
<div class="hl">Automatic-Transfer-Switch</div>
<div class="h2">Version %ver% (c) 2019 by Jörg Mahn</div>
<br><div class="h2">Generator</div>
<table id="a">
<tr><td id="a"><font class="n">Umschaltzeit nach Stromausfall</font></td><td id="a"><font class="n">%lm% Min.</font></td></tr>
<tr><td id="a"><font class="n">Umschaltzeit nach Stromwiederkehr</font></td><td id="a"><font class="n">%bm% Min.</font></td></tr>
<tr><td id="a"><font class="n">Vorglühdauer</font></td><td id="a"><font class="n">%gt% Sek.</font></td></tr>
<tr><td id="a"><font class="n">Startdauer</font></td><td id="a"><font class="n">%it% Millisek.</font></td></tr>
<tr><td id="a"><font class="n">Startversuche</font></td><td id="a"><font class="n">%st% Mal</font></td></tr>
<tr><td id="a"><font class="n">Wartezeit nach Start</font></td><td id="a"><font class="n">%startd% Sek.</font></td></tr>
<tr><td id="a"><font class="n">Wartezeit nach Stop</font></td><td id="a"><font class="n">%stopd% Sek.</font></td></tr>
<tr><td id="a"><font class="n">Umschaltdauer</font></td><td id="a"><font class="n">%sd% Sek.</font></td></tr>
<tr><td id="a"><font class="n">Warnschwelle Starterbatterie</font></td><td id="a"><font class="n">%mv% Volt</font></td></tr>
</table>
<br><div class="h2">SD-Card</div>
<table id="a">
<tr><td id="a"><font class="n">Name</font></td>
<td id="a"><font class="n">Größe</font></td>
<td id="a"><font class="n">löschen</font></td></tr>
<sdcontent>Hier wird der Inhalt der SD-Card eingefuegt</sdcontent>
</table>
<br><div class="h2">Datei Speichern</div>
<form method="post" enctype="multipart/form-data">
<table id="c">
<tr><td id="c" colspan="2"><input class="gr" type="file" name="datei" accept="text/*"></td></tr>
<tr><td id="c"><input class="gr" type="submit" value="Speichern"></td>
<td id="c"><input class="gr" type="button" value="Aktualisieren" onclick="window.location.href='http://ats.joergmahn.de/?config'" /></td></tr>
<tr><td id="c"><input class="gr" type="button" value="Zurück" onclick="window.location.href='http://ats.joergmahn.de'" /></td>
<td id="c"><input class="gr" type="button" value="Neu Starten" onclick="window.location.href='http://ats.joergmahn.de/?reset'" /></td></tr>
</table>
</form>
</center></body></html>
<!DOCTYPE html>
<html>
<head>
<title>ATS</title>
</head>
<body>
<font size="5"><b>Automatic-Transfer-Switch</b></font><br>
<font>(c) 2019 by Jörg Mahn</font><br><br>
<table>
<tr><td><b>Version:</b></td><td><a href="ats.webserver.com">%ver%</a></td></tr>
<tr><td><b>Status:</b></td><td>%sta%</td></tr>
<tr><td><b>Generator:</b></td><td>%gen%</td></tr>
<tr><td><b>RAM:</b></td><td>%ram% Bytes frei</td></tr>
<tr><td><b>Zeit:</b></td><td>%d% %z%</td></tr>
<tr><td><b>ATS:</b></td><td>%sys% °C</td></tr>
<tr><td><b>Motor:</b></td><td>%mot% °C</td></tr>
<tr><td><b>Halle:</b></td><td>%aut% °C</td></tr>
<tr><td><b>Batterie:</b></td><td>%bat% V</td></tr>
<tr><td><b>Leistung:</b></td><td>%p%</td></tr>
</table>
<br>
<table>
<tr><td><b>L1</b></td><td><b>L2</b></td><td><b>L3</b></td><td></tr>
<tr><td>%1v% V</td><td>%2v% V</td><td>%3v% V</td><td></tr>
<tr><td>%1c% A</td><td>%2c% A</td><td>%3c% A</td><td></tr>
</table>
</body>
</html>
; INI-Datei fuer den ATS (c) 2019 by Joerg Mahn
;
; Wegen der Puffer-Einstellung, darf die maximale Zeilenlaenge 80 Zeichen
; nicht ueberschreiten!
;
[Generator]
; Zeit in Minuten bis Stromausfall akzeptiert wird
lostmain = 5
; Zeit in Minuten bis nach Strom-Wiederkehr auf
; Normalbetrieb zurueck geschaltet wird
backmain = 20
; Vorglueh-Zeit in Sekunden (15)
glowtime = 15
; Startdauer (x 100ms) (8)
ignitiontime = 8
; Startversuche (3)
starttrying = 3
; Wartezeit nach dem Start in Sekunden
genstartdelay = 8
; Wartezeit nach dem Stopp in Sekunden
genstoppdelay = 5
; Dauer zwischen den Umschaltungen in Sekunden (2)
switchdelay = 2
; Minimale Batteriespannung bevor Warnung (20.0)
minbatvoltage = 20.0
[Network]
Host = ats
Domain = webserver.com
IP = 192.168.100.29
DNS = 192.168.100.1
GW = 192.168.100.251
SUB = 255.255.255.0
; ntp = Zeitserver, IP oder Hostname
NTP = ptbtime1.ptb.de
[Mail]
Server = mail.webserver.com
To = me@webserver.com
To_RN = Joerg Mahn
From = ats@webserver.com
From_RN = ATS
[Webserver]
; Webfrontend Vorlagen
Mail = mail.htm
ATS = ats.htm
Config = config.htm
CSS = ats.css
[HC]
; Homecontrol
; Texte werden in der settings.ini (Haussteuerung - Sound) festgelegt.
enable = true
Server = home.webserver.com
Request = GET /sound/index.php?
Start = 7
Stop = 8
Error = 9
Loss = 10
Back = 11
[VZ]
; Volkazaehler
; Werte werden an die angegebene UUID gepusht.
enable = true
Server = vz.webserver.com
Request = GET /middleware/data/
UUID = f4fd4310-e4ee-11e9-9c45-a9ae0b49728e
[MQTT]
; Zugangsdaten zum Broker und Topic-Settings
; <Topic>/set zum Konfigurieren
; <Topic>/get zum Abfragen
enable = true
Server = mqtt.webserver.com
Topic = home/ats
Username = user
Password = *******
body {
background-color: #151515;
font-family: Helvetica, Arial, Geneva, sans-serif;
color: yellow;
font-size: 3vmin;
}
.hl {
font-size: 6vmin;
font-weight: bold;
}
a {
text-decoration: none;
}
table#a, td#a {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: left;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#b, td#b {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: center;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#c, td#c{
width: 80%;
table-layout:fixed;
text-align: center;
border: none;
/*border: 1px solid white;*/
padding: 10px;
}
.e {
font-size: 5vmin;
font-weight: bold;
}
.n {
font-size: 4vmin;
color: lightyellow;
}
.v {
font-size: 5vmin;
color: lightyellow;
}
.gn {
background-color: #58FA58;
color: black;
}
.rt {
background-color: #FA5858;
color: black;
}
.gr {
/*background-color: #BDBDBD;*/
background-color: lightyellow;
color: black;
}
.bl {
background-color: #5882FA;
color: black;
}
.ge {
background-color: #F4FA58;
color: black;
}
input[type="submit"], input[type="button"]{
width: 35vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
input[type="file"]{
width: 68vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
body {
background-color: #151515;
font-family: Helvetica, Arial, Geneva, sans-serif;
color: yellow;
font-size: 3vmin;
}
.hl {
font-size: 6vmin;
font-weight: bold;
}
a {
text-decoration: none;
}
table#a, td#a {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: left;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#b, td#b {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: center;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#c, td#c{
width: 80%;
table-layout:fixed;
text-align: center;
border: none;
/*border: 1px solid white;*/
padding: 10px;
}
.e {
font-size: 5vmin;
font-weight: bold;
}
.n {
font-size: 4vmin;
color: lightyellow;
}
.v {
font-size: 5vmin;
color: lightyellow;
}
.gn {
background-color: #58FA58;
color: black;
}
.rt {
background-color: #FA5858;
color: black;
}
.gr {
/*background-color: #BDBDBD;*/
background-color: lightyellow;
color: black;
}
.bl {
background-color: #5882FA;
color: black;
}
.ge {
background-color: #F4FA58;
color: black;
}
input[type="submit"], input[type="button"]{
width: 35vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
input[type="file"]{
width: 68vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
body {
background-color: #151515;
font-family: Helvetica, Arial, Geneva, sans-serif;
color: yellow;
font-size: 3vmin;
}
.hl {
font-size: 6vmin;
font-weight: bold;
}
a {
text-decoration: none;
}
table#a, td#a {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: left;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#b, td#b {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: center;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#c, td#c{
width: 80%;
table-layout:fixed;
text-align: center;
border: none;
/*border: 1px solid white;*/
padding: 10px;
}
.e {
font-size: 5vmin;
font-weight: bold;
}
.n {
font-size: 4vmin;
color: lightyellow;
}
.v {
font-size: 5vmin;
color: lightyellow;
}
.gn {
background-color: #58FA58;
color: black;
}
.rt {
background-color: #FA5858;
color: black;
}
.gr {
/*background-color: #BDBDBD;*/
background-color: lightyellow;
color: black;
}
.bl {
background-color: #5882FA;
color: black;
}
.ge {
background-color: #F4FA58;
color: black;
}
input[type="submit"], input[type="button"]{
width: 35vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
input[type="file"]{
width: 68vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
body {
background-color: #151515;
font-family: Helvetica, Arial, Geneva, sans-serif;
color: yellow;
font-size: 3vmin;
}
.hl {
font-size: 6vmin;
font-weight: bold;
}
a {
text-decoration: none;
}
table#a, td#a {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: left;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#b, td#b {
height: 5vmin;
width: 80%;
table-layout:fixed;
text-align: center;
border-collapse: collapse;
border: 1px solid white;
padding: 5px;
}
table#c, td#c{
width: 80%;
table-layout:fixed;
text-align: center;
border: none;
/*border: 1px solid white;*/
padding: 10px;
}
.e {
font-size: 5vmin;
font-weight: bold;
}
.n {
font-size: 4vmin;
color: lightyellow;
}
.v {
font-size: 5vmin;
color: lightyellow;
}
.gn {
background-color: #58FA58;
color: black;
}
.rt {
background-color: #FA5858;
color: black;
}
.gr {
/*background-color: #BDBDBD;*/
background-color: lightyellow;
color: black;
}
.bl {
background-color: #5882FA;
color: black;
}
.ge {
background-color: #F4FA58;
color: black;
}
input[type="submit"], input[type="button"]{
width: 35vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
input[type="file"]{
width: 68vmin;
font-size: 3vmin;
border: none;
padding: 20px;
text-align: center;
display: inline-block;
}
<html><head>
<title>ATS</title>
<link rel="shortcut icon" type="image/x-icon" href="http://joergmahn.de/ats.ico">
<meta name="referrer" content="no-referrer" />
<meta http-equiv="refresh" content="10; URL=http://ats.joergmahn.de">
</head><body><center>
<div class="hl">Automatic-Transfer-Switch</div>
<div class="h2">Version %ver% (c) 2019 by Jörg Mahn</div><br>
<table id="a">
<tr><td id="a"><font class="e">Status:</font></td><td id="a"><font class="v">%sta%</font></td></tr>
<tr><td id="a"><font class="e">Generator:</font></td><td id="a"><font class="v">%gen%</font></td></tr>
<tr><td id="a"><font class="e">Zeit:</font></td><td id="a"><font class="v">%d% %z%</font></td></tr>
<tr><td id="a"><font class="e">RAM:</font></td><td id="a"><font class="v">%ram% Bytes frei</font></td></tr>
<tr><td id="a"><font class="e">ATS:</font></td><td id="a"><font class="v">%sys% °C</font></td></tr>
<tr><td id="a"><font class="e">Halle:</font></td><td id="a"><font class="v">%aut% °C</font></td></tr>
<tr><td id="a"><font class="e">Motor:</font></td><td id="a"><font class="v">%mot% °C</font></td></tr>
<tr><td id="a"><font class="e">Batterie:</font></td><td id="a"><font class="v">%bat% V</font></td></tr>
<tr><td id="a"><font class="e">Leistung:</font></td><td id="a"><font class="v">%p%</font></td></tr>
</table><br>
<table id="b">
<tr><td id="b"><font class="e">L1</font></td><td id="b"><font class="e">L2</font></td><td id="b"><font class="e">L3</font></td></tr>
<tr><td id="b"><font class="v">%1v% V</font></td><td id="b"><font class="v">%2v% V</font></td><td id="b"><font class="v">%3v% V</font></td></tr>
<tr><td id="b"><font class="v">%1c% A</font></td><td id="b"><font class="v">%2c% A</font></td><td id="b"><font class="v">%3c% A</font></td></tr>
</table><br>
<form method="get" name="ats">
<table id="c">
<tr><td id="c"><input type="submit" class="gr" name="refresh" value="Aktualisieren"></td>
<td id="c"><input type="submit" class="gr" name="sendmail" value="EMail senden"></td></tr>
<tr><td id="c"><input type="submit" class="gr" name="config" value="Konfiguration"></td>
<td id="c"><input type="submit" class="ge" name="reset" value="System Reset"></td></tr>
<tr><td id="c"><input type="submit" class="gr" name="genstop" value="Generator stoppen"></td>
<td id="c"><input type="submit" class="bl" name="genstart" value="Generator starten"></td></tr>
<tr><td id="c"><input type="submit" class="gn" name="mainbet" value="Netzbetrieb"></td>
<td id="c"><input type="submit" class="rt" name="genbet" value="Generatorbetrieb"></td></tr>
</table>
</form>
</center></body></html>
Comments