//Tft Display
#include <Adafruit_GFX.h>
#include <TouchScreen.h>
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
//Uhr
#include <Wire.h>
#include "RTClib.h"
RTC_DS3231 rtc;
//DHT22
#include "DHT.h"
#define DHTPIN 53
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
//DS18B20
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 51
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature DS18B20(&oneWire);
#include "Arduino.h"
#include "VeDirectFrameHandler.h"
VeDirectFrameHandler myve1;
VeDirectFrameHandler myve2;
// Touch Panel
#define MINPRESSURE 100
#define MAXPRESSURE 1000
//***********True Components Display*********************************************************************************************
constexpr int XP = 8, XM = A2, YP = A3, YM = 9; //320x480
constexpr int TS_LEFT = 921, TS_RT = 107, TS_TOP = 73, TS_BOT = 923;
//****************AZ Delivery Display
//constexpr int XP = 6, XM = A2, YP = A1, YM = 7; //320x480
//constexpr int TS_LEFT = 187, TS_RT = 916, TS_TOP = 939, TS_BOT = 211;
//********************************************************************************************************************************
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
TSPoint p;
//tft Button
Adafruit_GFX_Button page1_btn, page2_btn, page3_btn, page4_btn, page5_btn, page6_btn, page7_btn;;
Adafruit_GFX_Button menu_btn, next_btn;
int pixel_x, pixel_y;
//Relais Heizung
#define RELAISHEIZUNG 52
//Aktor Alarm
#define RELAISINNENLICHT 29
//Relais Arbeitsscheinwerfer
#define RELAISSCHEINWERFER 22
//Alarm Kontakte
#define AKONTAKT 27
#define SKONTAKT 23
int Alarm_ausgeloest = 0;
static unsigned long prev_millis;
//Farben definieren
constexpr uint16_t BLACK = 0x0000, BLUE = 0x001F, RED = 0xF800, DARKGREEN = 0x02C2, CYAN = 0x07FF, WHITE = 0xFFFF, YELLOW = 0xFFE0;
//Screen
enum pageId {MENU, BATTERIE, SOLAR, SOLAR1, SOLAR2, WETTER, HEIZUNG, ALARM, SCHEINWERFER};
//Standard Seite
unsigned int currentPage = MENU, oldPage = -1;
void setup(void) {
Serial.begin(115200);
Serial1.begin(19200);
Serial2.begin(19200);
Serial3.begin(9600);
Serial1.flush();
Serial2.flush();
tft.begin();
tft.setRotation(0);
tft.fillScreen(BLACK);
rtc.begin();
//rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
//rtc.adjust(DateTime(2025, 1, 16, 13, 59, 0)); //zum manuellen einstellen der Uhrzeit
dht.begin();
DS18B20.begin();
pinMode(RELAISHEIZUNG, OUTPUT);
pinMode(RELAISINNENLICHT, OUTPUT);
pinMode(RELAISSCHEINWERFER, OUTPUT);
pinMode(SKONTAKT, INPUT_PULLUP);
myve1.setErrorHandler(&LogHelper);
myve1.addHexCallback(&HexCallback, (void*)42);
currentPage = WETTER;
}
bool down;
void loop(void) {
down = Touch_getXY();
switch (currentPage) {
case MENU:
if (currentPage != oldPage) drawMenuScreen();
redrawButtons();
break;
case HEIZUNG:
if (currentPage != oldPage) {
Heizung();
currentPage = MENU;
}
break;
case WETTER:
if (currentPage != oldPage) drawTemperaturScreen();
getUhr();
if (prev_millis + 2893 < millis()) {
getTemperatur();
prev_millis = millis();
}
menu_btn.press(down && menu_btn.contains(pixel_x, pixel_y));
if (menu_btn.justReleased())
menu_btn.drawButton();
if (menu_btn.justPressed()) {
menu_btn.drawButton(true);
currentPage = MENU;
}
break;
case BATTERIE:
if (currentPage != oldPage) drawBatterieScreen();
//ReadVEData2();
if (millis() - prev_millis > 1000) {
getShunt();
prev_millis = millis();
}
menu_btn.press(down && menu_btn.contains(pixel_x, pixel_y));
if (menu_btn.justReleased())
menu_btn.drawButton();
if (menu_btn.justPressed()) {
menu_btn.drawButton(true);
currentPage = MENU;
}
break;
case SOLAR:
if (currentPage != oldPage) drawSolarScreen();
//ReadVEData1();
if (millis() - prev_millis > 1000) {
getSolar();
prev_millis = millis();
}
menu_btn.press(down && menu_btn.contains(pixel_x, pixel_y));
next_btn.press(down && next_btn.contains(pixel_x, pixel_y));
if (menu_btn.justReleased())
menu_btn.drawButton();
if (menu_btn.justPressed()) {
menu_btn.drawButton(true);
currentPage = MENU;
}
break;
case SOLAR1:
if (currentPage != oldPage) drawSolar1Screen();
//ReadVEData1();
if (millis() - prev_millis > 1000) {
getSolar1();
prev_millis = millis();
}
menu_btn.press(down && menu_btn.contains(pixel_x, pixel_y));
next_btn.press(down && next_btn.contains(pixel_x, pixel_y));
if (menu_btn.justReleased())
menu_btn.drawButton();
if (next_btn.justReleased())
next_btn.drawButton();
if (menu_btn.justPressed()) {
menu_btn.drawButton(true);
currentPage = MENU;
}
if (next_btn.justPressed()) {
next_btn.drawButton(true);
currentPage = SOLAR2;
}
break;
case SOLAR2:
if (currentPage != oldPage) drawSolar2Screen();
//ReadVEData1();
if (millis() - prev_millis > 1000) {
getSolar2();
prev_millis = millis();
}
menu_btn.press(down && menu_btn.contains(pixel_x, pixel_y));
if (menu_btn.justReleased())
menu_btn.drawButton();
if (menu_btn.justPressed()) {
menu_btn.drawButton(true);
currentPage = MENU;
}
break;
case ALARM:
if (currentPage != oldPage) drawAlarmScreen();
Ausgeloest();
if (prev_millis + 2893 < millis()) {
getUhr();
getTemperatur();
prev_millis = millis();
}
menu_btn.press(down && menu_btn.contains(pixel_x, pixel_y));
if (menu_btn.justReleased())
menu_btn.drawButton();
if (menu_btn.justPressed()) {
menu_btn.drawButton(true);
currentPage = MENU;
}
break;
case SCHEINWERFER:
if (currentPage != oldPage) {
Scheinwerfer();
updateScheinwerferButton();
currentPage = MENU;
}
break;
}
if (oldPage == currentPage) {
down = Touch_getXY();
}
else {
down = false;
}
Sabotage();
Bluetooth();
ReadVEData1();
ReadVEData2();
}
/*****************************************************************************************************************************************************************************************
SCREENS
******************************************************************************************************************************************************************************************/
void drawMenuScreen() {
Alarm_ausgeloest = 0;
tft.fillScreen(BLACK);
tft.setTextColor(BLUE);
digitalWrite(RELAISINNENLICHT, LOW);
tft.setCursor(50, 8);
tft.println(F("WoMo-Monitor"));
tft.setTextSize(2);
page1_btn.initButton(&tft, 120, 60, 210, 35 , WHITE, CYAN, BLACK, "Heizung", 2);
page2_btn.initButton(&tft, 120, 172, 210, 35 , WHITE, CYAN, BLACK, "Batterie", 2);
page3_btn.initButton(&tft, 175, 227, 100, 35 , WHITE, CYAN, BLACK, "Sol his", 2);
page4_btn.initButton(&tft, 120, 115, 210, 35 , WHITE, CYAN, BLACK, "Temp", 2);
page5_btn.initButton(&tft, 65, 227, 100, 35 , WHITE, CYAN, BLACK, "Sol akt", 2);
page6_btn.initButton(&tft, 65, 282, 100, 35 , RED, CYAN, RED, "Alarm", 2);
updateScheinwerferButton();
page1_btn.drawButton(false);
page2_btn.drawButton(false);
page3_btn.drawButton(false);
page4_btn.drawButton(false);
page5_btn.drawButton(false);
page6_btn.drawButton(false);
tft.drawRoundRect(5, 32, 230, 280, 10, BLUE);
oldPage = currentPage;
}
void updateScheinwerferButton() {
if (digitalRead(RELAISSCHEINWERFER) == HIGH) {
page7_btn.initButton(&tft, 175, 282, 100, 35 , WHITE, YELLOW, BLACK, "Scheinw", 2);
}
else {
page7_btn.initButton(&tft, 175, 282, 100, 35 , WHITE, CYAN, BLACK, "Scheinw", 2);
}
page7_btn.drawButton(false);
}
//Batterie
void drawBatterieScreen() {
tft.setTextSize(2);
tft.fillScreen(BLACK);
tft.setTextColor(WHITE,BLACK);
tft.setCursor(10, 10);
tft.println(F("Batteriespannung V:"));
tft.setCursor(10, 70);
tft.println(F("Ladestrom A:"));
tft.setCursor(10, 130);
tft.println(F("verbrauchte AH:"));
tft.setCursor(10, 190);
tft.println(F("Ladestand %:"));
menu_btn.initButton(&tft, 120, 295, 120, 35, WHITE, CYAN, BLACK, "MENU", 2);
menu_btn.drawButton(false);
oldPage = currentPage;
}
//Solar Leistung,Spannung
void drawSolarScreen() {
tft.setTextSize(2);
tft.fillScreen(BLACK);
tft.setTextColor(WHITE,BLACK);
tft.setCursor(0, 10);
tft.println(F("Leistung W:"));
tft.setCursor(0,70);
tft.println(F("Spannung Panel V:"));
tft.setCursor(0,130);
tft.println(F("Laden Modus"));
tft.setCursor(0, 190);
tft.println(F("Zustand Lastausgang:"));
menu_btn.initButton(&tft, 120, 295, 120, 35, WHITE, CYAN, BLACK, "MENU", 2);
menu_btn.drawButton(false);
oldPage = currentPage;
}
//Solar Ertrag Historie
void drawSolar1Screen() {
tft.setTextSize(2);
tft.fillScreen(BLACK);
tft.setTextColor(WHITE,BLACK);
tft.setCursor(0, 10);
tft.println(F("Ertrag heute Wh:"));
tft.setCursor(0,70);
tft.println(F("P max heute W:"));
tft.setCursor(0,140);
tft.println(F("Ertrag gestern Wh:"));
tft.setCursor(0,200);
tft.println(F("P max gestern W:"));
menu_btn.initButton(&tft, 60, 295, 80, 35, WHITE, CYAN, BLACK, "MENU", 2);
next_btn.initButton(&tft, 180, 295, 80, 35, WHITE, CYAN, BLACK, "INFO", 2);
menu_btn.drawButton(false);
next_btn.drawButton(false);
oldPage = currentPage;
}
// MPPT Regler Info
void drawSolar2Screen() {
tft.setTextSize(2);
tft.fillScreen(BLACK);
tft.setTextColor(WHITE,BLACK);
tft.setCursor(0, 10);
tft.println(F("Product ID:"));
tft.setCursor(0,70);
tft.println(F("Seriennummer:"));
tft.setCursor(0,140);
tft.println(F("Software Version:"));
tft.setCursor(0,200);
menu_btn.initButton(&tft,120, 295, 120, 35, WHITE, CYAN, BLACK, "MENU", 2);
menu_btn.drawButton(false);
oldPage = currentPage;
}
//Temperatur
void drawTemperaturScreen() {
tft.fillScreen(BLACK);
menu_btn.initButton(&tft, 121, 295, 100, 35 , WHITE, CYAN, BLACK, "MENU", 2);
menu_btn.drawButton(false);
oldPage = currentPage;
}
//Alarm
void drawAlarmScreen() {
tft.fillScreen(BLACK);
tft.setTextSize(2);
tft.setTextColor(RED,BLACK);
menu_btn.initButton(&tft,121, 295, 100, 35, WHITE, RED, BLACK, "AUS", 2);
menu_btn.drawButton(false);
oldPage = currentPage;
}
/*************************************************************************************************************************************************************************************************************
Funktionen
*************************************************************************************************************************************************************************************************************/
bool Touch_getXY() {
p = ts.getPoint();
pinMode(YP, OUTPUT);
pinMode(XM, OUTPUT);
digitalWrite(YP, HIGH);
digitalWrite(XM, HIGH);
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) {
pixel_x = map(p.x, TS_LEFT, TS_RT, 0, tft.width());
pixel_y = map(p.y, TS_TOP, TS_BOT, 0, tft.height());
return true;
}
return false;
}
void (*resetFunc) (void) = 0;
void redrawButtons() {
page1_btn.press(down && page1_btn.contains(pixel_x, pixel_y));
page2_btn.press(down && page2_btn.contains(pixel_x, pixel_y));
page3_btn.press(down && page3_btn.contains(pixel_x, pixel_y));
page4_btn.press(down && page4_btn.contains(pixel_x, pixel_y));
page5_btn.press(down && page5_btn.contains(pixel_x, pixel_y));
page6_btn.press(down && page6_btn.contains(pixel_x, pixel_y));
page7_btn.press(down && page7_btn.contains(pixel_x, pixel_y));
if (page1_btn.justReleased()) page1_btn.drawButton();
if (page2_btn.justReleased()) page2_btn.drawButton();
if (page3_btn.justReleased()) page3_btn.drawButton();
if (page4_btn.justReleased()) page4_btn.drawButton();
if (page5_btn.justReleased()) page5_btn.drawButton();
if (page6_btn.justReleased()) page6_btn.drawButton();
if (page7_btn.justReleased()) page7_btn.drawButton();
if (page1_btn.justPressed()) changePage(HEIZUNG, page1_btn);
if (page2_btn.justPressed()) changePage(BATTERIE, page2_btn);
if (page3_btn.justPressed()) changePage(SOLAR1, page3_btn);
if (page4_btn.justPressed()) changePage(WETTER, page4_btn);
if (page5_btn.justPressed()) changePage(SOLAR, page5_btn);
if (page6_btn.justPressed()) changePage(ALARM, page6_btn);
if (page7_btn.justPressed()) changePage(SCHEINWERFER, page7_btn);
}
void changePage(unsigned int newPage, Adafruit_GFX_Button &btn) {
btn.drawButton(true);
currentPage = newPage;
}
//Heizung Relais schalten
void Heizung() {
digitalWrite(RELAISHEIZUNG, HIGH);
delay(1000);
digitalWrite(RELAISHEIZUNG, LOW);
page1_btn.drawButton(false);
}
//Uhrzeit auslesen
void getUhr() {
char daysOfTheWeek[7][12] = { "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"};
tft.setTextSize(3);
tft.setTextColor(WHITE, BLACK);
tft.setCursor(180, 15);
DateTime now = rtc.now();
//Wochentag
tft.print(daysOfTheWeek[now.dayOfTheWeek()]);
//Uhrzeit
tft.setCursor(10, 15);
if (now.hour() < 10) {
tft.print(F("0"));
tft.print(now.hour());
}
else{
tft.print(now.hour(), DEC);
}
tft.print(':');
if (now.minute() < 10) {
tft.print(F("0"));
tft.print(now.minute());
}
else{
tft.print(now.minute(), DEC);
}
tft.print(':');
if (now.second() < 10) {
tft.print(F("0"));
tft.print(now.second());
}
else{
tft.print(now.second(), DEC);
}
}
void getTemperatur() {
float h, te, h_alt, t_alt;
h = dht.readHumidity();
te = dht.readTemperature();
delay(50);
DS18B20.requestTemperatures();
float tpa = DS18B20.getTempCByIndex(0);
if (tpa == -127) {
resetFunc();
}
static float lastTe = -1000.0, lastTpa = -1000.0, lastH = -1.0;
if (te != lastTe || h != lastH || tpa != lastTpa) {
tft.drawLine(0, 60, tft.width() * 2, 60, WHITE); // Linie für Innenbereich
tft.drawLine(0, 185, tft.width() * 2, 185, WHITE); // Linie für Außenbereich
//Innenbereich
tft.setCursor(10, 70);
tft.print(F("innen:"));
int tempColor = (te < 10) ? BLUE : (te >= 10 && te < 25) ? DARKGREEN : RED;
tft.setTextColor(tempColor, BLACK);
tft.setCursor(10, 105);
tft.print(te, 1);
tft.setTextColor(WHITE, BLACK);
tft.write(" ");
tft.write(0xF7);
tft.write("C");
int humidityColor = (h < 40) ? BLUE : (h >= 40 && h < 60) ? DARKGREEN : RED;
tft.setTextColor(humidityColor, BLACK);
tft.setCursor(10, 145);
tft.print(h, 1);
tft.setTextColor(WHITE, BLACK);
tft.write(" ");
tft.write("%");
// Außenbereich
tft.setTextColor(WHITE, BLACK);
tft.setCursor(10, 195);
tft.print(F("aussen:"));
int outsideTempColor = (tpa < 0) ? BLUE : (tpa >= 0 && tpa < 10) ? BLUE : (tpa >= 10 && tpa < 25) ? DARKGREEN : RED;
tft.setTextColor(outsideTempColor, BLACK);
tft.setCursor(10, 235);
tft.print(tpa, 1);
tft.setTextColor(WHITE, BLACK);
tft.write(" ");
tft.write(0xF7);
tft.write("C");
lastTe = te;
lastTpa = tpa;
lastH = h;
}
}
void ReadVEData1(){
if (Serial1.available()) {
//Serial.println("serial1 ok ");
myve1.rxData(Serial1.read());
}
}
void ReadVEData2(){
while (Serial2.available()) {
//Serial.println("serial2 ok ");
myve2.rxData(Serial2.read());
}
}
//Shunt Werte
void getShunt() {
tft.setTextSize(2);
for (int i = 0; i < myve2.veEnd; i++) {
Serial.print(myve2.veName[i]);
Serial.print("= ");
Serial.println(myve2.veValue[i]);
if (strcmp(myve2.veName[i], "V") == 0) {
float value = atof(myve2.veValue[i]);
float result = value / 1000;
tft.setTextColor(WHITE,BLACK);
tft.setCursor(90, 35);
tft.println(result);
}
else if (strcmp(myve2.veName[i], "SOC") == 0) {
float value = atof(myve2.veValue[i]);
float result = value / 10;
tft.setTextColor(WHITE,BLACK);
tft.setCursor(90, 215);
tft.println(result);
}
else if (strcmp(myve2.veName[i], "CE") == 0) {
float value = atof(myve2.veValue[i]);
float result = value / 1000;
tft.setTextColor(WHITE,BLACK);
tft.setCursor(90, 155);
tft.println(result);
}
else if (strcmp(myve2.veName[i], "I") == 0) {
float value = atof(myve2.veValue[i]);
float result = value / 1000;
if (value < 0) {
tft.setTextColor(RED,BLACK);
tft.setCursor(90, 95);
tft.println(result);
}
else {
tft.setTextColor(BLUE,BLACK);
tft.setCursor(90, 95);
tft.print("");
tft.println(result);
}
}
}
}
//Solar Werte Panel
void getSolar() {
tft.setTextColor(WHITE,BLACK);
tft.setTextSize(2);
for (int i = 0; i < myve1.veEnd; i++) {
if (strcmp(myve1.veName[i], "PPV") == 0) {
float value = atof(myve1.veValue[i]);
float result = value;
tft.setTextColor(WHITE,BLACK);
tft.setCursor(90, 35);
tft.println(result);
}
else if (strcmp(myve1.veName[i], "VPV") == 0) {
float value = atof(myve1.veValue[i]);
float result = value /1000;
tft.setCursor(90, 95);
tft.println(result);
}
else if (strcmp(myve1.veName[i], "CS") == 0) {
float value = atof(myve1.veValue[i]);
float result = value;
if (value == 2) {
tft.setTextColor(RED,BLACK);
tft.setCursor(90,155);
tft.write("Error");
}
else if (value == 3) {
tft.setCursor(90,155);
tft.write("BULK ");
}
else if (value == 4) {
tft.setCursor(90,155);
tft.write("ABS ");
}
else if (value == 5) {
tft.setCursor(90,155);
tft.write("FLOAT ");
}
else {
tft.setCursor(90,155);
tft.write("AUS ");
}
}
else if (strcmp(myve1.veName[i], "LOAD") == 0) {
tft.setTextColor(WHITE,BLACK);
tft.setCursor(90, 215);
tft.println(myve1.veValue[i]);
}
}
}
//Solar Ertrag
void getSolar1() {
tft.setTextColor(WHITE,BLACK);
tft.setTextSize(2);
for (int i = 0; i < myve1.veEnd; i++) {
if (strcmp(myve1.veName[i], "H20") == 0) {
float value = atof(myve1.veValue[i]);
float result = value *10;
tft.setCursor(90, 35);
tft.println(result);
}
else if (strcmp(myve1.veName[i], "H21") == 0) {
tft.setCursor(90, 95);
tft.println(myve1.veValue[i]);
}
else if (strcmp(myve1.veName[i], "H22") == 0) {
float value = atof(myve1.veValue[i]);
float result = value *10;
tft.setCursor(90, 165);
tft.println(result);
}
else if (strcmp(myve1.veName[i], "H23") == 0) {
tft.setCursor(90, 225);
tft.println(myve1.veValue[i]);
}
}
}
//MPPT Regler Daten
void getSolar2() {
tft.setTextColor(WHITE,BLACK);
tft.setTextSize(2);
for (int i = 0; i < myve1.veEnd; i++) {
if (strcmp(myve1.veName[i], "PID") == 0) {
tft.setCursor(90, 35);
tft.println(myve1.veValue[i]);
}
else if (strcmp(myve1.veName[i], "SER#") == 0) {
tft.setCursor(60, 100);
tft.println(myve1.veValue[i]);
}
else if (strcmp(myve1.veName[i], "FW") == 0) {
float value = atof(myve1.veValue[i]);
float result = value /100;
tft.setCursor(90, 165);
tft.println(result);
}
}
}
hex frame callback function
void HexCallback(const char* buffer, int size, void* data) {
char tmp[100];
memcpy(tmp, buffer, size*sizeof(char));
tmp[size]=0;
Serial.print("received hex frame: ");
Serial.println(tmp);
}
log helper
void LogHelper(const char* module, const char* error) {
Serial.print(module);
Serial.print(":");
Serial.println(error);
}
//Abfrage Kontakt für MX Alarm
void Sabotage() {
}
//Arbeitsscheinwerfer schalten
void Scheinwerfer() {
static boolean status=false;
status = digitalRead(RELAISSCHEINWERFER);
if (status == LOW) {
digitalWrite(RELAISSCHEINWERFER, HIGH);
delay(1000);
page7_btn.drawButton(false);
}
if (status == HIGH) {
digitalWrite(RELAISSCHEINWERFER, LOW);
delay(1000);
page7_btn.drawButton(false);
}
}
//Bluetooth schalten
void Bluetooth() {
}