From efe5cfa22db077f2acea710d33d934f1fecc2f29 Mon Sep 17 00:00:00 2001 From: drg Date: Sat, 11 Apr 2026 21:25:05 +0200 Subject: [PATCH] Optimize memory usage and fix MQTT HA discovery payload --- src/fooclock.ino | 164 ++++++++++++++++++++--------------------------- 1 file changed, 71 insertions(+), 93 deletions(-) diff --git a/src/fooclock.ino b/src/fooclock.ino index 2d675f3..89f6fa9 100644 --- a/src/fooclock.ino +++ b/src/fooclock.ino @@ -3,11 +3,12 @@ #include #include #include +#include // ---------------- MQTT Settings ------------------ const char *mqtt_server = "mqtt.chaospott.de"; // <<< anpassen! const int mqtt_port = 1883; -const char *mqtt_client = "fooclock"; +const char *mqtt_client = "fooclock7"; // Eindeutige ID für die Uhr in Home Assistant const char *device_id = "fooclock_01"; @@ -79,7 +80,6 @@ const int timeZone = 2; // Central European Time (summertime) //const int timeZone = -7; // Pacific Daylight Time (USA) time_t cur_time = 0; -time_t alarm_goal = 0; bool update_done = false; bool second_changed = false; bool init_done = false; @@ -87,26 +87,24 @@ bool transition_active = false; bool swipe_active = false; bool shrink_active = false; int cur_update = 0; -int shift_state = 0; +uint8_t shift_state = 0; int update_counter = 0; -int spinner_pos = 0; -int animation = 0; +uint8_t spinner_pos = 0; +uint8_t animation = 0; EthernetUDP Udp; -EthernetUDP AlarmClock; -unsigned int alarmClockPort = 123; // local port to listen for custom AlarmClock packets unsigned int localPort = 8888; // local port to listen for UDP packets -int OutputEnable = 3; +const uint8_t OutputEnable = 3; //Pin connected to ST_CP of 74HC595 -int latchPin = 4; +const uint8_t latchPin = 4; //Pin connected to SH_CP of 74HC595 -int clockPin = 2; +const uint8_t clockPin = 2; //Pin connected to DS of 74HC595 -int dataPin = 5; +const uint8_t dataPin = 5; // 4 // /======\ @@ -119,9 +117,9 @@ int dataPin = 5; // || 64 || // \======/ O 1 -int duration = 10; +const uint8_t duration = 10; -int digits[10] = { +const uint8_t digits[10] = { 238, // 0 0xEE 34, // 1 0x22 214, // 2 0xD6 @@ -134,7 +132,7 @@ int digits[10] = { 126 // 9 0x7E }; -int letter[26] = { +const uint8_t letter[26] = { 191, // A 248, // b 204, // C @@ -163,30 +161,31 @@ int letter[26] = { 214, // Z }; -int frame[6] = { +uint8_t frame[6] = { 0, letter[_T], letter[_R], letter[_A], letter[_T], letter[_S] // Display "Start" (write from right to left into the array) }; -int today[7] = { +uint8_t 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(F("Nachricht empfangen [")); Serial.print(topic); - Serial.print("] "); - Serial.println(message); + Serial.print(F("] ")); + for (unsigned int i = 0; i < length; i++) { + Serial.write(payload[i]); + } + Serial.println(); - if (String(topic) == mqtt_command_topic) { - if (message == "ON") { + if (strcmp(topic, mqtt_command_topic) == 0) { + if (length == 2 && payload[0] == 'O' && payload[1] == 'N') { displayEnabled = true; - Serial.println("MQTT: Display eingeschaltet"); - } else if (message == "OFF") { + Serial.println(F("MQTT: Display eingeschaltet")); + } else if (length == 3 && payload[0] == 'O' && payload[1] == 'F' && payload[2] == 'F') { displayEnabled = false; - Serial.println("MQTT: Display ausgeschaltet"); + Serial.println(F("MQTT: Display ausgeschaltet")); } // Sende den neuen Status sofort zurück publishState(); @@ -204,52 +203,52 @@ void publishState() { // ---------------- 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]; + // Home Assistant Discovery-JSON im Flash halten und aus PROGMEM formatieren. + char discoveryPayload[320]; + int payloadLen = snprintf_P(discoveryPayload, sizeof(discoveryPayload), + PSTR("{\"name\":\"7FooClock\",\"unique_id\":\"%s_display\",\"cmd_t\":\"%s\",\"stat_t\":\"%s\",\"pl_on\":\"ON\",\"pl_off\":\"OFF\",\"icon\":\"mdi:clock-digital\",\"device\":{\"identifiers\":[\"%s\"],\"name\":\"7FooClock\",\"manufacturer\":\"DIY\"}}"), + device_id, + mqtt_command_topic, + mqtt_state_topic, + device_id); - // 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 - ); + if (payloadLen < 0) { + Serial.println(F("HA discovery build failed")); + return; + } + if ((size_t)payloadLen >= sizeof(discoveryPayload)) { + Serial.print(F("HA discovery truncated, len=")); + Serial.println(payloadLen); + return; + } // Nachricht mit Retain-Flag publishen, damit HA sie auch nach einem Neustart findet - client.publish(mqtt_discovery_topic, discoveryPayload, true); + bool ok = client.publish(mqtt_discovery_topic, discoveryPayload, true); + if (!ok) { + Serial.print(F("HA discovery publish failed, len=")); + Serial.println(payloadLen); + } else { + Serial.print(F("HA discovery published, len=")); + Serial.println(payloadLen); + } } // ---------------- MQTT Reconnect ------------------ void reconnect() { while (!client.connected()) { - Serial.print("MQTT Verbindung herstellen..."); + Serial.print(F("MQTT Verbindung herstellen...")); if (client.connect(mqtt_client)) { - Serial.println("verbunden."); + Serial.println(F("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(F("Fehler, rc=")); Serial.print(client.state()); - Serial.println(" -> neuer Versuch in 5s"); + Serial.println(F(" -> neuer Versuch in 5s")); delay(5000); } } @@ -280,6 +279,7 @@ void setup() { // <<< MQTT init >>> client.setServer(mqtt_server, mqtt_port); + client.setBufferSize(384); client.setCallback(mqttCallback); } @@ -458,12 +458,12 @@ void dim(int direction, int lower_limit, int upper_limit) { if (direction == DOWN && local_counter % range <= range) { write_out = 255 - (upper_limit - (local_counter) % range); analogWrite(OutputEnable, write_out); - Serial.println("Down ->"); + Serial.println(F("Down ->")); Serial.println(write_out); } else { write_out = 255 - (lower_limit + (local_counter % range)); analogWrite(OutputEnable, write_out); - Serial.println("UP ->"); + Serial.println(F("UP ->")); Serial.println(write_out); } } @@ -489,7 +489,7 @@ void spin(int digit) { spinner_pos++; }; -void shrink(int direction, int *new_data) { +void shrink(int direction, uint8_t *new_data) { static int j = 0; static int height = 0; int bitmask = 0; @@ -526,7 +526,7 @@ void shrink(int direction, int *new_data) { } else { height--; } - Serial.print("Shrink.J = "); + Serial.print(F("Shrink.J = ")); Serial.println(j); } else { for (int digit = 0; digit < 6; digit++) { @@ -538,7 +538,7 @@ void shrink(int direction, int *new_data) { } else { height++; } - Serial.print("Shrink.J = "); + Serial.print(F("Shrink.J = ")); Serial.println(j); } @@ -546,7 +546,7 @@ void shrink(int direction, int *new_data) { } } -void swipe(int direction, int *new_data) { +void swipe(int direction, uint8_t *new_data) { static int j = 0; static int height = 0; int bitmask = 0; @@ -588,7 +588,7 @@ void swipe(int direction, int *new_data) { } else { height--; } - Serial.print("Swipe. J = "); + Serial.print(F("Swipe. J = ")); Serial.println(j); } else { for (int digit = 0; digit < 6; digit++) { @@ -600,7 +600,7 @@ void swipe(int direction, int *new_data) { } else { height++; } - Serial.print("Swipe. J = "); + Serial.print(F("Swipe. J = ")); Serial.println(j); } @@ -608,21 +608,21 @@ void swipe(int direction, int *new_data) { } }; -void combine(int *a) { +void combine(uint8_t *a) { static int i = 0; static int j = 0; - int current[6]; + uint8_t current[6]; if (transition_active == false) { transition_active = true; - memcpy(current, frame, 6 * sizeof(int)); + memcpy(current, frame, sizeof(current)); j = 0; } if (update_counter % 33 == 0 && j < 18) { - Serial.print("i : "); + Serial.print(F("i : ")); Serial.print(i); - Serial.print(" j : "); + Serial.print(F(" j : ")); Serial.println(j); if (j % 3 == 0) { @@ -648,8 +648,8 @@ void shift_right(int neu) { frame[5] = neu; } -void transition(int *a) { - if (update_counter % 150 == 0 && cur_update != update_counter && shift_state < sizeof(today) / 2) { +void transition(uint8_t *a) { + if (update_counter % 150 == 0 && cur_update != update_counter && shift_state < (sizeof(today) / sizeof(today[0]))) { //assuming the Array contains only ints, //which have a sizeof 2 on this Arduino Serial.println(sizeof(a)); @@ -659,28 +659,6 @@ void transition(int *a) { } } - -/*-------- Alarm Clock code ---------*/ -const int AlarmClockPacketSize = 256; -byte AlarmBuffer[AlarmClockPacketSize]; - -int listenForAlarm() { - while (AlarmClock.parsePacket() > 0); - Serial.println("Any Alarm imminent?"); - uint32_t packageBegin = millis(); - while (millis() - packageBegin < 1500) { - int size = AlarmClock.parsePacket(); - if (size >= AlarmClockPacketSize) { - Serial.println("Alarm seems to be imminent."); - AlarmClock.read(AlarmBuffer, AlarmClockPacketSize); - alarm_goal = (unsigned long) AlarmBuffer; - return 1; - } - } - Serial.println("No Alarm present!"); - return 0; -} - /*-------- NTP code ----------*/ const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message @@ -694,7 +672,7 @@ time_t getNtpTime() { while (millis() - beginWait < 1500) { int size = Udp.parsePacket(); if (size >= NTP_PACKET_SIZE) { - Serial.println("Receive NTP Response"); + Serial.println(F("Receive NTP Response")); Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer unsigned long secsSince1900; // convert four bytes starting at location 40 to a long integer