diff --git a/Wetterstation.HMI b/Wetterstation.HMI index a60d230890cfb6a381f23448640624b82e6ac854..dfc54bf2550c1b2488f437b699c14cc2118bf681 100644 Binary files a/Wetterstation.HMI and b/Wetterstation.HMI differ diff --git a/wetterstation/PubSubClient.cpp b/wetterstation/PubSubClient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0fa420d48f088506dd25f928733074c49d7fccca --- /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 0000000000000000000000000000000000000000..cb7a36d6df6dac38e087d574c5c5c313078fd74b --- /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 0000000000000000000000000000000000000000..5200d5f707e0a02bc824dd831654d1a4a75a482d --- /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 85d42c83b2f34ac016ff8f839a3fa3daf0c9572e..a3e3ed18498a4d7170d78c89852494fb0ed52b19 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";