diff --git a/ampel-firmware/ampel-firmware.h b/ampel-firmware/ampel-firmware.h index 41fe46bd593aa82a1a3c133e08f1fabe88e7b490..abe783aea97c4aa368cd0eb940dda401b131375a 100644 --- a/ampel-firmware/ampel-firmware.h +++ b/ampel-firmware/ampel-firmware.h @@ -33,6 +33,7 @@ #endif #include "util.h" +#include "ntp.h" #include "sensor_console.h" #include "co2_sensor.h" #include "led_effects.h" diff --git a/ampel-firmware/ampel-firmware.ino b/ampel-firmware/ampel-firmware.ino index 40efd11ee447d7de79a494e510ab52e756fbf7c8..c055bcb8aa8808b4b406aceea195c4b29ad31b35 100644 --- a/ampel-firmware/ampel-firmware.ino +++ b/ampel-firmware/ampel-firmware.ino @@ -88,10 +88,7 @@ void setup() { #ifdef AMPEL_WIFI wifi::connect(ampel.sensorId); - Serial.print(F("WiFi - Status: ")); - Serial.println(WiFi.status()); - - if (WiFi.status() == WL_CONNECTED) { + if (wifi::connected()) { # ifdef AMPEL_HTTP web_server::initialize(); # endif @@ -203,7 +200,7 @@ void checkFlashButton() { void keepServicesAlive() { #ifdef AMPEL_WIFI - if (WiFi.status() == WL_CONNECTED) { + if (wifi::connected()) { # if defined(ESP8266) //NOTE: Sadly, there seems to be a bug in the current MDNS implementation. // It stops working after 2 minutes. And forcing a restart leads to a memory leak. diff --git a/ampel-firmware/co2_sensor.cpp b/ampel-firmware/co2_sensor.cpp index 25a7102e19088ae65487234f248fada2ed25fd29..fae9895334099e885b8820b26eeb142e531364dc 100644 --- a/ampel-firmware/co2_sensor.cpp +++ b/ampel-firmware/co2_sensor.cpp @@ -1,5 +1,11 @@ #include "co2_sensor.h" +#include "config.h" +#include "ntp.h" +#include "led_effects.h" +#include "sensor_console.h" +#include + namespace config { // UPPERCASE values should be defined in config.h uint16_t measurement_timestep = MEASUREMENT_TIMESTEP; // [s] Value between 2 and 1800 (range for SCD30 sensor). diff --git a/ampel-firmware/co2_sensor.h b/ampel-firmware/co2_sensor.h index d9eff8724f018e885f260060aaed58678c814092..a6a9dd25b554028396577bb2d4c105959e8f49b0 100644 --- a/ampel-firmware/co2_sensor.h +++ b/ampel-firmware/co2_sensor.h @@ -4,11 +4,6 @@ // The SCD30 from Sensirion is a high quality Nondispersive Infrared (NDIR) based CO₂ sensor capable of detecting 400 to 10000ppm with an accuracy of ±(30ppm+3%). // https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library #include "src/lib/SparkFun_SCD30_Arduino_Library/src/SparkFun_SCD30_Arduino_Library.h" // From: http://librarymanager/All#SparkFun_SCD30 -#include "config.h" -#include "led_effects.h" -#include "util.h" -#include "sensor_console.h" -#include namespace config { extern uint16_t measurement_timestep; // [s] Value between 2 and 1800 (range for SCD30 sensor) diff --git a/ampel-firmware/config.public.h b/ampel-firmware/config.public.h index 2c9047e2e047bf4c40330d5684569c197de11da7..cf0325f388367658469cede1eb3b29874eca83bd 100644 --- a/ampel-firmware/config.public.h +++ b/ampel-firmware/config.public.h @@ -119,10 +119,7 @@ */ // 1) Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE". -// 2) Region and transceiver type should be specified in: -// * Arduino/libraries/MCCI_LoRaWAN_LMIC_library/project_config/lmic_project_config.h for Arduino IDE -// * platformio.ini for PlatformIO -// See https://github.com/mcci-catena/arduino-lmic#configuration for more information +// 2) If you need to, region and transceiver type can be specified in lorawan.cpp. Default is "Europe 868" // 3) It has been tested with "TTGO ESP32 SX1276 LoRa 868" and will only work with an ESP32 + LoRa modem // 4) In order to use LoRaWAN, a gateway should be close to the co2ampel, and an account, an application and a device should be registered, // e.g. on https://www.thethingsnetwork.org/docs/applications/ diff --git a/ampel-firmware/csv_writer.cpp b/ampel-firmware/csv_writer.cpp index d0828a40999dc7326d1ce19f9a4808be10dd6852..35972cde0447c34b6ff32a14a4627b684730b3b5 100644 --- a/ampel-firmware/csv_writer.cpp +++ b/ampel-firmware/csv_writer.cpp @@ -1,5 +1,10 @@ #include "csv_writer.h" +#include "config.h" +#include "ntp.h" +#include "led_effects.h" +#include "sensor_console.h" + namespace config { // Values should be defined in config.h uint16_t csv_interval = CSV_INTERVAL; // [s] diff --git a/ampel-firmware/csv_writer.h b/ampel-firmware/csv_writer.h index f85f34641aaf4f1915e773ad7c1675f846ecfa90..dcee7130c27a23e66d20fb8b390889b0fd53468e 100644 --- a/ampel-firmware/csv_writer.h +++ b/ampel-firmware/csv_writer.h @@ -11,11 +11,6 @@ # error Board should be either ESP8266 or ESP832 #endif -#include "config.h" -#include "util.h" -#include "led_effects.h" -#include "sensor_console.h" - namespace config { extern uint16_t csv_interval; // [s] } diff --git a/ampel-firmware/led_effects.cpp b/ampel-firmware/led_effects.cpp index 6d7e185f29939730f6be892708904eba69b66123..4d0d59958aa06b05c54e7b86b406a4e03ea73750 100644 --- a/ampel-firmware/led_effects.cpp +++ b/ampel-firmware/led_effects.cpp @@ -1,4 +1,13 @@ #include "led_effects.h" + +#include "config.h" +#include "sensor_console.h" + +// Adafruit NeoPixel (Arduino library for controlling single-wire-based LED pixels and strip) +// https://github.com/adafruit/Adafruit_NeoPixel +// Documentation : http://adafruit.github.io/Adafruit_NeoPixel/html/class_adafruit___neo_pixel.html +#include "src/lib/Adafruit_NeoPixel/Adafruit_NeoPixel.h" + /***************************************************************** * Configuration * *****************************************************************/ diff --git a/ampel-firmware/led_effects.h b/ampel-firmware/led_effects.h index b6447426ab847c726ce60e69db78ef17425c4285..9f681a66c28fe80ce3880123f985acf50ffc8526 100644 --- a/ampel-firmware/led_effects.h +++ b/ampel-firmware/led_effects.h @@ -1,13 +1,7 @@ #ifndef LED_EFFECTS_H_INCLUDED #define LED_EFFECTS_H_INCLUDED -#include -#include "config.h" -#include "sensor_console.h" -// Adafruit NeoPixel (Arduino library for controlling single-wire-based LED pixels and strip) -// https://github.com/adafruit/Adafruit_NeoPixel -// Documentation : http://adafruit.github.io/Adafruit_NeoPixel/html/class_adafruit___neo_pixel.html -#include "src/lib/Adafruit_NeoPixel/Adafruit_NeoPixel.h" +#include // For uint32_t namespace color { const uint32_t red = 0xFF0000; diff --git a/ampel-firmware/lorawan.cpp b/ampel-firmware/lorawan.cpp index 746893b3698137ff14417158e5a2728fd8ad4a25..5dfad5e5c1fadb1df3018bc142e438ef7a55a9a9 100644 --- a/ampel-firmware/lorawan.cpp +++ b/ampel-firmware/lorawan.cpp @@ -1,7 +1,42 @@ #include "lorawan.h" + #if defined(AMPEL_LORAWAN) && defined(ESP32) +#include "led_effects.h" +#include "sensor_console.h" +#include "util.h" +#include "ntp.h" + +/*** Define region and transceiver type, and ignore lmic_project_config.h from lmic library ***/ +// Those values are probably okay if you're in Europe. +#define ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS +#define CFG_eu868 1 +#define CFG_sx1276_radio 1 +/****************************************************************************************/ + +// Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE" +// Tested successfully with v3.2.0 and connected to a thethingsnetwork.org app. +#include +#include +#include +#include + namespace config { +#if defined(CFG_eu868) + const char *lorawan_frequency_plan = "Europe 868"; +#elif defined(CFG_us915) + const char *lorawan_frequency_plan = "US 915"; +#elif defined(CFG_au915) + const char *lorawan_frequency_plan = "Australia 915"; +#elif defined(CFG_as923) + const char *lorawan_frequency_plan = "Asia 923"; +#elif defined(CFG_kr920) + const char *lorawan_frequency_plan = "Korea 920"; +#elif defined(CFG_in866) + const char *lorawan_frequency_plan = "India 866"; +#else +# error "Region should be specified" +#endif // Values should be defined in config.h uint16_t lorawan_sending_interval = LORAWAN_SENDING_INTERVAL; // [s] @@ -36,7 +71,9 @@ namespace lorawan { char last_transmission[23] = ""; void initialize() { - Serial.println(F("Starting LoRaWAN. Frequency plan : " LMIC_FREQUENCY_PLAN " MHz.")); + Serial.print(F("Starting LoRaWAN. Frequency plan : ")); + Serial.print(config::lorawan_frequency_plan); + Serial.println(F(" MHz.")); // More info about pin mapping : https://github.com/mcci-catena/arduino-lmic#pin-mapping // Has been tested successfully with ESP32 TTGO LoRa32 V1, and might work with other ESP32+LoRa boards. @@ -51,7 +88,7 @@ namespace lorawan { } // Checks if OTAA is connected, or if payload should be sent. - // NOTE: while a transaction is in process (i.e. until the TXcomplete event has been received, no blocking code (e.g. delay loops etc.) are allowed, otherwise the LMIC/OS code might miss the event. + // NOTE: while a transaction is in process (i.e. until the TXcomplete event has been received), no blocking code (e.g. delay loops etc.) are allowed, otherwise the LMIC/OS code might miss the event. // If this rule is not followed, a typical symptom is that the first send is ok and all following ones end with the 'TX not complete' failure. void process() { os_runloop_once(); diff --git a/ampel-firmware/lorawan.h b/ampel-firmware/lorawan.h index 45f462b2ef761066797ccd2ff114762088b5c628..ed005f5c3a09e38591009e487b97a6a74e2d2b28 100644 --- a/ampel-firmware/lorawan.h +++ b/ampel-firmware/lorawan.h @@ -3,39 +3,15 @@ #include "config.h" -#if defined(AMPEL_LORAWAN) && defined(ESP32) -#include -// Requires "MCCI LoRaWAN LMIC library", which will be automatically used with PlatformIO but should be added in "Arduino IDE". -// Tested successfully with v3.2.0 and connected to a thethingsnetwork.org app. -#include -#include -#include -#include +# if defined(AMPEL_LORAWAN) && defined(ESP32) -#include "led_effects.h" -#include "sensor_console.h" -#include "util.h" +#include // For uint32_t & uint16_t namespace config { extern uint16_t lorawan_sending_interval; // [s] + extern const char *lorawan_frequency_plan; // e.g. "Europe 868" } -#if defined(CFG_eu868) -# define LMIC_FREQUENCY_PLAN "Europe 868" -#elif defined(CFG_us915) -# define LMIC_FREQUENCY_PLAN "US 915" -#elif defined(CFG_au915) -# define LMIC_FREQUENCY_PLAN "Australia 915" -#elif defined(CFG_as923) -# define LMIC_FREQUENCY_PLAN "Asia 923" -#elif defined(CFG_kr920) -# define LMIC_FREQUENCY_PLAN "Korea 920" -#elif defined(CFG_in866) -# define LMIC_FREQUENCY_PLAN "India 866" -#else -# error "Region should be specified" -#endif - namespace lorawan { extern bool waiting_for_confirmation; extern bool connected; @@ -47,5 +23,5 @@ namespace lorawan { void setLoRaInterval(int32_t sending_interval); } -#endif +# endif #endif diff --git a/ampel-firmware/mqtt.cpp b/ampel-firmware/mqtt.cpp index a3086dbfe170f60483921a51c71030e61b6b523f..57b5cd026f57586339e3d90eeb91aa204c242300 100644 --- a/ampel-firmware/mqtt.cpp +++ b/ampel-firmware/mqtt.cpp @@ -1,5 +1,18 @@ #include "mqtt.h" +#include "config.h" +#include "led_effects.h" +#include "sensor_console.h" +#include "wifi_util.h" +#include "ntp.h" +#include "src/lib/PubSubClient/src/PubSubClient.h" + +#if defined(ESP8266) +# include +#elif defined(ESP32) +# include +#endif + namespace config { // Values should be defined in config.h uint16_t mqtt_sending_interval = MQTT_SENDING_INTERVAL; // [s] diff --git a/ampel-firmware/mqtt.h b/ampel-firmware/mqtt.h index 7edeb9ae8886a9204fbb0a27ae4938ef19f1a514..0e39b09f7c88300b93de0b98741a63ffb664a36c 100644 --- a/ampel-firmware/mqtt.h +++ b/ampel-firmware/mqtt.h @@ -1,13 +1,9 @@ #ifndef MQTT_H_INCLUDED #define MQTT_H_INCLUDED -#include -#include "config.h" -#include "led_effects.h" -#include "sensor_console.h" -#include "src/lib/PubSubClient/src/PubSubClient.h" -#include "wifi_util.h" +#include // For uint32_t & uint16_t +#include "config.h" #if !defined(MQTT_ENCRYPTED) # define MQTT_ENCRYPTED true // Old config files might not define it, and encryption was on by default. #endif diff --git a/ampel-firmware/ntp.cpp b/ampel-firmware/ntp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..79d429f6c21f15e8ed47e1323c3350c6f54d7cff --- /dev/null +++ b/ampel-firmware/ntp.cpp @@ -0,0 +1,49 @@ +#include "ntp.h" +#include "sensor_console.h" +#include "config.h" +#include // required for NTP +#include "src/lib/NTPClient-master/NTPClient.h" // NTP + +namespace config { + const char *ntp_server = NTP_SERVER; + const long utc_offset_in_seconds = UTC_OFFSET_IN_SECONDS; // UTC+1 +} + +//NOTE: ESP32 sometimes couldn't access the NTP server, and every loop would take +1000ms +// ifdefs could be used to define functions specific to ESP32, e.g. with configTime +namespace ntp { + WiFiUDP ntpUDP; + NTPClient timeClient(ntpUDP, config::ntp_server, config::utc_offset_in_seconds, 60000UL); + bool connected_at_least_once = false; + void setLocalTime(int32_t unix_seconds); + + void initialize() { + timeClient.begin(); + sensor_console::defineIntCommand("set_time", ntp::setLocalTime, F("1618829570 (Sets time to the given UNIX time)")); + } + + void update() { + connected_at_least_once |= timeClient.update(); + } + + void getLocalTime(char *timestamp) { + timeClient.getFormattedDate(timestamp); + } + + void setLocalTime(int32_t unix_seconds) { + char time[23]; + timeClient.getFormattedDate(time); + Serial.print(F("Current time : ")); + Serial.println(time); + if (connected_at_least_once) { + Serial.println(F("NTP update already happened. Not changing anything.")); + return; + } + Serial.print(F("Setting UNIX time to : ")); + Serial.println(unix_seconds); + timeClient.setEpochTime(unix_seconds - seconds()); + timeClient.getFormattedDate(time); + Serial.print(F("Current time : ")); + Serial.println(time); + } +} diff --git a/ampel-firmware/ntp.h b/ampel-firmware/ntp.h new file mode 100644 index 0000000000000000000000000000000000000000..fe1647c7da1f4ca302c6dba3c6bba33e5595d873 --- /dev/null +++ b/ampel-firmware/ntp.h @@ -0,0 +1,13 @@ +#ifndef AMPEL_TIME_H_INCLUDED +#define AMPEL_TIME_H_INCLUDED + +namespace ntp { + void initialize(); + void update(); + void getLocalTime(char *timestamp); +} + +//NOTE: Only use seconds() for duration comparison, not timestamps comparison. Otherwise, problems happen when millis roll over. +#define seconds() (millis() / 1000UL) + +#endif diff --git a/ampel-firmware/sensor_console.h b/ampel-firmware/sensor_console.h index fe30469d47c9fa47f35beed710ef037b0484a731..9594317b575bf53db0da92f34e62613423fe6a75 100644 --- a/ampel-firmware/sensor_console.h +++ b/ampel-firmware/sensor_console.h @@ -1,6 +1,6 @@ #ifndef SENSOR_CONSOLE_H_INCLUDED #define SENSOR_CONSOLE_H_INCLUDED -#include +#include // For Flash strings, uint8_t and int32_t /** Other scripts can use this namespace, in order to define commands, via callbacks. * Those callbacks can then be used to send commands to the sensor (reset, calibrate, led on/off, ...) @@ -12,7 +12,7 @@ namespace sensor_console { void defineIntCommand(const char *name, void (*function)(int32_t), const __FlashStringHelper *doc_fstring); void defineStringCommand(const char *name, void (*function)(char*), const __FlashStringHelper *doc_fstring); - void processSerialInput(const byte in_byte); + void processSerialInput(const uint8_t in_byte); void execute(const char *command_line); } diff --git a/ampel-firmware/src/lib/PubSubClient/src/PubSubClient.cpp b/ampel-firmware/src/lib/PubSubClient/src/PubSubClient.cpp index 2b48d2b6b8ff28f5be7ad46a06c2e4f3f389aeae..2619e58e8c0b32c2f2051223140c3d40459e9bc2 100644 --- a/ampel-firmware/src/lib/PubSubClient/src/PubSubClient.cpp +++ b/ampel-firmware/src/lib/PubSubClient/src/PubSubClient.cpp @@ -1,161 +1,161 @@ /* - PubSubClient.cpp - A simple client for MQTT. - Nick O'Leary - http://knolleary.net -*/ + 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); - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} - -PubSubClient::PubSubClient(Client& client) { - this->_state = MQTT_DISCONNECTED; - setClient(client); - this->stream = NULL; - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} - -PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client) { - this->_state = MQTT_DISCONNECTED; - setServer(addr, port); - setClient(client); - this->stream = NULL; - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client& client, Stream& stream) { - this->_state = MQTT_DISCONNECTED; - setServer(addr,port); - setClient(client); - setStream(stream); - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -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; - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -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); - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} - -PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client) { - this->_state = MQTT_DISCONNECTED; - setServer(ip, port); - setClient(client); - this->stream = NULL; - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client& client, Stream& stream) { - this->_state = MQTT_DISCONNECTED; - setServer(ip,port); - setClient(client); - setStream(stream); - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -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; - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -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); - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} - -PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client) { - this->_state = MQTT_DISCONNECTED; - setServer(domain,port); - setClient(client); - this->stream = NULL; - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -PubSubClient::PubSubClient(const char* domain, uint16_t port, Client& client, Stream& stream) { - this->_state = MQTT_DISCONNECTED; - setServer(domain,port); - setClient(client); - setStream(stream); - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -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; - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); -} -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); - this->bufferSize = 0; - setBufferSize(MQTT_MAX_PACKET_SIZE); - setKeepAlive(MQTT_KEEPALIVE); - setSocketTimeout(MQTT_SOCKET_TIMEOUT); + this->_state = MQTT_DISCONNECTED; + this->_client = NULL; + this->stream = NULL; + setCallback(NULL); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} + +PubSubClient::PubSubClient(Client &client) { + this->_state = MQTT_DISCONNECTED; + setClient(client); + this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} + +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client &client) { + this->_state = MQTT_DISCONNECTED; + setServer(addr, port); + setClient(client); + this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +PubSubClient::PubSubClient(IPAddress addr, uint16_t port, Client &client, Stream &stream) { + this->_state = MQTT_DISCONNECTED; + setServer(addr, port); + setClient(client); + setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +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; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +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); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} + +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client &client) { + this->_state = MQTT_DISCONNECTED; + setServer(ip, port); + setClient(client); + this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +PubSubClient::PubSubClient(uint8_t *ip, uint16_t port, Client &client, Stream &stream) { + this->_state = MQTT_DISCONNECTED; + setServer(ip, port); + setClient(client); + setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +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; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +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); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} + +PubSubClient::PubSubClient(const char *domain, uint16_t port, Client &client) { + this->_state = MQTT_DISCONNECTED; + setServer(domain, port); + setClient(client); + this->stream = NULL; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +PubSubClient::PubSubClient(const char *domain, uint16_t port, Client &client, Stream &stream) { + this->_state = MQTT_DISCONNECTED; + setServer(domain, port); + setClient(client); + setStream(stream); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +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; + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); +} +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); + this->bufferSize = 0; + setBufferSize(MQTT_MAX_PACKET_SIZE); + setKeepAlive(MQTT_KEEPALIVE); + setSocketTimeout(MQTT_SOCKET_TIMEOUT); } PubSubClient::~PubSubClient() { @@ -163,424 +163,431 @@ PubSubClient::~PubSubClient() { } boolean PubSubClient::connect(const char *id) { - return connect(id,NULL,NULL,0,0,0,0,1); + 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); + 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 *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) { + 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; +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 (_client->connected()) { + result = 1; + } else { + if (domain != NULL) { + result = _client->connect(this->domain, this->port); + } else { + result = _client->connect(this->ip, this->port); + } + } - if(_client->connected()) { - result = 1; - } else { - 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 (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}; + uint8_t d[7] = { 0x00, 0x04, 'M', 'Q', 'T', 'T', MQTT_VERSION }; #define MQTT_HEADER_VERSION_LENGTH 7 #endif - for (j = 0;jbuffer[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); - } - } - this->buffer[length++] = v; - - this->buffer[length++] = ((this->keepAlive) >> 8); - this->buffer[length++] = ((this->keepAlive) & 0xFF); - - CHECK_STRING_LENGTH(length,id) - length = writeString(id,this->buffer,length); - if (willTopic) { - CHECK_STRING_LENGTH(length,willTopic) - length = writeString(willTopic,this->buffer,length); - CHECK_STRING_LENGTH(length,willMessage) - length = writeString(willMessage,this->buffer,length); - } - - if(user != NULL) { - CHECK_STRING_LENGTH(length,user) - length = writeString(user,this->buffer,length); - if(pass != NULL) { - CHECK_STRING_LENGTH(length,pass) - length = writeString(pass,this->buffer,length); - } - } + for (j = 0; j < MQTT_HEADER_VERSION_LENGTH; j++) { + this->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); + } + } + this->buffer[length++] = v; + + this->buffer[length++] = ((this->keepAlive) >> 8); + this->buffer[length++] = ((this->keepAlive) & 0xFF); + + CHECK_STRING_LENGTH(length, id) + length = writeString(id, this->buffer, length); + if (willTopic) { + CHECK_STRING_LENGTH(length, willTopic) + length = writeString(willTopic, this->buffer, length); + CHECK_STRING_LENGTH(length, willMessage) + length = writeString(willMessage, this->buffer, length); + } + + if (user != NULL) { + CHECK_STRING_LENGTH(length, user) + length = writeString(user, this->buffer, length); + if (pass != NULL) { + CHECK_STRING_LENGTH(length, pass) + length = writeString(pass, this->buffer, length); + } + } - write(MQTTCONNECT,this->buffer,length-MQTT_MAX_HEADER_SIZE); + write(MQTTCONNECT, this->buffer, length - MQTT_MAX_HEADER_SIZE); - lastInActivity = lastOutActivity = millis(); + lastInActivity = lastOutActivity = millis(); - while (!_client->available()) { - unsigned long t = millis(); - if (t-lastInActivity >= ((int32_t) this->socketTimeout*1000UL)) { - _state = MQTT_CONNECTION_TIMEOUT; - _client->stop(); - return false; - } - } - uint8_t llen; - uint32_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(); + while (!_client->available()) { + unsigned long t = millis(); + if (t - lastInActivity >= ((int32_t) this->socketTimeout * 1000UL)) { + _state = MQTT_CONNECTION_TIMEOUT; + _client->stop(); + return false; + } + } + uint8_t llen; + uint32_t len = readPacket(&llen); + + if (len == 4) { + if (buffer[3] == 0) { + lastInActivity = millis(); + pingOutstanding = false; + _state = MQTT_CONNECTED; + return true; } else { - _state = MQTT_CONNECT_FAILED; + _state = buffer[3]; } - return false; + } + _client->stop(); + } else { + _state = MQTT_CONNECT_FAILED; } - return true; + 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) this->socketTimeout * 1000)){ - return false; - } - } - *result = _client->read(); - return true; +boolean PubSubClient::readByte(uint8_t *result) { + uint32_t previousMillis = millis(); + while (!_client->available()) { + yield(); + uint32_t currentMillis = millis(); + if (currentMillis - previousMillis >= ((int32_t) this->socketTimeout * 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){ +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)){ + uint8_t *write_address = &(result[current_index]); + if (readByte(write_address)) { *index = current_index + 1; return true; } return false; } -uint32_t PubSubClient::readPacket(uint8_t* lengthLength) { - uint16_t len = 0; - if(!readByte(this->buffer, &len)) return 0; - bool isPublish = (this->buffer[0]&0xF0) == MQTTPUBLISH; - uint32_t multiplier = 1; - uint32_t length = 0; - uint8_t digit = 0; - uint16_t skip = 0; - uint32_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; - this->buffer[len++] = digit; - length += (digit & 127) * multiplier; - multiplier <<=7; //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(this->buffer, &len)) return 0; - if(!readByte(this->buffer, &len)) return 0; - skip = (this->buffer[*lengthLength+1]<<8)+this->buffer[*lengthLength+2]; - start = 2; - if (this->buffer[0]&MQTTQOS1) { - // skip message id - skip += 2; - } +uint32_t PubSubClient::readPacket(uint8_t *lengthLength) { + uint16_t len = 0; + if (!readByte(this->buffer, &len)) + return 0; + bool isPublish = (this->buffer[0] & 0xF0) == MQTTPUBLISH; + uint32_t multiplier = 1; + uint32_t length = 0; + uint8_t digit = 0; + uint16_t skip = 0; + uint32_t start = 0; + + do { + if (len == 5) { + // Invalid remaining length encoding - kill the connection + _state = MQTT_DISCONNECTED; + _client->stop(); + return 0; } - uint32_t idx = len; - - for (uint32_t i = start;istream) { - if (isPublish && idx-*lengthLength-2>skip) { - this->stream->write(digit); - } - } - - if (len < this->bufferSize) { - this->buffer[len] = digit; - len++; - } - idx++; + if (!readByte(&digit)) + return 0; + this->buffer[len++] = digit; + length += (digit & 127) * multiplier; + multiplier <<= 7; //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(this->buffer, &len)) + return 0; + if (!readByte(this->buffer, &len)) + return 0; + skip = (this->buffer[*lengthLength + 1] << 8) + this->buffer[*lengthLength + 2]; + start = 2; + if (this->buffer[0] & MQTTQOS1) { + // skip message id + skip += 2; + } + } + uint32_t idx = len; + + for (uint32_t i = start; i < length; i++) { + if (!readByte(&digit)) + return 0; + if (this->stream) { + if (isPublish && idx - *lengthLength - 2 > skip) { + this->stream->write(digit); + } } - if (!this->stream && idx > this->bufferSize) { - len = 0; // This will cause the packet to be ignored. + if (len < this->bufferSize) { + this->buffer[len] = digit; + len++; } - return len; + idx++; + } + + if (!this->stream && idx > this->bufferSize) { + len = 0; // This will cause the packet to be ignored. + } + return len; } boolean PubSubClient::loop() { - if (connected()) { - unsigned long t = millis(); - if ((t - lastInActivity > this->keepAlive*1000UL) || (t - lastOutActivity > this->keepAlive*1000UL)) { - if (pingOutstanding) { - this->_state = MQTT_CONNECTION_TIMEOUT; - _client->stop(); - return false; + if (connected()) { + unsigned long t = millis(); + if ((t - lastInActivity > this->keepAlive * 1000UL) || (t - lastOutActivity > this->keepAlive * 1000UL)) { + if (pingOutstanding) { + this->_state = MQTT_CONNECTION_TIMEOUT; + _client->stop(); + return false; + } else { + this->buffer[0] = MQTTPINGREQ; + this->buffer[1] = 0; + _client->write(this->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 = this->buffer[0] & 0xF0; + if (type == MQTTPUBLISH) { + if (callback) { + uint16_t tl = (this->buffer[llen + 1] << 8) + this->buffer[llen + 2]; /* topic length in bytes */ + memmove(this->buffer + llen + 2, this->buffer + llen + 3, tl); /* move topic inside buffer 1 byte to front */ + this->buffer[llen + 2 + tl] = 0; /* end the topic as a 'C' string with \x00 */ + char *topic = (char*) this->buffer + llen + 2; + // msgId only present for QOS>0 + if ((this->buffer[0] & 0x06) == MQTTQOS1) { + msgId = (this->buffer[llen + 3 + tl] << 8) + this->buffer[llen + 3 + tl + 1]; + payload = this->buffer + llen + 3 + tl + 2; + callback(topic, payload, len - llen - 3 - tl - 2); + + this->buffer[0] = MQTTPUBACK; + this->buffer[1] = 2; + this->buffer[2] = (msgId >> 8); + this->buffer[3] = (msgId & 0xFF); + _client->write(this->buffer, 4); + lastOutActivity = t; + } else { - this->buffer[0] = MQTTPINGREQ; - this->buffer[1] = 0; - _client->write(this->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 = this->buffer[0]&0xF0; - if (type == MQTTPUBLISH) { - if (callback) { - uint16_t tl = (this->buffer[llen+1]<<8)+this->buffer[llen+2]; /* topic length in bytes */ - memmove(this->buffer+llen+2,this->buffer+llen+3,tl); /* move topic inside buffer 1 byte to front */ - this->buffer[llen+2+tl] = 0; /* end the topic as a 'C' string with \x00 */ - char *topic = (char*) this->buffer+llen+2; - // msgId only present for QOS>0 - if ((this->buffer[0]&0x06) == MQTTQOS1) { - msgId = (this->buffer[llen+3+tl]<<8)+this->buffer[llen+3+tl+1]; - payload = this->buffer+llen+3+tl+2; - callback(topic,payload,len-llen-3-tl-2); - - this->buffer[0] = MQTTPUBACK; - this->buffer[1] = 2; - this->buffer[2] = (msgId >> 8); - this->buffer[3] = (msgId & 0xFF); - _client->write(this->buffer,4); - lastOutActivity = t; - - } else { - payload = this->buffer+llen+3+tl; - callback(topic,payload,len-llen-3-tl); - } - } - } else if (type == MQTTPINGREQ) { - this->buffer[0] = MQTTPINGRESP; - this->buffer[1] = 0; - _client->write(this->buffer,2); - } else if (type == MQTTPINGRESP) { - pingOutstanding = false; - } - } else if (!connected()) { - // readPacket has closed the connection - return false; + payload = this->buffer + llen + 3 + tl; + callback(topic, payload, len - llen - 3 - tl); } + } + } else if (type == MQTTPINGREQ) { + this->buffer[0] = MQTTPINGRESP; + this->buffer[1] = 0; + _client->write(this->buffer, 2); + } else if (type == MQTTPINGRESP) { + pingOutstanding = false; } - return true; + } else if (!connected()) { + // readPacket has closed the connection + return false; + } } - return false; + return true; + } + return false; } -boolean PubSubClient::publish(const char* topic, const char* payload) { - return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,false); +boolean PubSubClient::publish(const char *topic, const char *payload) { + return publish(topic, (const uint8_t*) payload, payload ? strnlen(payload, this->bufferSize) : 0, false); } -boolean PubSubClient::publish(const char* topic, const char* payload, boolean retained) { - return publish(topic,(const uint8_t*)payload, payload ? strnlen(payload, this->bufferSize) : 0,retained); +boolean PubSubClient::publish(const char *topic, const char *payload, boolean retained) { + return publish(topic, (const uint8_t*) payload, payload ? strnlen(payload, this->bufferSize) : 0, 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) { + return publish(topic, payload, plength, false); } -boolean PubSubClient::publish(const char* topic, const uint8_t* payload, unsigned int plength, boolean retained) { - if (connected()) { - if (this->bufferSize < MQTT_MAX_HEADER_SIZE + 2+strnlen(topic, this->bufferSize) + 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,this->buffer,length); - - // Add payload - uint16_t i; - for (i=0;ibuffer[length++] = payload[i]; - } +boolean PubSubClient::publish(const char *topic, const uint8_t *payload, unsigned int plength, boolean retained) { + if (connected()) { + if (this->bufferSize < MQTT_MAX_HEADER_SIZE + 2 + strnlen(topic, this->bufferSize) + 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, this->buffer, length); + + // Add payload + uint16_t i; + for (i = 0; i < plength; i++) { + this->buffer[length++] = payload[i]; + } - // Write the header - uint8_t header = MQTTPUBLISH; - if (retained) { - header |= 1; - } - return write(header,this->buffer,length-MQTT_MAX_HEADER_SIZE); + // Write the header + uint8_t header = MQTTPUBLISH; + if (retained) { + header |= 1; } - return false; + return write(header, this->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, payload ? strnlen(payload, this->bufferSize) : 0, retained); +boolean PubSubClient::publish_P(const char *topic, const char *payload, boolean retained) { + return publish_P(topic, (const uint8_t*) payload, payload ? strnlen(payload, this->bufferSize) : 0, 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; - int expectedLength; +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; + uint32_t expectedLength; - if (!connected()) { - return false; - } + if (!connected()) { + return false; + } - tlen = strnlen(topic, this->bufferSize); + tlen = strnlen(topic, this->bufferSize); - header = MQTTPUBLISH; - if (retained) { - header |= 1; + header = MQTTPUBLISH; + if (retained) { + header |= 1; + } + this->buffer[pos++] = header; + len = plength + 2 + tlen; + do { + digit = len & 127; //digit = len %128 + len >>= 7; //len = len / 128 + if (len > 0) { + digit |= 0x80; } - this->buffer[pos++] = header; - len = plength + 2 + tlen; - do { - digit = len & 127; //digit = len %128 - len >>= 7; //len = len / 128 - if (len > 0) { - digit |= 0x80; - } - this->buffer[pos++] = digit; - llen++; - } while(len>0); + this->buffer[pos++] = digit; + llen++; + } while (len > 0); - pos = writeString(topic,this->buffer,pos); + pos = writeString(topic, this->buffer, pos); - rc += _client->write(this->buffer,pos); + rc += _client->write(this->buffer, pos); - for (i=0;iwrite((char)pgm_read_byte_near(payload + i)); - } + for (i = 0; i < plength; i++) { + rc += _client->write((char) pgm_read_byte_near(payload + i)); + } - lastOutActivity = millis(); + lastOutActivity = millis(); - expectedLength = 1 + llen + 2 + tlen + plength; + expectedLength = 1 + llen + 2 + tlen + plength; - return (rc == expectedLength); + return (rc == expectedLength); } -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,this->buffer,length); - uint8_t header = MQTTPUBLISH; - if (retained) { - header |= 1; - } - size_t hlen = buildHeader(header, this->buffer, plength+length-MQTT_MAX_HEADER_SIZE); - uint16_t rc = _client->write(this->buffer+(MQTT_MAX_HEADER_SIZE-hlen),length-(MQTT_MAX_HEADER_SIZE-hlen)); - lastOutActivity = millis(); - return (rc == (length-(MQTT_MAX_HEADER_SIZE-hlen))); +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, this->buffer, length); + uint8_t header = MQTTPUBLISH; + if (retained) { + header |= 1; } - return false; + size_t hlen = buildHeader(header, this->buffer, plength + length - MQTT_MAX_HEADER_SIZE); + uint16_t rc = _client->write(this->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; + return 1; } size_t PubSubClient::write(uint8_t data) { - lastOutActivity = millis(); - return _client->write(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 & 127; //digit = len %128 - len >>= 7; //len = len / 128 - if (len > 0) { - digit |= 0x80; - } - lenBuf[pos++] = digit; - llen++; - } while(len>0); - - buf[4-llen] = header; - for (int i=0;iwrite(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 & 127; //digit = len %128 + len >>= 7; //len = len / 128 + if (len > 0) { + digit |= 0x80; } - return llen+1; // Full header size is variable length bit plus the 1-byte fixed header + 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); +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); @@ -596,174 +603,173 @@ boolean PubSubClient::write(uint8_t header, uint8_t* buf, uint16_t length) { } return result; #else - rc = _client->write(buf+(MQTT_MAX_HEADER_SIZE-hlen),length+hlen); - lastOutActivity = millis(); - return (rc == hlen+length); + 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) { + return subscribe(topic, 0); } -boolean PubSubClient::subscribe(const char* topic, uint8_t qos) { - size_t topicLength = strnlen(topic, this->bufferSize); - if (topic == 0) { - return false; - } - if (qos > 1) { - return false; - } - if (this->bufferSize < 9 + topicLength) { - // 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; - } - this->buffer[length++] = (nextMsgId >> 8); - this->buffer[length++] = (nextMsgId & 0xFF); - length = writeString((char*)topic, this->buffer,length); - this->buffer[length++] = qos; - return write(MQTTSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE); - } +boolean PubSubClient::subscribe(const char *topic, uint8_t qos) { + size_t topicLength = strnlen(topic, this->bufferSize); + if (topic == 0) { + return false; + } + if (qos > 1) { return false; + } + if (this->bufferSize < 9 + topicLength) { + // 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; + } + this->buffer[length++] = (nextMsgId >> 8); + this->buffer[length++] = (nextMsgId & 0xFF); + length = writeString((char*) topic, this->buffer, length); + this->buffer[length++] = qos; + return write(MQTTSUBSCRIBE | MQTTQOS1, this->buffer, length - MQTT_MAX_HEADER_SIZE); + } + return false; } -boolean PubSubClient::unsubscribe(const char* topic) { - size_t topicLength = strnlen(topic, this->bufferSize); - if (topic == 0) { - return false; - } - if (this->bufferSize < 9 + topicLength) { - // Too long - return false; - } - if (connected()) { - uint16_t length = MQTT_MAX_HEADER_SIZE; - nextMsgId++; - if (nextMsgId == 0) { - nextMsgId = 1; - } - this->buffer[length++] = (nextMsgId >> 8); - this->buffer[length++] = (nextMsgId & 0xFF); - length = writeString(topic, this->buffer,length); - return write(MQTTUNSUBSCRIBE|MQTTQOS1,this->buffer,length-MQTT_MAX_HEADER_SIZE); - } +boolean PubSubClient::unsubscribe(const char *topic) { + size_t topicLength = strnlen(topic, this->bufferSize); + if (topic == 0) { return false; + } + if (this->bufferSize < 9 + topicLength) { + // Too long + return false; + } + if (connected()) { + uint16_t length = MQTT_MAX_HEADER_SIZE; + nextMsgId++; + if (nextMsgId == 0) { + nextMsgId = 1; + } + this->buffer[length++] = (nextMsgId >> 8); + this->buffer[length++] = (nextMsgId & 0xFF); + length = writeString(topic, this->buffer, length); + return write(MQTTUNSUBSCRIBE | MQTTQOS1, this->buffer, length - MQTT_MAX_HEADER_SIZE); + } + return false; } void PubSubClient::disconnect() { - this->buffer[0] = MQTTDISCONNECT; - this->buffer[1] = 0; - _client->write(this->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; + this->buffer[0] = MQTTDISCONNECT; + this->buffer[1] = 0; + _client->write(this->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; + 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(); + } } else { - rc = (int)_client->connected(); - if (!rc) { - if (this->_state == MQTT_CONNECTED) { - this->_state = MQTT_CONNECTION_LOST; - _client->flush(); - _client->stop(); - } - } else { - return this->_state == MQTT_CONNECTED; - } + return this->_state == MQTT_CONNECTED; } - return rc; + } + 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(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; + 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::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; + this->callback = callback; + return *this; } -PubSubClient& PubSubClient::setClient(Client& client){ - this->_client = &client; - return *this; +PubSubClient& PubSubClient::setClient(Client &client) { + this->_client = &client; + return *this; } -PubSubClient& PubSubClient::setStream(Stream& stream){ - this->stream = &stream; - return *this; +PubSubClient& PubSubClient::setStream(Stream &stream) { + this->stream = &stream; + return *this; } int PubSubClient::state() { - return this->_state; + return this->_state; } boolean PubSubClient::setBufferSize(uint16_t size) { - if (size == 0) { - // Cannot set it back to 0 - return false; - } - if (this->bufferSize == 0) { - this->buffer = (uint8_t*)malloc(size); + if (size == 0) { + // Cannot set it back to 0 + return false; + } + if (this->bufferSize == 0) { + this->buffer = (uint8_t*) malloc(size); + } else { + uint8_t *newBuffer = (uint8_t*) realloc(this->buffer, size); + if (newBuffer != NULL) { + this->buffer = newBuffer; } else { - uint8_t* newBuffer = (uint8_t*)realloc(this->buffer, size); - if (newBuffer != NULL) { - this->buffer = newBuffer; - } else { - return false; - } + return false; } - this->bufferSize = size; - return (this->buffer != NULL); + } + this->bufferSize = size; + return (this->buffer != NULL); } uint16_t PubSubClient::getBufferSize() { - return this->bufferSize; + return this->bufferSize; } PubSubClient& PubSubClient::setKeepAlive(uint16_t keepAlive) { - this->keepAlive = keepAlive; - return *this; + this->keepAlive = keepAlive; + return *this; } PubSubClient& PubSubClient::setSocketTimeout(uint16_t timeout) { - this->socketTimeout = timeout; - return *this; + this->socketTimeout = timeout; + return *this; } diff --git a/ampel-firmware/util.cpp b/ampel-firmware/util.cpp index 73f53766a47e5da2a2d079ed64eed019cd8bad5e..130a051e56880b88598408726cc2c5ce3b3dc1fb 100644 --- a/ampel-firmware/util.cpp +++ b/ampel-firmware/util.cpp @@ -1,62 +1,16 @@ #include "util.h" - -namespace config { - const char *ntp_server = NTP_SERVER; - const long utc_offset_in_seconds = UTC_OFFSET_IN_SECONDS; // UTC+1 -} +#include "sensor_console.h" #if defined(ESP8266) +# include // required to get MAC address const char *current_board = "ESP8266"; -# if !defined(AMPEL_WIFI) -void preinit() { - // WiFi would be initialized otherwise (on ESP8266), even if unused. - // see https://github.com/esp8266/Arduino/issues/2111#issuecomment-224251391 - ESP8266WiFiClass::preinitWiFiOff(); -} -# endif #elif defined(ESP32) +# include // required to get MAC address const char *current_board = "ESP32"; #else const char *current_board = "UNKNOWN"; #endif -//NOTE: ESP32 sometimes couldn't access the NTP server, and every loop would take +1000ms -// ifdefs could be used to define functions specific to ESP32, e.g. with configTime -namespace ntp { - WiFiUDP ntpUDP; - NTPClient timeClient(ntpUDP, config::ntp_server, config::utc_offset_in_seconds, 60000UL); - bool connected_at_least_once = false; - - void initialize() { - timeClient.begin(); - } - - void update() { - connected_at_least_once |= timeClient.update(); - } - - void getLocalTime(char *timestamp) { - timeClient.getFormattedDate(timestamp); - } - - void setLocalTime(int32_t unix_seconds) { - char time[23]; - timeClient.getFormattedDate(time); - Serial.print(F("Current time : ")); - Serial.println(time); - if (connected_at_least_once) { - Serial.println(F("NTP update already happened. Not changing anything.")); - return; - } - Serial.print(F("Setting UNIX time to : ")); - Serial.println(unix_seconds); - timeClient.setEpochTime(unix_seconds - seconds()); - timeClient.getFormattedDate(time); - Serial.print(F("Current time : ")); - Serial.println(time); - } -} - void Ampel::showFreeSpace() { Serial.print(F("Free heap space : ")); Serial.print(ESP.getFreeHeap()); @@ -90,7 +44,6 @@ char* getSensorId() { Ampel::Ampel() : board(current_board), sensorId(getSensorId()), macAddress(getMacString()), max_loop_duration(0) { - sensor_console::defineIntCommand("set_time", ntp::setLocalTime, F("1618829570 (Sets time to the given UNIX time)")); sensor_console::defineCommand("free", Ampel::showFreeSpace, F("(Displays available heap space)")); sensor_console::defineCommand("reset", []() { ESP.restart(); diff --git a/ampel-firmware/util.h b/ampel-firmware/util.h index 5d13bae02394ca2db91a319e0d8d7aaf3eac0612..495f7eaa0b149658631ec38b1cd5dab318e00af8 100644 --- a/ampel-firmware/util.h +++ b/ampel-firmware/util.h @@ -1,28 +1,16 @@ #ifndef AMPEL_UTIL_H_INCLUDED #define AMPEL_UTIL_H_INCLUDED -#include -#include "config.h" -#include "sensor_console.h" -#include // required for NTP -#include "src/lib/NTPClient-master/NTPClient.h" // NTP +#include // For uint32_t #if defined(ESP8266) -# include // required to get MAC address # define esp_get_max_free_block_size() ESP.getMaxFreeBlockSize() # define esp_get_heap_fragmentation() ESP.getHeapFragmentation() #elif defined(ESP32) -# include // required to get MAC address # define esp_get_max_free_block_size() ESP.getMaxAllocHeap() //largest block of heap that can be allocated. # define esp_get_heap_fragmentation() "?" // apparently not available for ESP32 #endif -namespace ntp { - void initialize(); - void update(); - void getLocalTime(char *timestamp); -} - namespace util { template inline auto min(const Tpa &a, const Tpb &b) -> decltype(a < b ? a : b) { @@ -48,7 +36,4 @@ public: extern Ampel ampel; -//NOTE: Only use seconds() for duration comparison, not timestamps comparison. Otherwise, problems happen when millis roll over. -#define seconds() (millis() / 1000UL) - #endif diff --git a/ampel-firmware/web_server.cpp b/ampel-firmware/web_server.cpp index 75897bc253172c7459be4f1a1a557ff132c73c55..266be7cfa7f33eeb42510182bedd87819a3a70d3 100644 --- a/ampel-firmware/web_server.cpp +++ b/ampel-firmware/web_server.cpp @@ -1,5 +1,27 @@ #include "web_server.h" +#if defined(ESP8266) +# include +#elif defined(ESP32) +# include +#endif + +#include "config.h" +#include "util.h" +#include "ntp.h" +#include "wifi_util.h" +#include "co2_sensor.h" +#include "sensor_console.h" +#ifdef AMPEL_CSV +# include "csv_writer.h" +#endif +#ifdef AMPEL_MQTT +# include "mqtt.h" +#endif +#ifdef AMPEL_LORAWAN +# include "lorawan.h" +#endif + namespace config { // Values should be defined in config.h #ifdef HTTP_USER @@ -236,7 +258,7 @@ namespace web_server { mqtt::connected ? "Yes" : "No", mqtt::last_successful_publish, config::mqtt_sending_interval, #endif #if defined(AMPEL_LORAWAN) && defined(ESP32) - lorawan::connected ? "Yes" : "No", LMIC_FREQUENCY_PLAN, lorawan::last_transmission, + lorawan::connected ? "Yes" : "No", config::lorawan_frequency_plan, lorawan::last_transmission, config::lorawan_sending_interval, #endif config::temperature_offset, config::auto_calibrate_sensor ? "Yes" : "No", ampel.sensorId, ampel.sensorId, diff --git a/ampel-firmware/web_server.h b/ampel-firmware/web_server.h index bc2c0101fca661d1d7cd0db5ff922851b0224e58..ad52b656bf25279d9816679e6de41a6046962985 100644 --- a/ampel-firmware/web_server.h +++ b/ampel-firmware/web_server.h @@ -1,27 +1,6 @@ #ifndef WEB_SERVER_H_ #define WEB_SERVER_H_ -#if defined(ESP8266) -# include -#elif defined(ESP32) -# include -#endif - -#include "config.h" -#include "util.h" -#include "wifi_util.h" -#include "co2_sensor.h" -#include "sensor_console.h" -#ifdef AMPEL_CSV -# include "csv_writer.h" -#endif -#ifdef AMPEL_MQTT -# include "mqtt.h" -#endif -#ifdef AMPEL_LORAWAN -# include "lorawan.h" -#endif - namespace web_server { void initialize(); void update(); diff --git a/ampel-firmware/wifi_util.cpp b/ampel-firmware/wifi_util.cpp index 4cdc7a2a597d3d66523c6e37b837346e5231311e..131a61574bdcec7d83c7087474cf44b87f34147f 100644 --- a/ampel-firmware/wifi_util.cpp +++ b/ampel-firmware/wifi_util.cpp @@ -1,5 +1,17 @@ #include "wifi_util.h" +#include "config.h" +#include "util.h" +#include "ntp.h" +#include "led_effects.h" +#include "sensor_console.h" + +#if defined(ESP8266) +# include +#elif defined(ESP32) +# include +#endif + namespace config { // WiFi config. See 'config.h' if you want to modify those values. const char *wifi_ssid = WIFI_SSID; @@ -8,7 +20,7 @@ namespace config { #ifdef WIFI_TIMEOUT const uint8_t wifi_timeout = WIFI_TIMEOUT; // [s] Will try to connect during wifi_timeout seconds before failing. #else - const uint8_t wifi_timeout = 60; // [s] Will try to connect during wifi_timeout seconds before failing. + const uint8_t wifi_timeout = 60; // [s] Will try to connect during wifi_timeout seconds before failing. #endif } @@ -40,6 +52,10 @@ namespace wifi { Serial.println(WIFI_SSID); } + bool connected() { + return WiFi.status() == WL_CONNECTED; + } + // Initialize Wi-Fi void connect(const char *hostname) { @@ -64,7 +80,7 @@ namespace wifi { led_effects::showRainbowWheel(); Serial.print("."); } - if (WiFi.status() == WL_CONNECTED) { + if (connected()) { led_effects::showKITTWheel(color::green); Serial.println(); Serial.print(F("WiFi - Connected! IP address: ")); @@ -74,7 +90,8 @@ namespace wifi { } else { //TODO: Allow sensor to work as an Access Point, in order to define SSID & password? led_effects::showKITTWheel(color::red); - Serial.println(F("Connection to WiFi failed")); + Serial.print(F("Connection to WiFi failed! Status : ")); + Serial.println(WiFi.status()); } } } diff --git a/ampel-firmware/wifi_util.h b/ampel-firmware/wifi_util.h index 1cfe3da134de83f735bad7b8d58cd825fbd5e5d1..78249ec8269d9b0fead0e0cbf030f0670248ce81 100644 --- a/ampel-firmware/wifi_util.h +++ b/ampel-firmware/wifi_util.h @@ -1,13 +1,10 @@ #ifndef WIFI_UTIL_H_INCLUDED #define WIFI_UTIL_H_INCLUDED -#include "config.h" -#include "util.h" -#include "led_effects.h" - namespace wifi { extern char local_ip[]; void connect(const char *hostname); + bool connected(); } #endif diff --git a/platformio.ini b/platformio.ini index becf69fc263e19f956d1cf7a7ff4701dd7322247..3d016a6c7c5d7c0a985c08a37e29b23aa2c3a69c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -24,8 +24,3 @@ monitor_speed = 115200 lib_deps = MCCI LoRaWAN LMIC library - -build_flags = - -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS - -D CFG_eu868=1 - -D CFG_sx1276_radio=1