diff --git a/platformio.ini b/platformio.ini index 55f7999..b0dcaf2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,3 +12,4 @@ lib_deps = https://github.com/PaulStoffregen/Time TimerOne Ethernet + PubSubClient3 diff --git a/src/fooclock.ino b/src/fooclock.ino index 0764b8c..2d675f3 100644 --- a/src/fooclock.ino +++ b/src/fooclock.ino @@ -2,11 +2,38 @@ #include #include #include +#include + +// ---------------- MQTT Settings ------------------ +const char *mqtt_server = "mqtt.chaospott.de"; // <<< anpassen! +const int mqtt_port = 1883; +const char *mqtt_client = "fooclock"; + +// Eindeutige ID für die Uhr in Home Assistant +const char *device_id = "fooclock_01"; + +// Topics für Home Assistant +const char *mqtt_command_topic = "fooclock/display/set"; // Topic zum Senden von Befehlen (ON/OFF) +const char *mqtt_state_topic = "fooclock/display/state"; // Topic zum Melden des Status (ON/OFF) +const char *mqtt_discovery_topic = "homeassistant/switch/fooclock/display/config"; // HA Auto-Discovery Topic + +EthernetClient ethClient; +PubSubClient client(ethClient); + +bool displayEnabled = true; // Steuerflag für Anzeige + +// Forward declaration der neuen Funktionen +void publishState(); +void publishDiscoveryMessage(); + +// ------------------------------------------------- #define _A 0 #define _B 1 #define _C 2 +#define _D 3 #define _E 4 +#define _F 5 #define _G 6 #define _H 7 #define _I 8 @@ -28,11 +55,11 @@ #define _Y 24 #define _Z 25 -#define UP 1 -#define DOWN 2 +#define UP 1 +#define DOWN 2 -#define light_lvl_standard 60 -#define light_lvl_risen 255 +#define light_lvl_standard 60 +#define light_lvl_risen 255 // ---------------------------------- Definition of Global Variables -------------------------- // @@ -58,9 +85,9 @@ bool second_changed = false; bool init_done = false; bool transition_active = false; bool swipe_active = false; -bool shrink_active = false; // Maybe we should move them to a single variable and set or unset bits inside the variable +bool shrink_active = false; int cur_update = 0; -unsigned int shift_state = 0; +int shift_state = 0; int update_counter = 0; int spinner_pos = 0; int animation = 0; @@ -143,6 +170,92 @@ int today[7] = { 184, 220, 200, 200, 240, 0, 0 // Display "Hello" }; + +// ---------------- MQTT Callback ------------------ +void mqttCallback(char *topic, byte *payload, unsigned int length) { + payload[length] = '\0'; // String terminieren + String message = String((char *) payload); + Serial.print("Nachricht empfangen ["); + Serial.print(topic); + Serial.print("] "); + Serial.println(message); + + if (String(topic) == mqtt_command_topic) { + if (message == "ON") { + displayEnabled = true; + Serial.println("MQTT: Display eingeschaltet"); + } else if (message == "OFF") { + displayEnabled = false; + Serial.println("MQTT: Display ausgeschaltet"); + } + // Sende den neuen Status sofort zurück + publishState(); + } +} + +// ---------------- MQTT Publish State ------------------ +void publishState() { + if (displayEnabled) { + client.publish(mqtt_state_topic, "ON", true); // true = retain message + } else { + client.publish(mqtt_state_topic, "OFF", true); // true = retain message + } +} + +// ---------------- MQTT Publish Home Assistant Discovery Message ------------------ +void publishDiscoveryMessage() { + // Use a fixed-size buffer to create the JSON payload. This is much more + // memory-efficient than using the Arduino String class for concatenation. + char discoveryPayload[400]; + + // Create the JSON payload using snprintf to prevent buffer overflows + snprintf(discoveryPayload, sizeof(discoveryPayload), + "{" + "\"name\":\"FooClock Display\"," + "\"unique_id\":\"%s_display\"," + "\"cmd_t\":\"%s\"," + "\"stat_t\":\"%s\"," + "\"pl_on\":\"ON\"," + "\"pl_off\":\"OFF\"," + "\"icon\":\"mdi:clock-digital\"," + "\"device\":{" + "\"identifiers\":[\"%s\"]," + "\"name\":\"FooClock\"," + "\"manufacturer\":\"DIY\"" + "}" + "}", + device_id, + mqtt_command_topic, + mqtt_state_topic, + device_id + ); + + // Nachricht mit Retain-Flag publishen, damit HA sie auch nach einem Neustart findet + client.publish(mqtt_discovery_topic, discoveryPayload, true); +} + + +// ---------------- MQTT Reconnect ------------------ +void reconnect() { + while (!client.connected()) { + Serial.print("MQTT Verbindung herstellen..."); + if (client.connect(mqtt_client)) { + Serial.println("verbunden."); + // Sobald verbunden, Befehls-Topic abonnieren + client.subscribe(mqtt_command_topic); + // Discovery-Nachricht und initialen Status senden + publishDiscoveryMessage(); + publishState(); + } else { + Serial.print("Fehler, rc="); + Serial.print(client.state()); + Serial.println(" -> neuer Versuch in 5s"); + delay(5000); + } + } +} + + // ----------------------------------- Setup Code ---------------------------------------------- // void setup() { pinMode(OutputEnable, OUTPUT); @@ -164,10 +277,20 @@ void setup() { setSyncProvider(getNtpTime); update_counter = 0; init_done = true; + + // <<< MQTT init >>> + client.setServer(mqtt_server, mqtt_port); + client.setCallback(mqttCallback); } // -------------------------------------- Main Loop ------------------------------------------------- // void loop() { + // <<< MQTT >>> + if (!client.connected()) { + reconnect(); + } + client.loop(); + time_t timestamp = now(); // Sync blinking with second change if (cur_time != timestamp && !second_changed) { @@ -216,7 +339,7 @@ void loop() { shift_state = 0; updateDate(timestamp); - animation = rand() % 6; + animation = (int) (rand() % 6); } // Blinkenfoo - comment to remove pulsating light @@ -233,10 +356,19 @@ void loop() { // ---------------------- Interrupt Handler (Timer1) ------------------- // void updateDisplay() { + if (!displayEnabled) { + // Anzeige aus: alles dunkel + digitalWrite(latchPin, LOW); + for (int digitCount = 5; digitCount >= 0; digitCount--) { + shiftOut(dataPin, clockPin, MSBFIRST, 0); + } + digitalWrite(latchPin, HIGH); + return; + } + // take the latchPin low so // the LEDs don't flicker while you're sending in bits: digitalWrite(latchPin, LOW); - for (int digitCount = 5; digitCount >= 0; digitCount--) { shiftOut(dataPin, clockPin, MSBFIRST, frame[digitCount]); } @@ -244,7 +376,6 @@ void updateDisplay() { digitalWrite(latchPin, HIGH); update_counter++; - if (update_counter == 1000) { update_counter = 0; } @@ -256,6 +387,7 @@ void updateDisplay() { } } + // -------------------------------- Date Functions ---------------------- // void updateDate(time_t t) { today[0] = digits[(day(t) / 10)];