From 43ff3591d4de400a5b812b7dd34bd0d60f3112ca Mon Sep 17 00:00:00 2001
From: "de@itstall.de" <de@itstall.de>
Date: Mon, 3 Feb 2020 18:04:00 +0100
Subject: [PATCH] Connection to backend established

---
 Wetterstation.HMI               | Bin 3885042 -> 3885042 bytes
 wetterstation/PubSubClient.cpp  | 653 ++++++++++++++++++++++++++++++++
 wetterstation/PubSubClient.h    | 173 +++++++++
 wetterstation/settings.h        |  49 +++
 wetterstation/wetterstation.ino | 457 +++++++---------------
 5 files changed, 1022 insertions(+), 310 deletions(-)
 create mode 100644 wetterstation/PubSubClient.cpp
 create mode 100644 wetterstation/PubSubClient.h
 create mode 100644 wetterstation/settings.h

diff --git a/Wetterstation.HMI b/Wetterstation.HMI
index a60d230890cfb6a381f23448640624b82e6ac854..dfc54bf2550c1b2488f437b699c14cc2118bf681 100644
GIT binary patch
delta 2319
zcmeH|TS!zv7{|X^J?chFr`*|GJ*lgCuU#!m(+Cq%>|&cmM5Q3*Q&bXmqb#DOt?VXC
zPKZIpKA7~7tgvJcY7vB8427Pg3l{VuNQDy7<$u<*%+h1zL)5?z{_~yrzQg&?d~-ft
z9%ml`+tSU7nD4AfHD7x%k0^?W1vcrSVy4Er1lL2pzBq1nwR5T~#ADw;TzuBW=bZfY
zn9=m;AKXu=;K|)`r6Au%A_>p#mItOxYodLAlbXG;Mx)(G50A)%z!{DFIiUn4uq`%$
z6&E-gQCyCZ_bJpB5sA!hu1uw4A58k|6yJro#^}=A`*Tk_%wC6?0TYM<(Lez)L5Df^
z@#H`I;U3j9*E7!(@3Ho9SG(mvdf<}2t;H~Vj#$~w^G^0a4x6UUKZ662Y-qnibOm#L
z6q<=H8hT(h(K$n}Rf*0TdN^Ljcg&aiU82-`lV#0s4x;l><P5#Qn?clS6iUF^m@stQ
zVxmiiZp%k<koA^)Qi5M^@W~wU<|{80H=lB=(sZP$s!`OB@!RaYP&+v{Jz%2#3%3f_
zA#IVU?cg_s`?OtUOmv^pPKfWlB{AatBW@GT*SRWQjqq48bk*)=Hqkx7<74(U97aj8
zx|`>Up-!zr-00$pCF*$pYrPG0_cZZ+5bn8|bT2Jhx>pt>9eW1%!j5gV$oN=<dwZ;M
z&1bk5cKZ}ME1WwkMQUGKNS_S3r)5f8@4&rq@(Q>Ii$mif+zS_4j}s7%zbumLca_Tc
z_$s;nP`Qj#1yPGpv!!Ys(Pd+jxIyaMZlX-%91YdT?d;nsw{ydGxqh@3A0V)`n}heB
z^0J+6qkJh#n$fL&I~BRLUfg0}K1cv|kO-231^HTXnx*SRtyRCy{?@i>o7uLRBiyRZ
zCMIriMO3%gGnp3ZqS@8^Q>)ymM;n@)PUkPZBwD+8rRey<`@Owh4J-i3!Cr5Qz0Oi{
z<$zzm|Nr;dbnpJrK5LuVKKqTc;Ke^3I6xXm2Ma+4$OKs+8{~jokOvll#lQ(%ARiQf
YC15Ei1k1p3Py~v>3Q!`ND@zal1m+zQk^lez

