/***************************************************
  Juni 2025
  HTU Sensor mit MQTT Broker Verbindung
  Ausgabe am seriellen Monitor
 ****************************************************/

#include <Wire.h>
#include "Adafruit_HTU21DF.h"
#include <ESP8266WiFi.h>
#include <PubSubClient.h>  // von Nick für MQTT Datenaustausch

#define BUILT_IN_LED 2  // GPIO for D1-mini

//===========================
WiFiClient espClient;
PubSubClient client(espClient);

// WLAN Zugangsdaten
const char* ssid = "HSB-Gast";
const char* password = "";

// MQTT Zugangsdaten
//const char* mqtt_broker = "test.mosquitto.org";       // Broker-IP
const char* mqtt_broker = "mqtt.eclipseprojects.io";  // Broker-IP
const char* mqtt_username = "";
const char* mqtt_password = "";
const int mqtt_port = 1883;

// MQTT stuff Topic
const char* mqtt_main_Topic = "My_SmartHome/Wohnraum_1/#";           // alle Topic subscribe
const char* mqtt_LWT_Topic = "My_SmartHome/Wohnraum_1/Wifi_Status";  // Last Will & Testament if connection lost

const char* mqtt_TimeStamp_Topic = "My_SmartHome/Wohnraum_1/TimeStamp";
const char* mqtt_debug_Topic = "My_SmartHome/Wohnraum_1/Debug_Info";
const char* mqtt_DHT_Temp_Topic = "My_SmartHome/Wohnraum_1/HTU_Temperatur_°C";
const char* mqtt_DHT_Luft_Topic = "My_SmartHome/Wohnraum_1/HTU_Luftfeuchte_%rel";

const char* mqtt_DeviceName_Topic = "My_SmartHome/Wohnraum_1/DeviceName";
const char* mqtt_DevicePlace_Topic = "My_SmartHome/Wohnraum_1/DevicePlace";

const char* deviceName = "D1mini_HTU_MQTT";
const char* devicePlace = "HTL-Leonding";

uint32_t chipId = ESP.getChipId();
String clientId = "ESP-" + String(chipId) + "-" + String(random(0xffff), HEX);  // eine eindeutige einzigartige ID
String str_WiFi_IP = " -- ";

int qos = 1;         // MQTT Quality of Service
bool retain = true;  // sende diese Message if new Client

// Connect Vin to 3-5VDC
// Connect GND to ground
// Connect SCL to I2C clock pin (A5 on UNO)
// Connect SDA to I2C data pin (A4 on UNO)

Adafruit_HTU21DF htu = Adafruit_HTU21DF();


unsigned long lastSensorTime = 0;
const int intervalSensor = 10000;  // 10 sek
unsigned long lastTestTime = 0;
const int intervalMQTT_Test = 20000;  // 5 sek

//============  Program Code Message income ===================
void callbackMessage(char* topic, byte* payload, unsigned int length) {
  String incommingMessage = "";
  for (int i = 0; i < length; i++) incommingMessage += (char)payload[i];
  //Serial.println("Message arrived \t[" + String(topic) + "]\t" + incommingMessage);   // jede abonnierte Message kommt hier herein

  //==============MESSAGE RECEPTION HERE ====================
  if (strcmp(topic, mqtt_DHT_Temp_Topic) == 0) {
    Serial.println("MQTT received: HTU-Temperatur = " + incommingMessage + " °C");
  }
  // hier die nächsten Abfragen auf Topics ....
}

//================ publising as string ====================
void publishMessage(const char* topic, String payload , boolean retained) {
  if (client.publish(topic, payload.c_str(), retained)) {
    //Serial.println("Message publised [" + String(topic) + "]: " + payload);
  }
}

// Fehlercode der MQTT Verbindung
void printMQTTError(int state) {
  switch (state) {
    case -4: Serial.println("Timeout (Broker nicht erreichbar)"); break;
    case -3: Serial.println("Verbindung verloren"); break;
    case -2: Serial.println("Connect fehlgeschlagen"); break;
    case -1: Serial.println("Disconnected"); break;
    case 1: Serial.println("Falsches Protokoll"); break;
    case 2: Serial.println("Client-ID abgelehnt"); break;
    case 3: Serial.println("Broker nicht verfügbar"); break;
    case 4: Serial.println("Falscher Benutzername/Passwort"); break;
    case 5: Serial.println("Nicht autorisiert"); break;
    default: Serial.println("Unbekannter Fehler"); break;
  }
}


