From 4f6b71a61598ed95868bf85e81cb0667db4a5f4a Mon Sep 17 00:00:00 2001 From: Willem Oldemans Date: Sun, 2 May 2021 21:07:20 +0200 Subject: [PATCH] mqtt code added --- .gitignore | 1 + lib/pubsubclient/PubSubClient.cpp | 653 ++++++++++++++++++++++++++++++ lib/pubsubclient/PubSubClient.h | 173 ++++++++ platformio.ini | 7 +- src/led.cpp | 278 +++++++++++++ src/led.h | 87 ++++ src/main.cpp | 194 +-------- src/mqtt.cpp | 168 ++++++++ src/mqtt.h | 7 + src/timer.h | 63 +++ 10 files changed, 1445 insertions(+), 186 deletions(-) create mode 100755 lib/pubsubclient/PubSubClient.cpp create mode 100755 lib/pubsubclient/PubSubClient.h create mode 100644 src/led.cpp create mode 100644 src/led.h create mode 100644 src/mqtt.cpp create mode 100644 src/mqtt.h create mode 100644 src/timer.h diff --git a/.gitignore b/.gitignore index 26a67c9..5e8d119 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ .vscode/c_cpp_properties.json .vscode/launch.json .vscode/ipch +.vscode/settings.json **/.DS_Store diff --git a/lib/pubsubclient/PubSubClient.cpp b/lib/pubsubclient/PubSubClient.cpp new file mode 100755 index 0000000..8c28795 --- /dev/null +++ b/lib/pubsubclient/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>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;istream) { + 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 0) { + digit |= 0x80; + } + buffer[pos++] = digit; + llen++; + } while(len>0); + + pos = writeString(topic,buffer,pos); + + rc += _client->write(buffer,pos); + + for (i=0;iwrite((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 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/lib/pubsubclient/PubSubClient.h b/lib/pubsubclient/PubSubClient.h new file mode 100755 index 0000000..2fd6f1d --- /dev/null +++ b/lib/pubsubclient/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 +#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 128 +#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 +#define MQTT_CALLBACK_SIGNATURE std::function 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 - ) 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/platformio.ini b/platformio.ini index 8cc775b..91cf35c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,4 +12,9 @@ platform = espressif32 board = esp32dev framework = arduino -lib_deps = erropix/ESP32 AnalogWrite@^0.2 +monitor_speed = 115200 + +lib_deps = + + erropix/ESP32 AnalogWrite@^0.2 + Aasim-A/AsyncTimer diff --git a/src/led.cpp b/src/led.cpp new file mode 100644 index 0000000..24cf355 --- /dev/null +++ b/src/led.cpp @@ -0,0 +1,278 @@ +#include "led.h" +#include "analogWrite.h" +#include "timer.h" + +uint64_t lastmillis = 0; + +//_led ledarray[] = { +c_led dimFL(DIM_FL, "dim_FL"); +c_led dimFR(DIM_FR, "dim_FR"); +c_led dimRL(DIM_RL, "dim_RL"); +c_led dimRR(DIM_RR, "dim_RR"); +c_led blinkFL(BLINK_FL, "blink_FL"); +c_led blinkFR(BLINK_FR, "blink_FR"); +c_led interiourLed(INTERIOUR, "interiour"); + +void c_led::begin(void) +{ + Serial.printf("LED %s begin", _ledName.c_str()); + pinMode(_pin, OUTPUT); + digitalWrite(_pin, false); + _ledstate = false; +} + +void c_led::setState(e_ledstate newstate) +{ + if (newstate != noChange) + { + _state = newstate; + _newstate = true; + _timer = 0; + Serial.printf("led %s: setstate %s\n", _ledName.c_str(), ledStateToString(newstate).c_str()); + } + if(newstate == led_blink) + { + _ledstate = false; + } +} + +void c_led::init() +{ + Serial.printf("LED %s: init\n", _ledName.c_str()); + if (DIM_STEP == 0 || DIM_TIME < DIM_STEP) + { + _dimstepms = 1; + _dimstep = 10; + } + else + { + _dimstepms = DIM_TIME / DIM_STEP; + _dimstep = DIM_RES / DIM_STEP; + } + _dimmer = 0; + _timer = 0; + _state = led_off; + //_newstate = true; +} + +void c_led::update(void) +{ + //Serial.printf("led %s update, state = %s, newstate = %s\n", _ledName.c_str(), ledStateToString(_state).c_str(), String(_newstate).c_str()); + switch (_state) + { + case led_off: + { + digitalWrite(_pin, false); + _ledstate = false; + } + break; + case led_dim_on: + { + if (_newstate) + { + _dimmer = 0; + _timer = millis(); + _newstate = false; + } + uint64_t currentmillis = millis(); + if (currentmillis - _timer > _dimstepms) + { + _timer = currentmillis; + analogWrite(_pin, _dimmer, DIM_RES); + _dimmer += _dimstep; + } + if (_dimmer > DIM_RES) + { + setState(led_on); + } + } + break; + case led_on: + { + digitalWrite(_pin, true); + _newstate = false; + _ledstate = true; + } + case led_dim_off: + { + } + break; + case led_blink: + { + uint64_t currentmillis = millis(); + if (currentmillis - _timer > BLINKSPEED) + { + //Serial.printf("%s timer expired\n", _ledName.c_str()); + _ledstate = !_ledstate; + digitalWrite(_pin, _ledstate); + _timer = currentmillis; + } + } + break; + default: + { + } + break; + } +} + +void c_led::toggle(void) +{ + switch (_state) + { + + case led_on: + setState(led_off); + break; + case led_dim_on: + setState(led_off); + break; + case led_off: + setState(led_on); + break; + case led_dim_off: + setState(led_on); + break; + case led_blink: + setState(noChange); + break; + default: + setState(led_off); + } +} + +void initLeds(void) +{ + Serial.println("InitLeds"); + + dimFL.begin(); // + dimFR.begin(); //setState(led_on); + dimRL.begin(); //setState(led_on); + dimRR.begin(); //etState(led_on); + blinkFR.begin(); //setState(led_on); + blinkFL.begin(); //setState(led_on); + interiourLed.begin(); //setState(led_on); + + analogWriteResolution(10); + + dimFL.setState(led_on); + dimFR.setState(led_on); + dimRL.setState(led_on); + dimRR.setState(led_on); + blinkFR.setState(led_blink); + blinkFL.setState(led_blink); + interiourLed.setState(led_on); +} + +void runLeds(void) +{ + + dimFL.update(); + dimFR.update(); + dimRL.update(); + dimRR.update(); + blinkFL.update(); + blinkFR.update(); + interiourLed.update(); +} + +void setLeds(e_ledstate dim_FL, + e_ledstate dim_FR, + e_ledstate dim_RL, + e_ledstate dim_RR, + e_ledstate blink_FR, + e_ledstate blink_FL, + e_ledstate interiour) +{ + dimFL.setState(dim_FL); + dimFR.setState(dim_FR); + dimRL.setState(dim_RL); + dimRR.setState(dim_RR); + blinkFR.setState(blink_FR); + blinkFL.setState(blink_FL); + interiourLed.setState(interiour); +} + +e_ledpattern currentpattern = allOff; + +void setLedPattern(e_ledpattern setpattern) +{ + Serial.printf("SetLedPattern %i\n", setpattern); + switch (setpattern) + { + case allOff: + { + setLeds(led_off, led_off, led_off, led_off, led_off, led_off, led_off); + } + break; + case allOn: + { + setLeds(led_on, led_on, led_on, led_on, led_on, led_on, led_on); + } + break; + case panic: + { + //setLeds(noChange, noChange, noChange, noChange, led_off, led_off, noChange); + setLeds(noChange, noChange, noChange, noChange, led_blink, led_blink, noChange); + } + break; + case night: + { + setLeds(led_on, led_on, led_on, led_on, noChange, noChange, led_on); + } + break; + case turnLeft: + { + setLeds(noChange, noChange, noChange, noChange, led_off, led_blink, noChange); + } + break; + case turnRight: + { + setLeds(noChange, noChange, noChange, noChange, led_blink, led_off, noChange); + } + break; + case interiourOn: + { + setLeds(noChange, noChange, noChange, noChange, noChange, noChange, led_on); + } + break; + case interiourOff: + { + setLeds(noChange, noChange, noChange, noChange, noChange, noChange, led_off); + } + break; + default: + { + } + break; + } + currentpattern = setpattern; +} + +String ledStateToString(e_ledstate state) +{ + switch (state) + { + case led_off: + return "led Off"; + break; + case led_dim_on: + return "led_dim_on"; + break; + case led_on: + return "led On"; + break; + case led_dim_off: + return "led_dim_off"; + break; + case led_blink: + return "led_blink"; + break; + case noChange: + return "noChange"; + break; + default: + return "invalid"; + break; + } +} \ No newline at end of file diff --git a/src/led.h b/src/led.h new file mode 100644 index 0000000..4532997 --- /dev/null +++ b/src/led.h @@ -0,0 +1,87 @@ +#pragma once + +#include "Arduino.h" +#include "timer.h" + +#define DIM_FL 18 // +#define DIM_FR 27 // +#define BLINK_FL 4 +#define BLINK_FR 17 // +#define DIM_RL 14 +#define DIM_RR 26 // +#define INTERIOUR 5 // + +#define NUM_LEDS 7 +#define DIM_TIME 1000 +#define DIM_RES 1024 +#define DIM_STEP 255 +#define DIM_STEPMS (DIM_TIME / DIM_STEP) + +#define BLINKSPEED 400 + +typedef enum +{ + Dim_FL, + Dim_FR, + Dim_RL, + Dim_RR, + Blink_FL, + Blink_FR, + Interiour +} e_leds; + +typedef enum +{ + led_off, + led_dim_on, + led_on, + led_dim_off, + led_blink, + noChange +} e_ledstate; + +typedef enum +{ + allOff, + allOn, + panic, + night, + turnLeft, + turnRight, + interiourOn, + interiourOff +} e_ledpattern; + +String ledStateToString(e_ledstate state); + +class c_led +{ + const uint8_t _pin; + e_ledstate _state; + uint64_t _timer; + uint32_t _dimmer; + bool _newstate; + bool _ledstate; + uint64_t _dimstepms; + uint32_t _dimstep; + String _ledName; + + void init(); + +public: + c_led(uint8_t pin, String name) : _pin(pin) + { + + _ledName = name; + init(); + } + + void begin(void); + void update(void); + void setState(e_ledstate newstate); + void toggle(void); +}; + +void initLeds(void); +void runLeds(void); +void setLedPattern(e_ledpattern setpattern); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 58afa33..eb83a07 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,195 +1,19 @@ #include -#include "analogWrite.h" +#include "led.h" +#include "mqtt.h" -#define DIM_FL 18 // -#define DIM_FR 27 // -#define BLINK_FL 4 -#define BLINK_FR 17 // -#define DIM_RL 14 -#define DIM_RR 26 // -#define INTERIOUR 5 // - -#define BLINKSPEED 400 - -#define NUM_LEDS 7 -#define DIM_TIME 1000 -#define DIM_RES 1024 -#define DIM_STEP 255 -#define DIM_STEPMS (DIM_TIME / DIM_STEP) - -uint64_t lastmillis = 0; - -typedef enum -{ - Dim_FL, - Dim_FR, - Dim_RL, - Dim_RR, - Blink_FL, - Blink_FR, - Interiour -} e_leds; - -typedef enum -{ - led_off, - led_dim_on, - led_on, - led_dim_off, - led_blink -} e_ledstate; - -class c_led -{ - //const e_leds _led; - const uint8_t _pin; - e_ledstate _state; - uint64_t _timer; - uint32_t _dimmer; - bool _newstate; - uint64_t _dimstepms; - uint32_t _dimstep; - -public: - c_led(uint8_t pin) : _pin(pin) - { - if (DIM_STEP == 0 || DIM_TIME < DIM_STEP) - { - _dimstepms = 1; - _dimstep = 10; - } - else - { - _dimstepms = DIM_TIME / DIM_STEP; - _dimstep = DIM_RES / DIM_STEP; - } - _dimmer = 0; - _timer = 0; - _state = led_off; - _newstate = true; - } - - void begin(void) - { - pinMode(_pin, OUTPUT); - digitalWrite(_pin, false); - } - - void update(void) - { - switch (_state) - { - case led_off: - { - digitalWrite(_pin, false); - } - break; - case led_dim_on: - { - if (_newstate) - { - _dimmer = 0; - _timer = millis(); - } - uint64_t currentmillis = millis(); - if (currentmillis - _timer > _dimstepms) - { - _timer = currentmillis; - analogWrite(_pin, _dimmer, DIM_RES); - _dimmer += _dimstep; - } - if (_dimmer > DIM_RES) - { - setState(led_on); - } - _newstate = false; - } - break; - case led_on: - { - digitalWrite(_pin, true); - _newstate = false; - } - } - } - - void setState(e_ledstate newstate) - { - _state = newstate; - _newstate = true; - } - - void toggle(void) - { - switch (_state) - { - - case led_on: - setState(led_off); - break; - case led_dim_on: - setState(led_off); - break; - case led_off: - setState(led_on); - break; - case led_dim_off: - setState(led_on); - break; - default: - setState(led_off); - } - } -}; - -c_led ledarray[] = { - c_led(DIM_FL), - c_led(DIM_FR), - c_led(DIM_RL), - c_led(DIM_RR), - c_led(BLINK_FL), - c_led(BLINK_FR), - c_led(INTERIOUR)}; - -void sceneBlinkLeft(void) -{ -} void setup() { - // put your setup code here, to run once: - for (auto led : ledarray) - { - led.begin(); - } - - analogWriteResolution(10); - - ledarray[Dim_FL].setState(led_on); - ledarray[Dim_FR].setState(led_on); - ledarray[Dim_RL].setState(led_on); - ledarray[Dim_RR].setState(led_on); - ledarray[Interiour].setState(led_on); - + Serial.begin(115200); + initLeds(); + initWiFi(); + initMQTT(); } void loop() { - // put your main code here, to run repeatedly: - for (auto led : ledarray) - { - led.update(); - } - - uint64_t currentmillis = millis(); - if (currentmillis - lastmillis > BLINKSPEED) - { - ledarray[Blink_FR].toggle(); - ledarray[Blink_FL].toggle(); - ledarray[Dim_FL].toggle(); - ledarray[Dim_FR].toggle(); - ledarray[Dim_RL].toggle(); - ledarray[Dim_RR].toggle(); - lastmillis = currentmillis; - } + runWifi(); + runLeds(); + runMQTT(); } \ No newline at end of file diff --git a/src/mqtt.cpp b/src/mqtt.cpp new file mode 100644 index 0000000..2d65355 --- /dev/null +++ b/src/mqtt.cpp @@ -0,0 +1,168 @@ +#include +#include "Arduino.h" +#include "WiFi.h" +#include "PubSubClient.h" +#include "timer.h" +#include "led.h" + +// Update these with values suitable for your network. +byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED}; +IPAddress MQTTserver(192, 168, 2, 5); + +const char *SSID = "poes"; +const char *PWD = "Rijnstraat214"; + +c_timer mqttTimer("MQTT"); +c_timer WifiTimer("WiFi"); + +WiFiClient wificlient; +PubSubClient client(wificlient); + +void callback(char *topic, byte *payload, unsigned int length) +{ + Serial.print("Message arrived ["); + Serial.print(topic); + Serial.print("] "); + String str_payload; + for (int i = 0; i < length; i++) + { + Serial.print((char)payload[i]); + str_payload += (char)payload[i]; + } + Serial.println(""); + + if(!str_payload.compareTo("AllOff")) + { + Serial.println("MQTT payload: AllOff"); + setLedPattern(allOff); + } + else if(str_payload == "AllOn") + { + setLedPattern(allOn); + } + else if(str_payload == "Panic") + { + Serial.println("MQTT payload: panic"); + setLedPattern(panic); + } + else if(str_payload == "TurnLeft") + { + setLedPattern(turnLeft); + } + else if(str_payload == "TurnRight") + { + setLedPattern(turnRight); + } + else if(str_payload == "Night") + { + setLedPattern(night); + } + else if(str_payload == "InteriourOn") + { + setLedPattern(interiourOn); + } + else if(str_payload == "InteriourOff") + { + setLedPattern(interiourOff); + } + else{ + Serial.println("Invalid payload"); + return; + } + client.publish("VW-BUS/ledState",(const char*) payload); + + Serial.println(); +} + + +void reconnect() +{ + // Loop until we're reconnected + if (!client.connected() && mqttTimer.expired()) + { + Serial.print("Attempting MQTT connection..."); + // Attempt to connect + if (client.connect("VW-BUS-connection")) + { + Serial.println("connected"); + // Once connected, publish an announcement... + client.publish("VW-BUS/initState", "online"); + client.publish("VW-BUS/ledState","ledOff"); + // ... and resubscribe + client.subscribe("VW-BUS/ledState/set"); + } + else + { + Serial.print("failed, rc="); + Serial.print(client.state()); + Serial.println(" try again in 5 seconds"); + // Wait 5 seconds before retrying + mqttTimer.setInterval(5000); + } + } +} + +void initMQTT() +{ + client.setServer(MQTTserver, 1883); + client.setCallback(callback); + + // Allow the hardware to sort itself out + delay(1500); +} + +void runMQTT() +{ + if (!client.connected() && mqttTimer.expired()) + { + reconnect(); + } + client.loop(); + mqttTimer.handle(); +} + +uint8_t initcount = 0; + +bool WifiiIsConnected(void) +{ + if (WiFi.status() == WL_CONNECTED) + { + return true; + } + return false; +} + +void initWiFi() +{ + Serial.print("Connectiog to "); + + WiFi.begin(SSID, PWD); + Serial.println(SSID); + while (!WifiiIsConnected() && initcount < 20) + { + Serial.print("."); + delay(200); + initcount++; + } + if (WifiiIsConnected()) + { + + Serial.print("Wifi Connected!."); + Serial.println(WiFi.localIP()); + return; + } + Serial.print("wifi connection failed, retrying.\n"); + + +} + +void runWifi(void) +{ + if(!WifiiIsConnected() && WifiTimer.expired()) + { + initWiFi(); + WifiTimer.setInterval(5000); + return; + } + WifiTimer.handle(); +} \ No newline at end of file diff --git a/src/mqtt.h b/src/mqtt.h new file mode 100644 index 0000000..ebe914f --- /dev/null +++ b/src/mqtt.h @@ -0,0 +1,7 @@ +#pragma once + +void initMQTT(); +void runMQTT(); + +void initWiFi(); +void runWifi(void); diff --git a/src/timer.h b/src/timer.h new file mode 100644 index 0000000..624d9ef --- /dev/null +++ b/src/timer.h @@ -0,0 +1,63 @@ +#pragma once +#include + +class c_timer +{ + AsyncTimer _timer; + bool _timerDone; + uint64_t _timerInterval; + String _timerName; + short _timerID; + +public: + c_timer(String timerName) + { + _timerDone = true; + _timerName = timerName; + } + + // c_timer() + // { + // _timerDone = true; + // _timerName = ""; + // } + + void setTimerName(String timerName) + { + _timerName = timerName; + } + + short setInterval(uint64_t intervalms) + { + if (!_timerDone) + { + Serial.printf("%s(%hu) not ready\n", _timerName.c_str(), _timerID); + return _timerID; + } + _timerDone = false; + _timerInterval = intervalms; + _timerID = _timer.setTimeout([&]() { + _timerDone = true; + Serial.printf("%s(%hu) timer done\n", _timerName.c_str(), _timerID); + }, + _timerInterval); + Serial.printf("%s (%hu) timer started ( %llu ms)\n", _timerName.c_str(), _timerID, intervalms); + return _timerID; + } + + bool expired(void) + { + return _timerDone; + } + + bool handle(void) + { + _timer.handle(); + return _timerDone; + } + + void cancel(short ID) + { + _timer.cancel(ID); + } +};