delta 27753
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
NG(l*+0gSZ+{|^;#N5%jE

diff --git a/wetterstation/PubSubClient.cpp b/wetterstation/PubSubClient.cpp
new file mode 100644
index 0000000..0fa420d
--- /dev/null
+++ b/wetterstation/PubSubClient.cpp
@@ -0,0 +1,653 @@
+/*
+  PubSubClient.cpp - A simple client for MQTT.
+  Nick O'Leary
+  http://knolleary.net
+*/
+
+#include "PubSubClient.h"
+#include "Arduino.h"
+
+PubSubClient::PubSubClient() {
+    this->_state = MQTT_DISCONNECTED;
+    this->_client = NULL;
+    this->stream = NULL;
+    setCallback(NULL);
+}
+
+PubSubClient::PubSubClient(Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setClient(client);
+    this->stream = NULL;
+}
+
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr, port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr, port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(IPAddress addr, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(addr,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip, port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip, port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(ip,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setClient(client);
+    setStream(stream);
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setCallback(callback);
+    setClient(client);
+    this->stream = NULL;
+}
+PubSubClient::PubSubClient(const char* domain, uint16_t port, MQTT_CALLBACK_SIGNATURE, Client& client, Stream& stream) {
+    this->_state = MQTT_DISCONNECTED;
+    setServer(domain,port);
+    setCallback(callback);
+    setClient(client);
+    setStream(stream);
+}
+
+boolean PubSubClient::connect(const char *id) {
+    return connect(id,NULL,NULL,0,0,0,0,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass) {
+    return connect(id,user,pass,0,0,0,0,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+    return connect(id,NULL,NULL,willTopic,willQos,willRetain,willMessage,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage) {
+    return connect(id,user,pass,willTopic,willQos,willRetain,willMessage,1);
+}
+
+boolean PubSubClient::connect(const char *id, const char *user, const char *pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession) {
+    if (!connected()) {
+        int result = 0;
+
+        if (domain != NULL) {
+            result = _client->connect(this->domain, this->port);
+        } else {
+            result = _client->connect(this->ip, this->port);
+        }
+        if (result == 1) {
+            nextMsgId = 1;
+            // Leave room in the buffer for header and variable length field
+            uint16_t length = MQTT_MAX_HEADER_SIZE;
+            unsigned int j;
+
+#if MQTT_VERSION == MQTT_VERSION_3_1
+            uint8_t d[9] = {0x00,0x06,'M','Q','I','s','d','p', MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 9
+#elif MQTT_VERSION == MQTT_VERSION_3_1_1
+            uint8_t d[7] = {0x00,0x04,'M','Q','T','T',MQTT_VERSION};
+#define MQTT_HEADER_VERSION_LENGTH 7
+#endif
+            for (j = 0;j<MQTT_HEADER_VERSION_LENGTH;j++) {
+                buffer[length++] = d[j];
+            }
+
+            uint8_t v;
+            if (willTopic) {
+                v = 0x04|(willQos<<3)|(willRetain<<5);
+            } else {
+                v = 0x00;
+            }
+            if (cleanSession) {
+                v = v|0x02;
+            }
+
+            if(user != NULL) {
+                v = v|0x80;
+
+                if(pass != NULL) {
+                    v = v|(0x80>>1);
+                }
+            }
+
+            buffer[length++] = v;
+
+            buffer[length++] = ((MQTT_KEEPALIVE) >> 8);
+            buffer[length++] = ((MQTT_KEEPALIVE) & 0xFF);
+
+            CHECK_STRING_LENGTH(length,id)
+            length = writeString(id,buffer,length);
+            if (willTopic) {
+                CHECK_STRING_LENGTH(length,willTopic)
+                length = writeString(willTopic,buffer,length);
+                CHECK_STRING_LENGTH(length,willMessage)
+                length = writeString(willMessage,buffer,length);
+            }
+
+            if(user != NULL) {
+                CHECK_STRING_LENGTH(length,user)
+                length = writeString(user,buffer,length);
+                if(pass != NULL) {
+                    CHECK_STRING_LENGTH(length,pass)
+                    length = writeString(pass,buffer,length);
+                }
+            }
+
+            write(MQTTCONNECT,buffer,length-MQTT_MAX_HEADER_SIZE);
+
+            lastInActivity = lastOutActivity = millis();
+
+            while (!_client->available()) {
+                unsigned long t = millis();
+                if (t-lastInActivity >= ((int32_t) MQTT_SOCKET_TIMEOUT*1000UL)) {
+                    _state = MQTT_CONNECTION_TIMEOUT;
+                    _client->stop();
+                    return false;
+                }
+            }
+            uint8_t llen;
+            uint16_t len = readPacket(&llen);
+
+            if (len == 4) {
+                if (buffer[3] == 0) {
+                    lastInActivity = millis();
+                    pingOutstanding = false;
+                    _state = MQTT_CONNECTED;
+                    return true;
+                } else {
+                    _state = buffer[3];
+                }
+            }
+            _client->stop();
+        } else {
+            _state = MQTT_CONNECT_FAILED;
+        }
+        return false;
+    }
+    return true;
+}
+
+// reads a byte into result
+boolean PubSubClient::readByte(uint8_t * result) {
+   uint32_t previousMillis = millis();
+   while(!_client->available()) {
+     yield();
+     uint32_t currentMillis = millis();
+     if(currentMillis - previousMillis >= ((int32_t) MQTT_SOCKET_TIMEOUT * 1000)){
+       return false;
+     }
+   }
+   *result = _client->read();
+   return true;
+}
+
+// reads a byte into result[*index] and increments index
+boolean PubSubClient::readByte(uint8_t * result, uint16_t * index){
+  uint16_t current_index = *index;
+  uint8_t * write_address = &(result[current_index]);
+  if(readByte(write_address)){
+    *index = current_index + 1;
+    return true;
+  }
+  return false;
+}
+
+uint16_t PubSubClient::readPacket(uint8_t* lengthLength) {
+    uint16_t len = 0;
+    if(!readByte(buffer, &len)) return 0;
+    bool isPublish = (buffer[0]&0xF0) == MQTTPUBLISH;
+    uint32_t multiplier = 1;
+    uint16_t length = 0;
+    uint8_t digit = 0;
+    uint16_t skip = 0;
+    uint8_t start = 0;
+
+    do {
+        if (len == 5) {
+            // Invalid remaining length encoding - kill the connection
+            _state = MQTT_DISCONNECTED;
+            _client->stop();
+            return 0;
+        }
+        if(!readByte(&digit)) return 0;
+        buffer[len++] = digit;
+        length += (digit & 127) * multiplier;
+        multiplier *= 128;
+    } while ((digit & 128) != 0);
+    *lengthLength = len-1;
+
+    if (isPublish) {
+        // Read in topic length to calculate bytes to skip over for Stream writing
+        if(!readByte(buffer, &len)) return 0;
+        if(!readByte(buffer, &len)) return 0;
+        skip = (buffer[*lengthLength+1]<<8)+buffer[*lengthLength+2];
+        start = 2;
+        if (buffer[0]&MQTTQOS1) {
+            // skip message id
+            skip += 2;
+        }
+    }
+
+    for (uint16_t i = start;i<length;i++) {
+        if(!readByte(&digit)) return 0;
+        if (this->stream) {
+            if (isPublish && len-*lengthLength-2>skip) {
+                this->stream->write(digit);
+            }
+        }
+        if (len < MQTT_MAX_PACKET_SIZE) {
+            buffer[len] = digit;
+        }
+        len++;
+    }
+
+    if (!this->stream && len > MQTT_MAX_PACKET_SIZE) {
+        len = 0; // This will cause the packet to be ignored.
+    }
+
+    return len;
+}
+
+boolean PubSubClient::loop() {
+    if (connected()) {
+        unsigned long t = millis();
+        if ((t - lastInActivity > MQTT_KEEPALIVE*1000UL) || (t - lastOutActivity > MQTT_KEEPALIVE*1000UL)) {
+            if (pingOutstanding) {
+                this->_state = MQTT_CONNECTION_TIMEOUT;
+                _client->stop();
+                return false;
+            } else {
+                buffer[0] = MQTTPINGREQ;
+                buffer[1] = 0;
+                _client->write(buffer,2);
+                lastOutActivity = t;
+                lastInActivity = t;
+                pingOutstanding = true;
+            }
+        }
+        if (_client->available()) {
+            uint8_t llen;
+            uint16_t len = readPacket(&llen);
+            uint16_t msgId = 0;
+            uint8_t *payload;
+            if (len > 0) {
+                lastInActivity = t;
+                uint8_t type = buffer[0]&0xF0;
+                if (type == MQTTPUBLISH) {
+                    if (callback) {
+                        uint16_t tl = (buffer[llen+1]<<8)+buffer[llen+2]; /* topic length in bytes */
+                        memmove(buffer+llen+2,buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */
+                        buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */
+                        char *topic = (char*) buffer+llen+2;
+                        // msgId only present for QOS>0
+                        if ((buffer[0]&0x06) == MQTTQOS1) {
+                            msgId = (buffer[llen+3+tl]<<8)+buffer[llen+3+tl+1];
+                            payload = buffer+llen+3+tl+2;
+                            callback(topic,payload,len-llen-3-tl-2);
+
+                            buffer[0] = MQTTPUBACK;
+                            buffer[1] = 2;
+                            buffer[2] = (msgId >> 8);
+                            buffer[3] = (msgId & 0xFF);
+                            _client->write(buffer,4);
+                            lastOutActivity = t;
+
+                        } else {
+                            payload = buffer+llen+3+tl;
+                            callback(topic,payload,len-llen-3-tl);
+                        }
+                    }
+                } else if (type == MQTTPINGREQ) {
+                    buffer[0] = MQTTPINGRESP;
+                    buffer[1] = 0;
+                    _client->write(buffer,2);
+                } else if (type == MQTTPINGRESP) {
+                    pingOutstanding = false;
+                }
+            } else if (!connected()) {
+                // readPacket has closed the connection
+                return false;
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload) {
+    return publish(topic,(const uint8_t*)payload,strlen(payload),false);
+}
+
+boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) {
+    return publish(topic,(const uint8_t*)payload,strlen(payload),retained);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength) {
+    return publish(topic, payload, plength, false);
+}
+
+boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+    if (connected()) {
+        if (MQTT_MAX_PACKET_SIZE < MQTT_MAX_HEADER_SIZE + 2+strlen(topic) + plength) {
+            // Too long
+            return false;
+        }
+        // Leave room in the buffer for header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        length = writeString(topic,buffer,length);
+        uint16_t i;
+        for (i=0;i<plength;i++) {
+            buffer[length++] = payload[i];
+        }
+        uint8_t header = MQTTPUBLISH;
+        if (retained) {
+            header |= 1;
+        }
+        return write(header,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+boolean PubSubClient::publish_P(const char* topic, const char* payload, boolean retained) {
+    return publish_P(topic, (const uint8_t*)payload, strlen(payload), retained);
+}
+
+boolean PubSubClient::publish_P(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) {
+    uint8_t llen = 0;
+    uint8_t digit;
+    unsigned int rc = 0;
+    uint16_t tlen;
+    unsigned int pos = 0;
+    unsigned int i;
+    uint8_t header;
+    unsigned int len;
+
+    if (!connected()) {
+        return false;
+    }
+
+    tlen = strlen(topic);
+
+    header = MQTTPUBLISH;
+    if (retained) {
+        header |= 1;
+    }
+    buffer[pos++] = header;
+    len = plength + 2 + tlen;
+    do {
+        digit = len % 128;
+        len = len / 128;
+        if (len > 0) {
+            digit |= 0x80;
+        }
+        buffer[pos++] = digit;
+        llen++;
+    } while(len>0);
+
+    pos = writeString(topic,buffer,pos);
+
+    rc += _client->write(buffer,pos);
+
+    for (i=0;i<plength;i++) {
+        rc += _client->write((char)pgm_read_byte_near(payload + i));
+    }
+
+    lastOutActivity = millis();
+
+    return rc == tlen + 4 + plength;
+}
+
+boolean PubSubClient::beginPublish(const char* topic, unsigned int plength, boolean retained) {
+    if (connected()) {
+        // Send the header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        length = writeString(topic,buffer,length);
+        uint16_t i;
+        uint8_t header = MQTTPUBLISH;
+        if (retained) {
+            header |= 1;
+        }
+        size_t hlen = buildHeader(header, buffer, plength+length-MQTT_MAX_HEADER_SIZE);
+        uint16_t rc = _client->write(buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen));
+        lastOutActivity = millis();
+        return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen)));
+    }
+    return false;
+}
+
+int PubSubClient::endPublish() {
+ return 1;
+}
+
+size_t PubSubClient::write(uint8_t data) {
+    lastOutActivity = millis();
+    return _client->write(data);
+}
+
+size_t PubSubClient::write(const uint8_t *buffer, size_t size) {
+    lastOutActivity = millis();
+    return _client->write(buffer,size);
+}
+
+size_t PubSubClient::buildHeader(uint8_t header, uint8_t* buf, uint16_t length) {
+    uint8_t lenBuf[4];
+    uint8_t llen = 0;
+    uint8_t digit;
+    uint8_t pos = 0;
+    uint16_t len = length;
+    do {
+        digit = len % 128;
+        len = len / 128;
+        if (len > 0) {
+            digit |= 0x80;
+        }
+        lenBuf[pos++] = digit;
+        llen++;
+    } while(len>0);
+
+    buf[4-llen] = header;
+    for (int i=0;i<llen;i++) {
+        buf[MQTT_MAX_HEADER_SIZE-llen+i] = lenBuf[i];
+    }
+    return llen+1; // Full header size is variable length bit plus the 1-byte fixed header
+}
+
+boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) {
+    uint16_t rc;
+    uint8_t hlen = buildHeader(header, buf, length);
+
+#ifdef MQTT_MAX_TRANSFER_SIZE
+    uint8_t* writeBuf = buf+(MQTT_MAX_HEADER_SIZE-hlen);
+    uint16_t bytesRemaining = length+hlen;  //Match the length type
+    uint8_t bytesToWrite;
+    boolean result = true;
+    while((bytesRemaining > 0) && result) {
+        bytesToWrite = (bytesRemaining > MQTT_MAX_TRANSFER_SIZE)?MQTT_MAX_TRANSFER_SIZE:bytesRemaining;
+        rc = _client->write(writeBuf,bytesToWrite);
+        result = (rc == bytesToWrite);
+        bytesRemaining -= rc;
+        writeBuf += rc;
+    }
+    return result;
+#else
+    rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen);
+    lastOutActivity = millis();
+    return (rc == hlen+length);
+#endif
+}
+
+boolean PubSubClient::subscribe(const char* topic) {
+    return subscribe(topic, 0);
+}
+
+boolean PubSubClient::subscribe(const char* topic, uint8_t qos) {
+    if (qos > 1) {
+        return false;
+    }
+    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+        // Too long
+        return false;
+    }
+    if (connected()) {
+        // Leave room in the buffer for header and variable length field
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        nextMsgId++;
+        if (nextMsgId == 0) {
+            nextMsgId = 1;
+        }
+        buffer[length++] = (nextMsgId >> 8);
+        buffer[length++] = (nextMsgId & 0xFF);
+        length = writeString((char*)topic, buffer,length);
+        buffer[length++] = qos;
+        return write(MQTTSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+boolean PubSubClient::unsubscribe(const char* topic) {
+    if (MQTT_MAX_PACKET_SIZE < 9 + strlen(topic)) {
+        // Too long
+        return false;
+    }
+    if (connected()) {
+        uint16_t length = MQTT_MAX_HEADER_SIZE;
+        nextMsgId++;
+        if (nextMsgId == 0) {
+            nextMsgId = 1;
+        }
+        buffer[length++] = (nextMsgId >> 8);
+        buffer[length++] = (nextMsgId & 0xFF);
+        length = writeString(topic, buffer,length);
+        return write(MQTTUNSUBSCRIBE|MQTTQOS1,buffer,length-MQTT_MAX_HEADER_SIZE);
+    }
+    return false;
+}
+
+void PubSubClient::disconnect() {
+    buffer[0] = MQTTDISCONNECT;
+    buffer[1] = 0;
+    _client->write(buffer,2);
+    _state = MQTT_DISCONNECTED;
+    _client->flush();
+    _client->stop();
+    lastInActivity = lastOutActivity = millis();
+}
+
+uint16_t PubSubClient::writeString(const char* string, uint8_t* buf, uint16_t pos) {
+    const char* idp = string;
+    uint16_t i = 0;
+    pos += 2;
+    while (*idp) {
+        buf[pos++] = *idp++;
+        i++;
+    }
+    buf[pos-i-2] = (i >> 8);
+    buf[pos-i-1] = (i & 0xFF);
+    return pos;
+}
+
+
+boolean PubSubClient::connected() {
+    boolean rc;
+    if (_client == NULL ) {
+        rc = false;
+    } else {
+        rc = (int)_client->connected();
+        if (!rc) {
+            if (this->_state == MQTT_CONNECTED) {
+                this->_state = MQTT_CONNECTION_LOST;
+                _client->flush();
+                _client->stop();
+            }
+        }
+    }
+    return rc;
+}
+
+PubSubClient& PubSubClient::setServer(uint8_t * ip, uint16_t port) {
+    IPAddress addr(ip[0],ip[1],ip[2],ip[3]);
+    return setServer(addr,port);
+}
+
+PubSubClient& PubSubClient::setServer(IPAddress ip, uint16_t port) {
+    this->ip = ip;
+    this->port = port;
+    this->domain = NULL;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setServer(const char * domain, uint16_t port) {
+    this->domain = domain;
+    this->port = port;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
+    this->callback = callback;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setClient(Client& client){
+    this->_client = &client;
+    return *this;
+}
+
+PubSubClient& PubSubClient::setStream(Stream& stream){
+    this->stream = &stream;
+    return *this;
+}
+
+int PubSubClient::state() {
+    return this->_state;
+}
diff --git a/wetterstation/PubSubClient.h b/wetterstation/PubSubClient.h
new file mode 100644
index 0000000..cb7a36d
--- /dev/null
+++ b/wetterstation/PubSubClient.h
@@ -0,0 +1,173 @@
+/*
+ PubSubClient.h - A simple client for MQTT.
+  Nick O'Leary
+  http://knolleary.net
+*/
+
+#ifndef PubSubClient_h
+#define PubSubClient_h
+
+#include <Arduino.h>
+#include "IPAddress.h"
+#include "Client.h"
+#include "Stream.h"
+
+#define MQTT_VERSION_3_1      3
+#define MQTT_VERSION_3_1_1    4
+
+// MQTT_VERSION : Pick the version
+//#define MQTT_VERSION MQTT_VERSION_3_1
+#ifndef MQTT_VERSION
+#define MQTT_VERSION MQTT_VERSION_3_1_1
+#endif
+
+// MQTT_MAX_PACKET_SIZE : Maximum packet size
+#ifndef MQTT_MAX_PACKET_SIZE
+#define MQTT_MAX_PACKET_SIZE 1024
+#endif
+
+// MQTT_KEEPALIVE : keepAlive interval in Seconds
+#ifndef MQTT_KEEPALIVE
+#define MQTT_KEEPALIVE 15
+#endif
+
+// MQTT_SOCKET_TIMEOUT: socket timeout interval in Seconds
+#ifndef MQTT_SOCKET_TIMEOUT
+#define MQTT_SOCKET_TIMEOUT 15
+#endif
+
+// MQTT_MAX_TRANSFER_SIZE : limit how much data is passed to the network client
+//  in each write call. Needed for the Arduino Wifi Shield. Leave undefined to
+//  pass the entire MQTT packet in each write call.
+//#define MQTT_MAX_TRANSFER_SIZE 80
+
+// Possible values for client.state()
+#define MQTT_CONNECTION_TIMEOUT     -4
+#define MQTT_CONNECTION_LOST        -3
+#define MQTT_CONNECT_FAILED         -2
+#define MQTT_DISCONNECTED           -1
+#define MQTT_CONNECTED               0
+#define MQTT_CONNECT_BAD_PROTOCOL    1
+#define MQTT_CONNECT_BAD_CLIENT_ID   2
+#define MQTT_CONNECT_UNAVAILABLE     3
+#define MQTT_CONNECT_BAD_CREDENTIALS 4
+#define MQTT_CONNECT_UNAUTHORIZED    5
+
+#define MQTTCONNECT     1 << 4  // Client request to connect to Server
+#define MQTTCONNACK     2 << 4  // Connect Acknowledgment
+#define MQTTPUBLISH     3 << 4  // Publish message
+#define MQTTPUBACK      4 << 4  // Publish Acknowledgment
+#define MQTTPUBREC      5 << 4  // Publish Received (assured delivery part 1)
+#define MQTTPUBREL      6 << 4  // Publish Release (assured delivery part 2)
+#define MQTTPUBCOMP     7 << 4  // Publish Complete (assured delivery part 3)
+#define MQTTSUBSCRIBE   8 << 4  // Client Subscribe request
+#define MQTTSUBACK      9 << 4  // Subscribe Acknowledgment
+#define MQTTUNSUBSCRIBE 10 << 4 // Client Unsubscribe request
+#define MQTTUNSUBACK    11 << 4 // Unsubscribe Acknowledgment
+#define MQTTPINGREQ     12 << 4 // PING Request
+#define MQTTPINGRESP    13 << 4 // PING Response
+#define MQTTDISCONNECT  14 << 4 // Client is Disconnecting
+#define MQTTReserved    15 << 4 // Reserved
+
+#define MQTTQOS0        (0 << 1)
+#define MQTTQOS1        (1 << 1)
+#define MQTTQOS2        (2 << 1)
+
+// Maximum size of fixed header and variable length size header
+#define MQTT_MAX_HEADER_SIZE 5
+
+#if defined(ESP8266) || defined(ESP32)
+#include <functional>
+#define MQTT_CALLBACK_SIGNATURE std::function<void(char*, uint8_t*, unsigned int)> callback
+#else
+#define MQTT_CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
+#endif
+
+#define CHECK_STRING_LENGTH(l,s) if (l+2+strlen(s) > MQTT_MAX_PACKET_SIZE) {_client->stop();return false;}
+
+class PubSubClient : public Print {
+private:
+   Client* _client;
+   uint8_t buffer[MQTT_MAX_PACKET_SIZE];
+   uint16_t nextMsgId;
+   unsigned long lastOutActivity;
+   unsigned long lastInActivity;
+   bool pingOutstanding;
+   MQTT_CALLBACK_SIGNATURE;
+   uint16_t readPacket(uint8_t*);
+   boolean readByte(uint8_t * result);
+   boolean readByte(uint8_t * result, uint16_t * index);
+   boolean write(uint8_t header, uint8_t* buf, uint16_t length);
+   uint16_t writeString(const char* string, uint8_t* buf, uint16_t pos);
+   // Build up the header ready to send
+   // Returns the size of the header
+   // Note: the header is built at the end of the first MQTT_MAX_HEADER_SIZE bytes, so will start
+   //       (MQTT_MAX_HEADER_SIZE - <returned size>) bytes into the buffer
+   size_t buildHeader(uint8_t header, uint8_t* buf, uint16_t length);
+   IPAddress ip;
+   const char* domain;
+   uint16_t port;
+   Stream* stream;
+   int _state;
+public:
+   PubSubClient();
+   PubSubClient(Client& client);
+   PubSubClient(IPAddress, uint16_t, Client& client);
+   PubSubClient(IPAddress, uint16_t, Client& client, Stream&);
+   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(IPAddress, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+   PubSubClient(uint8_t *, uint16_t, Client& client);
+   PubSubClient(uint8_t *, uint16_t, Client& client, Stream&);
+   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(uint8_t *, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+   PubSubClient(const char*, uint16_t, Client& client);
+   PubSubClient(const char*, uint16_t, Client& client, Stream&);
+   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client);
+   PubSubClient(const char*, uint16_t, MQTT_CALLBACK_SIGNATURE,Client& client, Stream&);
+
+   PubSubClient& setServer(IPAddress ip, uint16_t port);
+   PubSubClient& setServer(uint8_t * ip, uint16_t port);
+   PubSubClient& setServer(const char * domain, uint16_t port);
+   PubSubClient& setCallback(MQTT_CALLBACK_SIGNATURE);
+   PubSubClient& setClient(Client& client);
+   PubSubClient& setStream(Stream& stream);
+
+   boolean connect(const char* id);
+   boolean connect(const char* id, const char* user, const char* pass);
+   boolean connect(const char* id, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
+   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage);
+   boolean connect(const char* id, const char* user, const char* pass, const char* willTopic, uint8_t willQos, boolean willRetain, const char* willMessage, boolean cleanSession);
+   void disconnect();
+   boolean publish(const char* topic, const char* payload);
+   boolean publish(const char* topic, const char* payload, boolean retained);
+   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength);
+   boolean publish(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
+   boolean publish_P(const char* topic, const char* payload, boolean retained);
+   boolean publish_P(const char* topic, const uint8_t * payload, unsigned int plength, boolean retained);
+   // Start to publish a message.
+   // This API:
+   //   beginPublish(...)
+   //   one or more calls to write(...)
+   //   endPublish()
+   // Allows for arbitrarily large payloads to be sent without them having to be copied into
+   // a new buffer and held in memory at one time
+   // Returns 1 if the message was started successfully, 0 if there was an error
+   boolean beginPublish(const char* topic, unsigned int plength, boolean retained);
+   // Finish off this publish message (started with beginPublish)
+   // Returns 1 if the packet was sent successfully, 0 if there was an error
+   int endPublish();
+   // Write a single byte of payload (only to be used with beginPublish/endPublish)
+   virtual size_t write(uint8_t);
+   // Write size bytes from buffer into the payload (only to be used with beginPublish/endPublish)
+   // Returns the number of bytes written
+   virtual size_t write(const uint8_t *buffer, size_t size);
+   boolean subscribe(const char* topic);
+   boolean subscribe(const char* topic, uint8_t qos);
+   boolean unsubscribe(const char* topic);
+   boolean loop();
+   boolean connected();
+   int state();
+};
+
+
+#endif
diff --git a/wetterstation/settings.h b/wetterstation/settings.h
new file mode 100644
index 0000000..5200d5f
--- /dev/null
+++ b/wetterstation/settings.h
@@ -0,0 +1,49 @@
+//
+// settings for WiFi connection
+//
+char * ssid     = "Sensors";                          // WiFi SSID
+char * password = "mySensorNetz";                     // WiFi password
+
+//
+// MQTT Server
+//
+String frontendId = String(ESP.getChipId());          // Set chipId as frontendId
+const char* mqttHost = "MQTT_HOST";                   // Adresse des MQTT Servers
+const char* mqttUser = "MQTT_USER";                   // MQTT Username
+const char* mqttPass = "MQTT_PASS";                   // MQTT Password
+int mqttPort = 1883;                                  // 1883 - Ist der Standard TCP Port
+
+//
+// MQTT Topics to subscribe
+//
+const char* mqttTopic = String("/wetterstation/frontend/" + frontendId + "/weather").c_str();
+const char* mqttTopicBackend = "/wetterstation/backend/settings";
+
+//
+// Settings for Backend
+//
+String backendPlz = "YOUT_PLZ";
+String backendLngCode = "YOUT_LANDUAGE_CODE";
+String backendCity = "YOUR_CITY";
+
+//
+// settings
+//
+int startupDelay = 1000;                              // startup delay
+int loopDelay = 1000;                                 // main loop delay between sensor updates
+
+int maxForecastLoop = 10;                             // number of main loops before the forecast is refreshed, looping through all cities
+int weatherForecastLoop = 0;
+int weatherForecastLoopInc = 1;
+
+int displayStartupDimValue = 30;                      // startup display backlight level
+int displayDimValue = 150;                            // main display backlight level
+int displayDimStep = 1;                               // dim step
+int dimStartupDelay = 5;                              // delay for fading in
+int dimPageDelay = 0;                                 // delay for fading between pages
+
+const char* ntpServerName = "de.pool.ntp.org";        // server pool
+const long timeZoneoffsetGMT = 3600;                  // offset from Greenwich Meridan Time
+boolean DST = false;                                  // daylight saving time
+int timeServerDelay = 1000;                           // delay for the time server to reply
+int timeServerRefreshDelay = 3600;
diff --git a/wetterstation/wetterstation.ino b/wetterstation/wetterstation.ino
index 85d42c8..a3e3ed1 100644
--- a/wetterstation/wetterstation.ino
+++ b/wetterstation/wetterstation.ino
@@ -15,7 +15,6 @@
 
   contact: dev@itstall.de
 **********************************************************************/
-
 //#define DEBUG
 #define DEBUG_NO_PAGE_FADE
 #define DEBUG_NO_TOUCH
@@ -27,100 +26,39 @@
 #include <Wire.h>
 #include <SPI.h>                                // I2C library
 #include <ArduinoJson.h>                        // https://github.com/bblanchon/ArduinoJson
+#include <NTPClient.h>
 #include <ESP8266WiFi.h>                        // WiFi library
 #include <WiFiUdp.h>                            // Udp library
 #include <TimeLib.h>                            // Time library
+#include "PubSubClient.h"                       // http://knolleary.net/arduino-client-for-mqtt/
+#include "settings.h"
 
 extern "C" {
   #include "user_interface.h"
 }
 
-//
-// settings for WiFi connection
-//
-char * ssid     = "";                    // WiFi SSID
-char * password = "";               // WiFi password
-
 unsigned int localPort = 2390;                  // local port to listen for UDP packets
 IPAddress timeServerIP;                         // IP address of random server
-const char* ntpServerName = "de.pool.ntp.org";  // server pool
+IPAddress mqttHostIP;                           // IP address of random server
 byte packetBuffer[48];                          // buffer to hold incoming and outgoing packets
-int timeZoneoffsetGMT = 3600;                   // offset from Greenwich Meridan Time
-boolean DST = false;                            // daylight saving time
-WiFiUDP clockUDP;                               // initialize a UDP instance
-
-//
-// settings for the openweathermap connection
-// sign up, get your api key and insert it below
-//
-char * servername = "api.openweathermap.org";         // remote server with weather info
-String APIKEY = "45f6ce019087f08b45d73872319c0573";   // personal api key for retrieving the weather data
-
-const int httpPort = 80;
-String result;
-int cityIDLoop = 0;
-
-// a list of cities you want to display the forecast for
-// get the ID at https://openweathermap.org/
-// type the city, click search and click on the town
-// then check the link, like this: https://openweathermap.org/city/5128581
-// 5128581 is the ID for New York
-String cityIDs[] = {
-  "2836455",  // Schonungen
-  ""          // end the list with an empty string
-};
-
-//
-// settings
-//
-int startupDelay = 1000;                      // startup delay
-int loopDelay = 3000;                         // main loop delay between sensor updates
-
-int timeServerDelay = 1000;                   // delay for the time server to reply
-int timeServerPasses = 4;                     // number of tries to connect to the time server before timing out
-int timeServerResyncNumOfLoops = 3000;        // number of loops before refreshing the time. one loop takes approx. 28 seconds
-int timeServerResyncNumOfLoopsCounter = 0;
-boolean timeServerConnected = false;          // is set to true when the time is read from the server
-
-int maxForecastLoop = 10;                     // number of main loops before the forecast is refreshed, looping through all cities
-int weatherForecastLoop = 0;
-int weatherForecastLoopInc = 1;
-
-int displayStartupDimValue = 30;              // startup display backlight level
-int displayDimValue = 150;                    // main display backlight level
-int displayDimStep = 1;                       // dim step
-int dimStartupDelay = 50;                     // delay for fading in
-int dimPageDelay = 0;                         // delay for fading between pages
 
 //
 // initialize variables
 //
 int page = 0;
-
-float bmeAltitude = 0;
-float bmeHumidity = 0;
-float bmePressure = 0;
-float bmeTemperature = 0;
-
 String command;
 String doubleQuote = "\"\"";
 
-//
-// initialize timer
-//
-os_timer_t secTimer;
-
-void timerDisplayTime(void *pArg) {
-  displayTime();
-}
+WiFiClient wifiClient;
+WiFiUDP ntpUDP;
+NTPClient timeClient(ntpUDP, ntpServerName, timeZoneoffsetGMT, timeServerRefreshDelay);
+PubSubClient mqttClient(wifiClient);
 
-//
-// setup
-//
 void setup() {
-#ifdef DEBUG
+//#ifdef DEBUG
   Serial.begin(9600);
-#endif
+  Serial.println("setup...");
+//#endif
 
   nexSerial.begin(9600);
 
@@ -130,53 +68,28 @@ void setup() {
 
   delay(startupDelay);
 
-  displayFadeIn(0, displayStartupDimValue, dimStartupDelay);
+  displayFadeIn(1, displayStartupDimValue, dimStartupDelay);
 
-  connectToWifi();
-  clockUDP.begin(localPort);
+  reconnectToWifi();
+  timeClient.begin();
   getTimeFromServer();
 
-  if (timeServerConnected) {
-    displayTime();
-  }
-
-  displayDate();
-
-  os_timer_setfn(&secTimer, timerDisplayTime, NULL);
-  if (timeServerConnected) {
-    os_timer_arm(&secTimer, 1000, true);
-  }
-
   displayFadeIn(displayStartupDimValue, displayDimValue, dimStartupDelay / 2);
 
-#ifdef DEBUG
-  Serial.println("Starting main loop");
-#endif
+  mqttClient.setServer(mqttHost, mqttPort);
+  mqttClient.setCallback(getWeatherDataMqtt);
+  reconnectMqtt();
 }
-
-//
-// main loop
-//
 void loop() {
-  if (!timeServerConnected) {
-    getTimeFromServer();
-    if (timeServerConnected)
-      os_timer_arm(&secTimer, 1000, true);
-  }
+#ifdef DEBUG
+  Serial.println("loop");
+#endif
 
   if (page == 0) {
 
     if (weatherForecastLoop == maxForecastLoop) {
-      timeServerResyncNumOfLoopsCounter += 1;
-      if (timeServerResyncNumOfLoopsCounter == timeServerResyncNumOfLoops) {
-        getTimeFromServer();
-        timeServerResyncNumOfLoopsCounter = 0;
-      }
-
       page = 1;
-      getWeatherData();
       weatherForecastLoopInc = -weatherForecastLoopInc;
-      displayDate();
     }
 
     delayCheckTouch(loopDelay);
@@ -197,9 +110,6 @@ void loop() {
 #endif
 
       weatherForecastLoopInc = -weatherForecastLoopInc;
-
-      displayTime();
-      displayDate();
     }
     else
       delayCheckTouch(loopDelay);
@@ -207,45 +117,30 @@ void loop() {
 
   weatherForecastLoop += weatherForecastLoopInc;
 
-#ifdef DEBUG
-  Serial.print(page);
-  Serial.print(" ");
-  Serial.print(weatherForecastLoop);
-  Serial.print(" ");
-  Serial.print(timeServerResyncNumOfLoopsCounter);
-  Serial.print(" ");
-  Serial.print(timeServerResyncNumOfLoops);
-  Serial.println("");
-#endif
-}
-
-void displayDate() {
-  time_t t = now();
+  // Update date and time on display
+  sendToLCD(1, "time", String(timeClient.getFormattedTime()));
+  sendToLCD(1, "date", dayAsString(timeClient.getDay()));
 
-  command = "date.txt=\"" + String(day(t)) + " " + monthAsString(month(t)) + "\"";
-  printNextionCommand(command);
-  command = "year.txt=\"" + String(year(t)) + "\"";
-  printNextionCommand(command);
-}
+  reconnectToWifi();
 
-void displayTime() {
-  time_t t = now();
-  char timeString[9];
+  reconnectMqtt();
+  mqttClient.loop();
 
-  snprintf(timeString, sizeof(timeString), "%02d:%02d:%02d", hour(t), minute(t), second(t));
-  command = "time.txt=\"" + String(timeString) + "\"";
-  printNextionCommand(command);
+#ifdef DEBUG
+    Serial.print(page);
+    Serial.print(" ");
+    Serial.print(weatherForecastLoop);
+    Serial.println("");
+#endif
 }
-
-//
-// Nextion commands
-//
 void printNextionCommand (String command) {
   nexSerial.print(command);
   endNextionCommand();
 }
-
 void sendToLCD(uint8_t type, String index, String cmd) {
+#ifdef DEBUG
+  Serial.println("sendToLCD");
+#endif
   if (type == 1 ) {
     nexSerial.print(index);
     nexSerial.print(".txt=");
@@ -270,14 +165,15 @@ void sendToLCD(uint8_t type, String index, String cmd) {
 
   endNextionCommand();
 }
-
 void endNextionCommand() {
   nexSerial.write(0xff);
   nexSerial.write(0xff);
   nexSerial.write(0xff);
 }
-
 void displayFadeIn(int fromValue, int toValue, int fadeDelay) {
+#ifdef DEBUG
+  Serial.println("displayFadeIn");
+#endif
   for (int i = fromValue; i <= toValue; i += displayDimStep) {
     if (i > toValue) {
       i = toValue;
@@ -286,8 +182,10 @@ void displayFadeIn(int fromValue, int toValue, int fadeDelay) {
     delay(fadeDelay);
   }
 }
-
 void displayFadeOut(int fromValue, int fadeDelay) {
+#ifdef DEBUG
+  Serial.println("displayFadeOut");
+#endif
   for (int i = fromValue; i >= 0; i -= displayDimStep) {
     if (i < 0) {
       i = 0;
@@ -296,174 +194,101 @@ void displayFadeOut(int fromValue, int fadeDelay) {
     delay(fadeDelay);
   }
 }
-
-//
-// network functions
-//
-void connectToWifi() {
-  int wifiBlink = 0;
-
-  //  WiFi.enableSTA(true);
-  WiFi.mode(WIFI_STA);
-  WiFi.disconnect();
-
-  WiFi.begin(ssid, password);
-
-  while (WiFi.status() != WL_CONNECTED) {
-    if (wifiBlink == 0) {
-      printNextionCommand("wifi_connect.txt=\"connect...\"");
-      wifiBlink = 1;
-    }
-    else {
-      printNextionCommand("wifi_connect.txt=" + doubleQuote);
-      wifiBlink = 0;
+void reconnectToWifi() {
+#ifdef DEBUG
+  Serial.println("reconnectToWifi...");
+#endif
+  if(WiFi.status() != WL_CONNECTED) {
+    int wifiBlink = 0;
+  
+    //  WiFi.enableSTA(true);
+    WiFi.mode(WIFI_STA);
+    WiFi.disconnect();
+    WiFi.begin(ssid, password);
+  
+    while (WiFi.status() != WL_CONNECTED) {
+      if (wifiBlink == 0) {
+        printNextionCommand("wifi_connect.txt=\"connect...\"");
+        sendToLCD(1, "txtIp", "\"No IP yet...\"");
+        wifiBlink = 1;
+      }
+      else {
+        printNextionCommand("wifi_connect.txt=" + doubleQuote);
+        wifiBlink = 0;
+      }
+      delay(500);
     }
-    delay(500);
   }
+#ifdef DEBUG
+    Serial.print("Wifi IP Address: ");
+    Serial.println(WiFi.localIP().toString());
+#endif
+  sendToLCD(1, "txtIp", WiFi.localIP().toString());
 
   printNextionCommand("wifi_connect.txt=" + doubleQuote);
 }
-
 void getTimeFromServer() {
 #ifdef DEBUG
-  Serial.print("Getting time from server...");
-#endif
-
-  int connectStatus = 0, i = 0;
-  unsigned long unixTime;
-
-  while (i < timeServerPasses && !connectStatus) {
-#ifdef DEBUG
-    Serial.print(i);
-    Serial.print("...");
+  Serial.println("getTimeFromServer");
 #endif
 
-    printNextionCommand("time.txt=\"get time..\"");
-    WiFi.hostByName(ntpServerName, timeServerIP);
-    sendNTPpacket(timeServerIP);
-    delay(timeServerDelay / 2);
-    connectStatus = clockUDP.parsePacket();
-    printNextionCommand("time.txt=" + doubleQuote);
-    delay(timeServerDelay / 2);
-    i++;
+  sendToLCD(1, "time", "get time..");
+  while(!timeClient.update()) {
+    timeClient.forceUpdate();
   }
-
-  if (connectStatus) {
+  timeClient.setTimeOffset(timeZoneoffsetGMT);
+  timeClient.setUpdateInterval(timeServerRefreshDelay);
+}
+void reconnectMqtt() {
+  String settings;
+  StaticJsonDocument<1024> doc;
+  JsonObject object = doc.to<JsonObject>();
+  object["plz"] = backendPlz;
+  object["lngCode"] = backendLngCode;
+  object["frontendId"] = frontendId;
+  serializeJson(doc, settings);
+
+  while (!mqttClient.connected()) {
+    if (mqttClient.connect(frontendId.c_str(), mqttUser, mqttPass)) {
+      Serial.println("MQTT Connected");
+      //mqttClient.subscribe(mqttTopic);
+      mqttClient.subscribe("/wetterstation/frontend/7363421/weather");
+      Serial.print("MQTT Subscribe to: ");
+      Serial.println(mqttTopic);
+      mqttClient.publish(mqttTopicBackend, settings.c_str());
+    }
+    else {
 #ifdef DEBUG
-    Serial.print(i);
-    Serial.println("...connected");
+      Serial.print("failed, rc=");
+      Serial.print(mqttClient.state());
+      Serial.println(" retrying in 5 seconds");
 #endif
-
-    timeServerConnected = true;
-    clockUDP.read(packetBuffer, 48);
-
-    // the timestamp starts at byte 40 of the received packet and is four bytes, or two words, long.
-    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
-    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
-    // the timestamp is in seconds from 1900, add 70 years to get Unixtime
-    unixTime = (highWord << 16 | lowWord) - 2208988800 + timeZoneoffsetGMT;
-
-    if (DST) {
-      unixTime = unixTime + 3600;
+      delay(5000);
     }
-
-    setTime(unixTime);
   }
-  else {
+}
+void getWeatherDataMqtt(char* topic, byte* payload, unsigned int length) {
 #ifdef DEBUG
-    Serial.print(i);
-    Serial.println("...failed...");
+  Serial.println("getWeatherDataMqtt...");
+  Serial.println("From mqttTopic: " + String(mqttTopic));
+  Serial.println("From topic: " + String(topic));
 #endif
-
-    printNextionCommand("time.txt=\"failed....\"");
-    delay(timeServerDelay);
-    printNextionCommand("time.txt=" + doubleQuote);
-  }
-}
-
-unsigned long sendNTPpacket(IPAddress& address) {
-  memset(packetBuffer, 0, 48);
-  packetBuffer[0] = 0b11100011;     // LI, Version, Mode
-  packetBuffer[1] = 0;              // Stratum, or type of clock
-  packetBuffer[2] = 6;              // Polling Interval
-  packetBuffer[3] = 0xEC;           // Peer Clock Precision
-  // 8 bytes of zero for Root Delay & Root Dispersion
-  packetBuffer[12]  = 49;
-  packetBuffer[13]  = 0x4E;
-  packetBuffer[14]  = 49;
-  packetBuffer[15]  = 52;
-
-  clockUDP.beginPacket(address, 123);    //NTP requests are to port 123
-  clockUDP.write(packetBuffer, 48);
-  clockUDP.endPacket();
-}
-
-//
-// get and display weather data
-//
-void getWeatherData() { //client function to send/receive GET request data.
-  WiFiClient client;
-  if (!client.connect(servername, httpPort)) {
-    return;
-  }
-
-  String cityID = cityIDs[cityIDLoop];
-  cityIDLoop++;
-
-  if (cityIDs[cityIDLoop] == "") {
-    cityIDLoop = 0;
-  }
-
-  String url = "/data/2.5/forecast?id=" + cityID + "&units=metric&cnt=1&APPID=" + APIKEY + "&lang=de";
-  //String url = "/data/2.5/weather?id=" + cityID + "&units=metric&cnt=1&APPID=" + APIKEY;
-  //check weather properties at https://openweathermap.org/current
-
-  // This will send the request to the server
-  client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + servername + "\r\n" + "Connection: close\r\n\r\n");
-  unsigned long timeout = millis();
-  while (client.available() == 0) {
-    if (millis() - timeout > 5000) {
-      client.stop();
-      return;
-    }
-  }
-
-  result = "";
-  // Read all the lines of the reply from server
-  while (client.available()) {
-    result = client.readStringUntil('\r');
-  }
-
-  result.replace('[', ' ');
-  result.replace(']', ' ');
-
-  char jsonArray [result.length() + 1];
-  result.toCharArray(jsonArray, sizeof(jsonArray));
-  jsonArray[result.length() + 1] = '\0';
-
-  StaticJsonBuffer<1024> json_buf;
-  JsonObject& root = json_buf.parseObject(jsonArray);
-  if (!root.success()) {
-    nexSerial.println("parseObject() failed");
-  }
-
-  //check properties forecasts at https://openweathermap.org/forecast5
-
-  int weatherID = root["list"]["weather"]["id"];
-
-  String tmp0 = root["city"]["name"];
-  String tmp1 = root["list"]["weather"]["main"];
-  String tmp2 = root["list"]["weather"]["description"];
-  float  tmp3 = root["list"]["main"]["temp_min"];
-  float  tmp4 = root["list"]["main"]["temp_max"];
-  float  tmp5 = root["list"]["main"]["humidity"];
-  float  tmp6 = root["list"]["clouds"]["all"];
-  float  tmp7 = root["list"]["rain"]["3h"];
-  float  tmp8 = root["list"]["snow"]["3h"];
-  float  tmp9 = root["list"]["wind"]["speed"];
-  int    tmp10 = root["list"]["wind"]["deg"];
-  float  tmp11 = root["list"]["main"]["pressure"];
-  timeZoneoffsetGMT = root["city"]["timezone"];
+  // Parsing
+  StaticJsonDocument<1024> root;
+  deserializeJson(root, payload, length);
+
+  String tmp0 = backendCity;
+  //String tmp1 = root["main"];          // Icon main
+  //String tmp2 = root["description"];   // Icon description
+  float  tmp3 = root["tempMin"];
+  float  tmp4 = root["tempMax"];
+  float  tmp5 = root["humidity"];
+  float  tmp6 = root["clouds"];
+  float  tmp7 = root["rain3h"];
+  float  tmp8 = root["snow3h"];
+  float  tmp9 = root["windSpeed"];
+  int    tmp10 = root["windDeg"];
+  float  tmp11 = root["pressure"];
   //String tmp12 = root["list"]["dt_text"]; command = command + tmp12;
 
 #if !defined (DEBUG_NO_PAGE_FADE)
@@ -476,9 +301,9 @@ void getWeatherData() { //client function to send/receive GET request data.
   displayFadeIn(0, displayDimValue, dimPageDelay );
 #endif
 
-  setWeatherPicture(weatherID);
+  //setWeatherPicture(weatherID);
   sendToLCD(1, "city", tmp0);
-  sendToLCD(1, "description", tmp2);
+  //sendToLCD(1, "description", tmp2);
   sendToLCD(1, "humidity", String(tmp5, 0));
   sendToLCD(1, "rain", String(tmp7, 1));
   sendToLCD(1, "wind_dir", getShortWindDirection(tmp10));
@@ -488,8 +313,10 @@ void getWeatherData() { //client function to send/receive GET request data.
   sendToLCD(1, "temp_min", String(tmp3, 1));
   sendToLCD(1, "temp_max", String(tmp4, 1));
 }
-
 String getWindDirection (int degrees) {
+#ifdef DEBUG
+  Serial.println("getWindDirection...");
+#endif
   int sector = ((degrees + 11) / 22.5 - 1);
   switch (sector) {
     case 0: return "Nord";
@@ -510,8 +337,10 @@ String getWindDirection (int degrees) {
     case 15: return "Nord-Nordwest";
   }
 }
-
 String getShortWindDirection (int degrees) {
+#ifdef DEBUG
+  Serial.println("getShortWindDirection...");
+#endif
   int sector = ((degrees + 11) / 22.5 - 1);
   switch (sector) {
     case 0: return "N";
@@ -532,8 +361,10 @@ String getShortWindDirection (int degrees) {
     case 15: return "NNW";
   }
 }
-
 void setWeatherPicture(int weatherID) {
+#ifdef DEBUG
+  Serial.println("setWeatherPicture...");
+#endif
   switch (weatherID) {
     case 200:
     case 201:
@@ -598,29 +429,35 @@ void setWeatherPicture(int weatherID) {
     default: sendToLCD(3, "weatherpic", "10"); break; // dunno
   }
 }
-
 void delayCheckTouch (int delayTime) {
+#ifdef DEBUG
+  Serial.println("delayCheckTouch...");
+#endif
   unsigned long startMillis = millis();
 
   while (millis() - startMillis < delayTime) {
     delay(1000);
   }
 }
-
 String dayAsString(int day) {
+#ifdef DEBUG
+  Serial.println("dayAsString...");
+#endif
   switch (day) {
-    case 1: return "Sonntag";
-    case 2: return "Montag";
-    case 3: return "Dienstag";
-    case 4: return "Mittwoch";
-    case 5: return "Donnerstag";
-    case 6: return "Freitag";
-    case 7: return "Samstag";
+    case 1: return "Montag";
+    case 2: return "Dienstag";
+    case 3: return "Mittwoch";
+    case 4: return "Donnerstag";
+    case 5: return "Freitag";
+    case 6: return "Samstag";
+    case 7: return "Sonntag";
   }
   return "" ;
 }
-
 String monthAsString(int month) {
+#ifdef DEBUG
+  Serial.println("monthAsString...");
+#endif
   switch (month) {
     case 1:  return "Januar";
     case 2:  return "Februar";
-- 
GitLab