//--------- setup
void setup() {
  Serial.begin(115200);
  Serial.println("\n\n************  HTU21D-F Test mit MQTT - Verbindung");
  pinMode(BUILT_IN_LED, OUTPUT);

  if (!htu.begin()) {
    Serial.println("Couldn't find sensor!");
    while (1);  // Endlosschleife
  }

  // WLAN Verbindung herstellen
  Serial.print("\nWiFi try to connect...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    digitalWrite(BUILT_IN_LED, !digitalRead(BUILT_IN_LED));
  }

  digitalWrite(BUILT_IN_LED, HIGH);  // LED aus

  Serial.print("\nWiFi connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("MAC address:  ");
  Serial.println(WiFi.macAddress());
  str_WiFi_IP = WiFi.localIP().toString();

  // MQTT Verbindung herstellen
  Serial.print("\nMQTT-Verbindung herstellen mit ");
  Serial.println(clientId);

  client.setServer(mqtt_broker, 1883);
  client.setCallback(callbackMessage);   // für einkommende Nachrichten callback Funktion registrieren

  bool clientConnected = client.connect(clientId.c_str(), mqtt_username, mqtt_password, mqtt_LWT_Topic, qos, retain, "offline");
  //client.connect(clientId.c_str());         // einfach ohne user und passwort
  delay(1000);  // etwas Zeit für den Client

  // MQTT Verbindung prüfen
  if (!clientConnected) {
    Serial.println("MQTT-Verbindung fehlgeschlagen!");
    Serial.print("Fehlercode: ");
    Serial.println(client.state());  // Gibt den Fehlerstatus zurück
    printMQTTError(client.state());  // siehe oben, Fehlercode in Klartext
  } else {
    Serial.print("MQTT connected to ");
    Serial.print(mqtt_broker);
    Serial.print(" on port: ");
    Serial.print(mqtt_port);

    client.subscribe(mqtt_main_Topic);  //SUBSCRIBE TO TOPIC HERE, wenn Nachrichten empfangen werden sollen.

    publishMessage(mqtt_LWT_Topic, "online", retain);
    publishMessage(mqtt_DeviceName_Topic, deviceName, retain);
    publishMessage(mqtt_DevicePlace_Topic, devicePlace, retain);
    //publishMessage(msgWiFi_topic, String(ssid).c_str(), retain);
    //publishMessage(msgWiFi_ip_topic, str_WiFi_data.c_str(), retain);
  }

  Serial.println("\nStart loop...\n ");
}

//------------ Prüft die Verbindung zum MQTT Broker und baut diese wieder auf, wenn disconnected
void reconnect() {
  while (!client.connected()) {  // Loop until we're reconnected
    delay(1000);
    Serial.println("MQTT not connected, try to connect...");
    bool clientConnected = client.connect(clientId.c_str(), mqtt_username, mqtt_password, mqtt_LWT_Topic, qos, retain, "offline");
    if (!clientConnected) {
      Serial.print("failed, rc= ");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");  // Wait 5 seconds before retrying
      printMQTTError(client.state());             // siehe oben Fehlercode in Klartext
      delay(5000);
    } else {
      Serial.print("MQTT reconnected to");
      Serial.print(mqtt_broker);
      Serial.print(" on port: ");
      Serial.print(mqtt_port);

      client.subscribe(mqtt_main_Topic);  //SUBSCRIBE TO TOPIC HERE, wenn Nachrichten empfangen werden sollen.

      publishMessage(mqtt_LWT_Topic, "online", retain);
      publishMessage(mqtt_DeviceName_Topic, deviceName, retain);
      publishMessage(mqtt_DevicePlace_Topic, devicePlace, retain);
      //publishMessage(msgWiFi_topic, String(ssid).c_str(), retain);
      //publishMessage(msgWiFi_ip_topic, str_WiFi_data.c_str(), retain);
    }
  }
}

//------------- main loop
void loop() {
  if (millis() > lastTestTime + intervalMQTT_Test) {
    // MQTT Verbindung prüfuen und wiederherstellen, wenn nötig
    if (!client.connected()) {
      reconnect();
    } else {
      publishMessage(mqtt_LWT_Topic, "online", retain);
      publishMessage(mqtt_DeviceName_Topic, deviceName, retain);
      publishMessage(mqtt_DevicePlace_Topic, devicePlace, retain);
    }
    lastTestTime = millis();
  }
  client.loop();

  // Sensor Abfrage in bestimmten Zeitinterval, kein delay() verwenden
  if (millis() > lastSensorTime + intervalSensor) {
    float temp = htu.readTemperature();
    float rel_hum = htu.readHumidity();

    Serial.print("Temp: ");
    Serial.print(temp);
    Serial.print(" °C");
    Serial.print("\t\t");

    Serial.print("Humidity: ");
    Serial.print(rel_hum);
    Serial.println(" \%rel");

    publishMessage(mqtt_DHT_Temp_Topic, String(temp, 1).c_str(), retain);
    publishMessage(mqtt_DHT_Luft_Topic, String(rel_hum, 0).c_str(), retain);

    lastSensorTime = millis();
  }
}
