diff --git a/platformio.ini b/platformio.ini index 7982363..2fc5abd 100644 --- a/platformio.ini +++ b/platformio.ini @@ -4,3 +4,4 @@ board = esp12e framework = arduino lib_deps = https://github.com/thorsten-gehrig/arduino-tpuart-knx-user-forum https://github.com/knolleary/pubsubclient + https://github.com/me-no-dev/ESPAsyncWebServer \ No newline at end of file diff --git a/src/Group.cpp b/src/Group.cpp new file mode 100644 index 0000000..18c9d0b --- /dev/null +++ b/src/Group.cpp @@ -0,0 +1,20 @@ +#include "Group.h" + +#include + +Group groups[500]; + +Group *groupCache(KnxTelegram *telegram) { + for (auto &&group: groups) { + if ((group.main == 0 && group.mid == 0 && group.sub == 0) || (group.main == telegram->getTargetMainGroup() && group.mid == telegram->getTargetMiddleGroup() && group.sub == telegram->getTargetSubGroup())) { + group.main = telegram->getTargetMainGroup(); + group.mid = telegram->getTargetMiddleGroup(); + group.sub = telegram->getTargetSubGroup(); + group.timestamp = time(nullptr); + group.value = static_cast(telegram->get1ByteIntValue()) + telegram->get2ByteIntValue() + (telegram->get4BitDirectionValue() ? telegram->get4BitStepsValue() : -telegram->get4BitStepsValue()) + telegram->get2ByteFloatValue() + telegram->get4ByteFloatValue(); + return &group; + } + } + mqttLogError("group cache full"); + return nullptr; +} diff --git a/src/Group.h b/src/Group.h index 1d36d5a..8df60e6 100644 --- a/src/Group.h +++ b/src/Group.h @@ -1,6 +1,8 @@ #ifndef GROUP_H #define GROUP_H +#include + class Group { public: @@ -11,18 +13,14 @@ public: uint8_t sub = 0; - uint8_t dpt = 0; + time_t timestamp = 0; - static Group groups[500]; - - static void write(uint8_t main, uint8_t mid, uint8_t sub) { - for (auto group: groups) { - if (group.main == main && group.mid == mid && group.sub == sub) { - group. - } - } - } + double value = 0; }; +extern Group groups[]; + +Group *groupCache(KnxTelegram *telegram); + #endif diff --git a/src/http.cpp b/src/http.cpp new file mode 100644 index 0000000..1079852 --- /dev/null +++ b/src/http.cpp @@ -0,0 +1,9 @@ +#include "http.h" + +#include + +AsyncWebServer server(80); + +void httpSetup() { + server.begin(); +} \ No newline at end of file diff --git a/src/http.h b/src/http.h new file mode 100644 index 0000000..abcba1f --- /dev/null +++ b/src/http.h @@ -0,0 +1,6 @@ +#ifndef HTTP_H +#define HTTP_H + +void httpSetup(); + +#endif diff --git a/src/knx.cpp b/src/knx.cpp new file mode 100644 index 0000000..f23823e --- /dev/null +++ b/src/knx.cpp @@ -0,0 +1,45 @@ +#include "knx.h" +#include "Group.h" +#include "wifi.h" +#include "time_.h" +#include "mqtt.h" + +#include + +KnxTpUart knx(&Serial1, "15.15.20"); + +void sendGroup(Group *group); + +void knxSetup() { + Serial1.begin(19200, SERIAL_8E1); + knx.uartReset(); + knx.setListenToBroadcasts(true); +} + +void knxLoop() { + if (knx.serialEvent() != KNX_TELEGRAM) { + return; + } + + KnxTelegram *telegram = knx.getReceivedTelegram(); + mqttLogInfo("received: %d/%d/%d", telegram->getTargetMainGroup(), telegram->getTargetMiddleGroup(), telegram->getTargetSubGroup()); + + Group *group = groupCache(telegram); + sendGroup(group); +} + +void sendGroup(Group *group) { + if (group == nullptr || !isTimeSet() || !isWiFiConnected()) { + return; + } + + correctTime(&group->timestamp); + + char topic[64]; + snprintf(topic, sizeof topic, "knx/group/%d/%d/%d", group->main, group->mid, group->sub); + + char payload[128]; + snprintf(payload, sizeof payload, "%d/%d/%d %lld %f", group->main, group->mid, group->sub, group->timestamp, group->value); + + mqttPublish(topic, payload); +} diff --git a/src/knx.h b/src/knx.h new file mode 100644 index 0000000..65417b3 --- /dev/null +++ b/src/knx.h @@ -0,0 +1,8 @@ +#ifndef KNX_H +#define KNX_H + +void knxSetup(); + +void knxLoop(); + +#endif diff --git a/src/main.cpp b/src/main.cpp index 16df186..dc69a71 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,70 +1,25 @@ #include -#include -#include -#include -KnxTpUart knx(&Serial1, "15.15.20"); - -PubSubClient mqtt; - -void knxSetup(); - -void knxLoop(); - -void mqttLoop(); - -void mqttSetup(); - -void wifiSetup(); +#include "knx.h" +#include "wifi.h" +#include "time_.h" +#include "mqtt.h" +#include "http.h" void setup() { - Serial.begin(112500); delay(500); + Serial.begin(112500); Serial.print("\n\n\nStartup\n"); knxSetup(); wifiSetup(); - configTime("Europe/Berlin", "de.pool.ntp.org"); + timeSetup(); mqttSetup(); -} - -void wifiSetup() { - WiFi.hostname("KnxEsp"); - WiFi.begin("HappyNet", "1Grausame!Sackratte7"); - while (WiFi.localIP() == 0UL) { - delay(100); - } - + httpSetup(); } void loop() { knxLoop(); + wifiLoop(); + timeLoop(); mqttLoop(); } - -void mqttSetup() { - mqtt.setServer("10.0.0.50", 1883); - mqtt.setBufferSize(512); -} - -void mqttLoop() { - if (!mqtt.loop()) { - if (!mqtt.connect(WiFi.hostname().c_str())) { - mqtt.disconnect(); - } - } -} - -void knxSetup() { - Serial1.begin(19200, SERIAL_8E1); - knx.uartReset(); - knx.setListenToBroadcasts(true); -} - -void knxLoop() { - auto type = knx.serialEvent(); - if (type != KNX_TELEGRAM) { - return; - } - KnxTelegram *t = knx.getReceivedTelegram(); - Serial.printf("%3d / %3d / %3d len=%d, bool=%d\n", t->getTargetMainGroup(), t->getTargetMiddleGroup(), t->getTargetSubGroup(), t->getPayloadLength(), t->getBool()); -} diff --git a/src/mqtt.cpp b/src/mqtt.cpp new file mode 100644 index 0000000..4753e87 --- /dev/null +++ b/src/mqtt.cpp @@ -0,0 +1,56 @@ +#include "mqtt.h" +#include "wifi.h" + +#include +#include + +PubSubClient mqtt; + +void mqttSetup() { + mqtt.setServer("10.0.0.50", 1883); + mqtt.setBufferSize(512); +} + +void mqttLoop() { + if (!isWiFiConnected()) { + mqtt.disconnect(); + } else if (!mqtt.loop()) { + if (!mqtt.connect(WiFi.hostname().c_str())) { + mqtt.disconnect(); + } + } +} + +void mqttPublish(const char *topic, const char *payload) { + mqtt.publish(topic, payload); +} + +String getLogPrefix(const char *level) { + tm info{}; + getLocalTime(&info); + char buffer[20]; + snprintf(buffer, sizeof buffer, "%04d-%02d-%02d %02d:%02d:%02d %5s ", info.tm_year, info.tm_mon, info.tm_mday, info.tm_hour, info.tm_min, info.tm_sec, level); + return {buffer}; +} + +void mqttLog(const char *level, const char *format, va_list vl) { + char message[400]; + vsnprintf(message, sizeof message, format, vl); + const String topic = String("knx/log/") + level; + const String payload = getLogPrefix(level) + message; + mqttPublish(topic.c_str(), payload.c_str()); +} + +void mqttLogInfo(const char *message, ...) { + va_list vl; + va_start(vl, message); + mqttLog("info", message, vl); + va_end(vl); +} + +void mqttLogError(const char *message, ...) { + va_list vl; + va_start(vl, message); + mqttLog("og/error", message, vl); + va_end(vl); +} diff --git a/src/mqtt.h b/src/mqtt.h new file mode 100644 index 0000000..f73853c --- /dev/null +++ b/src/mqtt.h @@ -0,0 +1,14 @@ +#ifndef MQTT_H +#define MQTT_H + +void mqttSetup(); + +void mqttLoop(); + +void mqttPublish(const char *topic, const char *payload); + +void mqttLogInfo(const char *message, ...); + +void mqttLogError(const char *message, ...); + +#endif diff --git a/src/time_.cpp b/src/time_.cpp new file mode 100644 index 0000000..2d72104 --- /dev/null +++ b/src/time_.cpp @@ -0,0 +1,36 @@ +#include "time_.h" + +#include + +#define TIME_MIN 1728424800 + +bool hasTime = false; + +time_t timeCorrection = 0; + +void timeSetup() { + configTime("Europe/Berlin", "de.pool.ntp.org"); +} + +void timeLoop() { + if (hasTime) { + return; + } + const time_t now = time(nullptr); + if (now >= TIME_MIN) { + hasTime = true; + timeCorrection = now - timeCorrection; + } else { + timeCorrection = now; + } +} + +bool isTimeSet() { + return hasTime; +} + +void correctTime(time_t *t) { + if (*t > 0 && *t < TIME_MIN) { + *t += timeCorrection; + } +} diff --git a/src/time_.h b/src/time_.h new file mode 100644 index 0000000..9cdfe56 --- /dev/null +++ b/src/time_.h @@ -0,0 +1,14 @@ +#ifndef MY_TIME_H +#define MY_TIME_H + +#include + +void timeSetup(); + +void timeLoop(); + +bool isTimeSet(); + +void correctTime(time_t *t); + +#endif diff --git a/src/wifi.cpp b/src/wifi.cpp new file mode 100644 index 0000000..399b1fa --- /dev/null +++ b/src/wifi.cpp @@ -0,0 +1,45 @@ +#include "wifi.h" + +#include + +#define WIFI_TIMEOUT_MILLIS 20000 + +unsigned long wifiConnectBegin = 0; + +bool wifiConnected = false; + +bool isWiFiConnected() { + return wifiConnected; +} + +void wifiSetup() { + wifiConnected = false; + WiFi.disconnect(); + yield(); + + wifiConnectBegin = millis(); + Serial.print("connecting wifi...\n"); + WiFi.hostname("KnxEsp"); + WiFi.begin("HappyNet", "1Grausame!Sackratte7"); +} + +void wifiLoop() { + if (!wifiConnected) { + if (WiFi.localIP() == 0UL) { + if (millis() - wifiConnectBegin > WIFI_TIMEOUT_MILLIS) { + if (wifiConnectBegin != 0) { + Serial.print("wifi timeout\n"); + } + wifiSetup(); + } + } else { + Serial.print("wifi connected\n"); + wifiConnected = true; + } + } else { + if (WiFi.localIP() == 0UL) { + Serial.print("wifi disconnected\n"); + wifiSetup(); + } + } +} diff --git a/src/wifi.h b/src/wifi.h new file mode 100644 index 0000000..4985adc --- /dev/null +++ b/src/wifi.h @@ -0,0 +1,14 @@ +#ifndef WIFI_H +#define WIFI_H + +bool isWiFiConnected(); + +void wifiSetup(); + +void wifiLoop(); + +void timeLoop(); + +bool isTimeSet(); + +#endif