Optimize memory usage and fix MQTT HA discovery payload

This commit is contained in:
2026-04-11 21:25:05 +02:00
parent b6b36eda9f
commit efe5cfa22d

View File

@@ -3,11 +3,12 @@
#include <SPI.h> #include <SPI.h>
#include <TimerOne.h> #include <TimerOne.h>
#include <PubSubClient.h> #include <PubSubClient.h>
#include <avr/pgmspace.h>
// ---------------- MQTT Settings ------------------ // ---------------- MQTT Settings ------------------
const char *mqtt_server = "mqtt.chaospott.de"; // <<< anpassen! const char *mqtt_server = "mqtt.chaospott.de"; // <<< anpassen!
const int mqtt_port = 1883; const int mqtt_port = 1883;
const char *mqtt_client = "fooclock"; const char *mqtt_client = "fooclock7";
// Eindeutige ID für die Uhr in Home Assistant // Eindeutige ID für die Uhr in Home Assistant
const char *device_id = "fooclock_01"; 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) //const int timeZone = -7; // Pacific Daylight Time (USA)
time_t cur_time = 0; time_t cur_time = 0;
time_t alarm_goal = 0;
bool update_done = false; bool update_done = false;
bool second_changed = false; bool second_changed = false;
bool init_done = false; bool init_done = false;
@@ -87,26 +87,24 @@ bool transition_active = false;
bool swipe_active = false; bool swipe_active = false;
bool shrink_active = false; bool shrink_active = false;
int cur_update = 0; int cur_update = 0;
int shift_state = 0; uint8_t shift_state = 0;
int update_counter = 0; int update_counter = 0;
int spinner_pos = 0; uint8_t spinner_pos = 0;
int animation = 0; uint8_t animation = 0;
EthernetUDP Udp; 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 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 //Pin connected to ST_CP of 74HC595
int latchPin = 4; const uint8_t latchPin = 4;
//Pin connected to SH_CP of 74HC595 //Pin connected to SH_CP of 74HC595
int clockPin = 2; const uint8_t clockPin = 2;
//Pin connected to DS of 74HC595 //Pin connected to DS of 74HC595
int dataPin = 5; const uint8_t dataPin = 5;
// 4 // 4
// /======\ // /======\
@@ -119,9 +117,9 @@ int dataPin = 5;
// || 64 || // || 64 ||
// \======/ O 1 // \======/ O 1
int duration = 10; const uint8_t duration = 10;
int digits[10] = { const uint8_t digits[10] = {
238, // 0 0xEE 238, // 0 0xEE
34, // 1 0x22 34, // 1 0x22
214, // 2 0xD6 214, // 2 0xD6
@@ -134,7 +132,7 @@ int digits[10] = {
126 // 9 0x7E 126 // 9 0x7E
}; };
int letter[26] = { const uint8_t letter[26] = {
191, // A 191, // A
248, // b 248, // b
204, // C 204, // C
@@ -163,30 +161,31 @@ int letter[26] = {
214, // Z 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) 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" 184, 220, 200, 200, 240, 0, 0 // Display "Hello"
}; };
// ---------------- MQTT Callback ------------------ // ---------------- MQTT Callback ------------------
void mqttCallback(char *topic, byte *payload, unsigned int length) { void mqttCallback(char *topic, byte *payload, unsigned int length) {
payload[length] = '\0'; // String terminieren Serial.print(F("Nachricht empfangen ["));
String message = String((char *) payload);
Serial.print("Nachricht empfangen [");
Serial.print(topic); Serial.print(topic);
Serial.print("] "); Serial.print(F("] "));
Serial.println(message); for (unsigned int i = 0; i < length; i++) {
Serial.write(payload[i]);
}
Serial.println();
if (String(topic) == mqtt_command_topic) { if (strcmp(topic, mqtt_command_topic) == 0) {
if (message == "ON") { if (length == 2 && payload[0] == 'O' && payload[1] == 'N') {
displayEnabled = true; displayEnabled = true;
Serial.println("MQTT: Display eingeschaltet"); Serial.println(F("MQTT: Display eingeschaltet"));
} else if (message == "OFF") { } else if (length == 3 && payload[0] == 'O' && payload[1] == 'F' && payload[2] == 'F') {
displayEnabled = false; displayEnabled = false;
Serial.println("MQTT: Display ausgeschaltet"); Serial.println(F("MQTT: Display ausgeschaltet"));
} }
// Sende den neuen Status sofort zurück // Sende den neuen Status sofort zurück
publishState(); publishState();
@@ -204,52 +203,52 @@ void publishState() {
// ---------------- MQTT Publish Home Assistant Discovery Message ------------------ // ---------------- MQTT Publish Home Assistant Discovery Message ------------------
void publishDiscoveryMessage() { void publishDiscoveryMessage() {
// Use a fixed-size buffer to create the JSON payload. This is much more // Home Assistant Discovery-JSON im Flash halten und aus PROGMEM formatieren.
// memory-efficient than using the Arduino String class for concatenation. char discoveryPayload[320];
char discoveryPayload[400]; 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\"}}"),
// 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, device_id,
mqtt_command_topic, mqtt_command_topic,
mqtt_state_topic, mqtt_state_topic,
device_id 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 // 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 ------------------ // ---------------- MQTT Reconnect ------------------
void reconnect() { void reconnect() {
while (!client.connected()) { while (!client.connected()) {
Serial.print("MQTT Verbindung herstellen..."); Serial.print(F("MQTT Verbindung herstellen..."));
if (client.connect(mqtt_client)) { if (client.connect(mqtt_client)) {
Serial.println("verbunden."); Serial.println(F("verbunden."));
// Sobald verbunden, Befehls-Topic abonnieren // Sobald verbunden, Befehls-Topic abonnieren
client.subscribe(mqtt_command_topic); client.subscribe(mqtt_command_topic);
// Discovery-Nachricht und initialen Status senden // Discovery-Nachricht und initialen Status senden
publishDiscoveryMessage(); publishDiscoveryMessage();
publishState(); publishState();
} else { } else {
Serial.print("Fehler, rc="); Serial.print(F("Fehler, rc="));
Serial.print(client.state()); Serial.print(client.state());
Serial.println(" -> neuer Versuch in 5s"); Serial.println(F(" -> neuer Versuch in 5s"));
delay(5000); delay(5000);
} }
} }
@@ -280,6 +279,7 @@ void setup() {
// <<< MQTT init >>> // <<< MQTT init >>>
client.setServer(mqtt_server, mqtt_port); client.setServer(mqtt_server, mqtt_port);
client.setBufferSize(384);
client.setCallback(mqttCallback); client.setCallback(mqttCallback);
} }
@@ -458,12 +458,12 @@ void dim(int direction, int lower_limit, int upper_limit) {
if (direction == DOWN && local_counter % range <= range) { if (direction == DOWN && local_counter % range <= range) {
write_out = 255 - (upper_limit - (local_counter) % range); write_out = 255 - (upper_limit - (local_counter) % range);
analogWrite(OutputEnable, write_out); analogWrite(OutputEnable, write_out);
Serial.println("Down ->"); Serial.println(F("Down ->"));
Serial.println(write_out); Serial.println(write_out);
} else { } else {
write_out = 255 - (lower_limit + (local_counter % range)); write_out = 255 - (lower_limit + (local_counter % range));
analogWrite(OutputEnable, write_out); analogWrite(OutputEnable, write_out);
Serial.println("UP ->"); Serial.println(F("UP ->"));
Serial.println(write_out); Serial.println(write_out);
} }
} }
@@ -489,7 +489,7 @@ void spin(int digit) {
spinner_pos++; spinner_pos++;
}; };
void shrink(int direction, int *new_data) { void shrink(int direction, uint8_t *new_data) {
static int j = 0; static int j = 0;
static int height = 0; static int height = 0;
int bitmask = 0; int bitmask = 0;
@@ -526,7 +526,7 @@ void shrink(int direction, int *new_data) {
} else { } else {
height--; height--;
} }
Serial.print("Shrink.J = "); Serial.print(F("Shrink.J = "));
Serial.println(j); Serial.println(j);
} else { } else {
for (int digit = 0; digit < 6; digit++) { for (int digit = 0; digit < 6; digit++) {
@@ -538,7 +538,7 @@ void shrink(int direction, int *new_data) {
} else { } else {
height++; height++;
} }
Serial.print("Shrink.J = "); Serial.print(F("Shrink.J = "));
Serial.println(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 j = 0;
static int height = 0; static int height = 0;
int bitmask = 0; int bitmask = 0;
@@ -588,7 +588,7 @@ void swipe(int direction, int *new_data) {
} else { } else {
height--; height--;
} }
Serial.print("Swipe. J = "); Serial.print(F("Swipe. J = "));
Serial.println(j); Serial.println(j);
} else { } else {
for (int digit = 0; digit < 6; digit++) { for (int digit = 0; digit < 6; digit++) {
@@ -600,7 +600,7 @@ void swipe(int direction, int *new_data) {
} else { } else {
height++; height++;
} }
Serial.print("Swipe. J = "); Serial.print(F("Swipe. J = "));
Serial.println(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 i = 0;
static int j = 0; static int j = 0;
int current[6]; uint8_t current[6];
if (transition_active == false) { if (transition_active == false) {
transition_active = true; transition_active = true;
memcpy(current, frame, 6 * sizeof(int)); memcpy(current, frame, sizeof(current));
j = 0; j = 0;
} }
if (update_counter % 33 == 0 && j < 18) { if (update_counter % 33 == 0 && j < 18) {
Serial.print("i : "); Serial.print(F("i : "));
Serial.print(i); Serial.print(i);
Serial.print(" j : "); Serial.print(F(" j : "));
Serial.println(j); Serial.println(j);
if (j % 3 == 0) { if (j % 3 == 0) {
@@ -648,8 +648,8 @@ void shift_right(int neu) {
frame[5] = neu; frame[5] = neu;
} }
void transition(int *a) { void transition(uint8_t *a) {
if (update_counter % 150 == 0 && cur_update != update_counter && shift_state < sizeof(today) / 2) { if (update_counter % 150 == 0 && cur_update != update_counter && shift_state < (sizeof(today) / sizeof(today[0]))) {
//assuming the Array contains only ints, //assuming the Array contains only ints,
//which have a sizeof 2 on this Arduino //which have a sizeof 2 on this Arduino
Serial.println(sizeof(a)); 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 ----------*/ /*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message 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) { while (millis() - beginWait < 1500) {
int size = Udp.parsePacket(); int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) { 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 Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900; unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer // convert four bytes starting at location 40 to a long integer