From d75d098247f96b8198985661972a590be555a159 Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Mon, 1 Jan 2024 11:14:33 +0700 Subject: [PATCH] IoT Comment --- .../lib/ESPMegaPRO/ESPMegaIoT.cpp | 253 +++++++++++++++++- .../lib/ESPMegaPRO/ESPMegaIoT.hpp | 54 ++-- 2 files changed, 287 insertions(+), 20 deletions(-) diff --git a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.cpp b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.cpp index eec998e..8cc2f13 100644 --- a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.cpp +++ b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.cpp @@ -1,6 +1,11 @@ #include #include +/** + * @brief Create a new ESPMegaIoT object + * + * @note You shold not create this object directly, Instead, you should use the ESPMegaPRO::iot object + */ ESPMegaIoT::ESPMegaIoT() : mqtt(tcpClient) { tcpClient.setTimeout(TCP_TIMEOUT_SEC); @@ -13,10 +18,24 @@ ESPMegaIoT::ESPMegaIoT() : mqtt(tcpClient) mqtt_connected = false; } +/** + * @brief Destroy the ESPMegaIoT object + */ ESPMegaIoT::~ESPMegaIoT() { } +/** + * @brief The mqtt callback function, This function is called when a message is received on a subscribed topic + * + * This function is called when a message is received on a subscribed topic + * The payload is copied to a buffer and a null terminator is added + * The payload is then passed to the respective card's mqtt callback + * + * @param topic The topic of the message + * @param payload The payload of the message in byte form + * @param length The length of the payload + */ void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length) { // Create a null terminated string from the payload @@ -46,17 +65,33 @@ void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length) components[card_id]->handleMqttMessage(topic_without_base + 3, payload_buffer); } +/** + * @brief Set the base topic for the IoT + * + * @param base_topic The base topic + */ void ESPMegaIoT::setBaseTopic(char *base_topic) { strcpy(this->mqtt_config.base_topic, base_topic); base_topic_length = strlen(this->mqtt_config.base_topic); } +/** + * @brief Begin the ESPMegaIoT object + * + * @param cards The array of ExpansionCard objects + */ void ESPMegaIoT::intr_begin(ExpansionCard *cards[]) { this->cards = cards; active = true; } + +/** + * @brief The main loop for the ESPMegaIoT object + * + * @note Normally you should not call this function, Instead, you should call ESPMegaPRO::loop() + */ void ESPMegaIoT::loop() { if (!active) @@ -73,7 +108,14 @@ void ESPMegaIoT::loop() sessionKeepAlive(); } -// Register Existing Card for use with IoT +/** + * @brief Register an existing card for use with IoT + * + * This function registers an existing card for use with IoT + * The card should be installed using ESPMegaPRO::installCard() before calling this function + * + * @param card_id The id of the card + */ void ESPMegaIoT::registerCard(uint8_t card_id) { // Check if the card is already registered @@ -127,6 +169,12 @@ void ESPMegaIoT::registerCard(uint8_t card_id) return; } } + +/** + * @brief Unregister a card + * + * @param card_id The id of the card + */ void ESPMegaIoT::unregisterCard(uint8_t card_id) { // Check if the card is registered @@ -138,6 +186,10 @@ void ESPMegaIoT::unregisterCard(uint8_t card_id) delete components[card_id]; components[card_id] = NULL; } + +/** + * @brief Publish all cards's reports + */ void ESPMegaIoT::publishCard(uint8_t card_id) { // Check if the card is registered @@ -148,30 +200,76 @@ void ESPMegaIoT::publishCard(uint8_t card_id) // Publish the card components[card_id]->publishReport(); } -void ESPMegaIoT::subscribeToTopic(char *topic) + +/** + * @brief Subscribe to a topic + * + * @param topic The topic to subscribe to + */ +void ESPMegaIoT::subscribe(char *topic) { mqtt.subscribe(topic); } + +/** + * @brief Unsubscribe from a topic + * + * @param topic The topic to unsubscribe from + */ void ESPMegaIoT::unsubscribeFromTopic(char *topic) { mqtt.unsubscribe(topic); } + +/** + * @brief Connect to a wifi network + * + * @param ssid The SSID of the wifi network + * @param password The password of the wifi network + */ void ESPMegaIoT::connectToWifi(char *ssid, char *password) { WiFi.begin(ssid, password); } + +/** + * @brief Connect to a unsecured wifi network + * + * @param ssid The SSID of the wifi network + */ void ESPMegaIoT::connectToWifi(char *ssid) { WiFi.begin(ssid); } + +/** + * @brief Disconnect from the wifi network + */ void ESPMegaIoT::disconnectFromWifi() { WiFi.disconnect(); } + +/** + * @brief Check if the wifi is connected + * + * @return True if the wifi is connected, false otherwise + */ bool ESPMegaIoT::wifiConnected() { return WiFi.status() == WL_CONNECTED; } + +/** + * @brief Connect to a MQTT broker with authentication + * + * @param client_id The client id to use + * @param mqtt_server The MQTT server to connect to + * @param mqtt_port The MQTT port to connect to + * @param mqtt_user The MQTT username to use + * @param mqtt_password The MQTT password to use + * @return True if the connection is successful, false otherwise + */ bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port, char *mqtt_user, char *mqtt_password) { mqtt.setServer(mqtt_server, mqtt_port); @@ -201,6 +299,15 @@ bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt mqtt_connected = false; return false; } + +/** + * @brief Connect to a MQTT broker without authentication + * + * @param client_id The client id to use + * @param mqtt_server The MQTT server to connect to + * @param mqtt_port The MQTT port to connect to + * @return True if the connection is successful, false otherwise + */ bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port) { ESP_LOGD("ESPMegaIoT", "Setting MQTT server to %s:%d", mqtt_server, mqtt_port); @@ -231,26 +338,51 @@ bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt mqtt_connected = false; return false; } + +/** + * @brief Disconnect from the MQTT broker + */ void ESPMegaIoT::disconnectFromMqtt() { mqtt.disconnect(); } + +/** + * @brief Publish a message to a topic + * + * @param topic The topic to publish to + * @param payload The payload to publish + */ void ESPMegaIoT::publish(const char *topic, const char *payload) { mqtt.publish(topic, payload); } +/** + * @brief Register a callback for MQTT messages + * + * @param callback The callback function + * @return The handler for the callback + */ uint8_t ESPMegaIoT::registerMqttCallback(std::function callback) { mqtt_callbacks[mqtt_callbacks_handler_index] = callback; return mqtt_callbacks_handler_index++; } +/** + * @brief Unregister a callback + * + * @param handler The handler of the callback + */ void ESPMegaIoT::unregisterMqttCallback(uint8_t handler) { mqtt_callbacks.erase(handler); } +/** + * @brief Subscribe to all components's topics + */ void ESPMegaIoT::mqttSubscribe() { ESP_LOGD("ESPMegaIoT", "Begin MQTT Subscription"); @@ -271,6 +403,9 @@ void ESPMegaIoT::mqttSubscribe() } } +/** + * @brief Publish relative to the base topic + */ void ESPMegaIoT::publishRelative(uint8_t card_id, char *topic, char *payload) { char absolute_topic[100]; @@ -278,6 +413,9 @@ void ESPMegaIoT::publishRelative(uint8_t card_id, char *topic, char *payload) mqtt.publish(absolute_topic, payload); } +/** + * @brief Subscribe relative to the base topic + */ bool ESPMegaIoT::mqttReconnect() { if (this->mqtt_config.mqtt_useauth) @@ -290,6 +428,10 @@ bool ESPMegaIoT::mqttReconnect() } } +/** + * @brief Keep the MQTT session alive + * @note This function is called automatically by the ESPMegaIoT object, You should not call this function directly + */ void ESPMegaIoT::sessionKeepAlive() { // This reconnect the MQTT if it disconnect. @@ -316,17 +458,36 @@ void ESPMegaIoT::sessionKeepAlive() } } +/** + * @brief Register a callback for MQTT messages relative to the base topic + * + * The message's base topic will be removed before calling the callback + * + * @param callback The callback function + * @return The handler for the callback + */ uint8_t ESPMegaIoT::registerRelativeMqttCallback(std::function callback) { mqtt_relative_callbacks[mqtt_relative_callbacks_handler_index] = callback; return mqtt_relative_callbacks_handler_index++; } +/** + * @brief Unregister a relative MQTT callback + * + * @param handler The handler of the callback + */ void ESPMegaIoT::unregisterRelativeMqttCallback(uint8_t handler) { mqtt_relative_callbacks.erase(handler); } +/** + * @brief Publish a message relative to the base topic + * + * @param topic The topic to publish to + * @param payload The payload to publish + */ void ESPMegaIoT::publishRelative(char *topic, char *payload) { char absolute_topic[100]; @@ -335,6 +496,11 @@ void ESPMegaIoT::publishRelative(char *topic, char *payload) mqtt.loop(); } +/** + * @brief Subscribe to a topic relative to the base topic + * + * @param topic The topic to subscribe to + */ void ESPMegaIoT::subscribeRelative(char *topic) { char absolute_topic[100]; @@ -343,22 +509,41 @@ void ESPMegaIoT::subscribeRelative(char *topic) mqtt.loop(); } +/** + * @brief Register a function to be called when the ESPMegaIoT object is subscribing to topics + * + * @param callback The callback function + * @return The handler for the callback + */ uint8_t ESPMegaIoT::registerSubscribeCallback(std::function callback) { subscribe_callbacks[subscribe_callbacks_handler_index] = callback; return subscribe_callbacks_handler_index++; } +/** + * @brief Unregister a subscribe callback + * + * @param handler The handler of the callback + */ void ESPMegaIoT::unregisterSubscribeCallback(uint8_t handler) { subscribe_callbacks.erase(handler); } +/** + * @brief Set the network config + * + * @param network_config The network config struct + */ void ESPMegaIoT::setNetworkConfig(NetworkConfig network_config) { this->network_config = network_config; } +/** + * @brief Load the network config from FRAM + */ void ESPMegaIoT::loadNetworkConfig() { // Load the network config from FRAM @@ -375,6 +560,9 @@ void ESPMegaIoT::loadNetworkConfig() fram->read(IOT_FRAM_ADDRESS + 87, (uint8_t *)network_config.password, 32); } +/** + * @brief Save the network config to FRAM + */ void ESPMegaIoT::saveNetworkConfig() { // Save the network config to FRAM @@ -391,11 +579,17 @@ void ESPMegaIoT::saveNetworkConfig() fram->write(IOT_FRAM_ADDRESS + 87, (uint8_t *)network_config.password, 32); } +/** + * @brief Begin the ethernet interface + */ void ESPMegaIoT::ethernetBegin() { ethernetIface->setHostname(network_config.hostname); } +/** + * @brief Load the MQTT config from FRAM + */ void ESPMegaIoT::loadMqttConfig() { // Load the mqtt config from FRAM @@ -409,6 +603,9 @@ void ESPMegaIoT::loadMqttConfig() this->base_topic_length = strlen(mqtt_config.base_topic); } +/** + * @brief Save the MQTT config to FRAM + */ void ESPMegaIoT::saveMqttConfig() { fram->write16(IOT_FRAM_ADDRESS + 128, mqtt_config.mqtt_port); @@ -419,6 +616,9 @@ void ESPMegaIoT::saveMqttConfig() fram->write(IOT_FRAM_ADDRESS + 227, (uint8_t *)mqtt_config.base_topic, 32); } +/** + * @brief Connect to MQTT with the current config + */ void ESPMegaIoT::connectToMqtt() { if (mqtt_config.mqtt_useauth) @@ -433,6 +633,9 @@ void ESPMegaIoT::connectToMqtt() } } +/** + * @brief Connect to the network using the current config + */ void ESPMegaIoT::connectNetwork() { if (network_config.useWifi) @@ -454,38 +657,78 @@ void ESPMegaIoT::connectNetwork() } } +/** + * @brief Set the MQTT config + * + * @param mqtt_config The MQTT config struct + */ void ESPMegaIoT::setMqttConfig(MqttConfig mqtt_config) { this->mqtt_config = mqtt_config; this->base_topic_length = strlen(mqtt_config.base_topic); } +/** + * @brief Bind an ethernet interface to the ESPMegaIoT object + * + * @param ethernetIface The ethernet interface to bind (ETH for ESPMegaPRO R3) + */ void ESPMegaIoT::bindEthernetInterface(ETHClass *ethernetIface) { this->ethernetIface = ethernetIface; } +/** + * @brief Get the IoTComponent object for a card + * + * @param card_id The id of the card + * @return The IoTComponent object for the card + */ IoTComponent *ESPMegaIoT::getComponent(uint8_t card_id) { return components[card_id]; } +/** + * @brief Get the network config + * + * @warning You should not modify the returned struct directly + * + * @return The network config struct + */ NetworkConfig *ESPMegaIoT::getNetworkConfig() { return &network_config; } +/** + * @brief Get the MQTT config + * + * @warning You should not modify the returned struct directly + * + * @return The MQTT config struct + */ MqttConfig *ESPMegaIoT::getMqttConfig() { return &mqtt_config; } +/** + * @brief Check if the MQTT is connected + * + * @return True if the MQTT is connected, false otherwise + */ bool ESPMegaIoT::mqttConnected() { //return mqtt_connected; return mqtt.connected(); } +/** + * @brief Check if the network is connected + * + * @return True if the network is connected, false otherwise + */ bool ESPMegaIoT::networkConnected() { if (network_config.useWifi) @@ -494,6 +737,12 @@ bool ESPMegaIoT::networkConnected() return ethernetIface->linkUp(); } +/** + * @brief Bind a FRAM object to the ESPMegaIoT object + * @note This class is hardcode to use the FRAM address 34-300 + * + * @param fram The FRAM object to bind + */ void ESPMegaIoT::bindFRAM(FRAM *fram) { this->fram = fram; diff --git a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.hpp b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.hpp index 9d59862..eec13d5 100644 --- a/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.hpp +++ b/ESPMegaPRO-firmware/lib/ESPMegaPRO/ESPMegaIoT.hpp @@ -15,6 +15,7 @@ #include #include +// MQTT Connection Parameters #define TCP_TIMEOUT_SEC 5 #define MQTT_RECONNECT_INTERVAL 30000 @@ -24,31 +25,48 @@ // Total of 267 bytes #define IOT_FRAM_ADDRESS 34 +/** + * @brief The network configuration struct + * @note This struct will be saved to FRAM when calling saveNetworkConfig + */ struct NetworkConfig { - IPAddress ip; - IPAddress gateway; - IPAddress subnet; - IPAddress dns1; - IPAddress dns2; - char hostname[32]; - bool useStaticIp; - bool useWifi; - bool wifiUseAuth; - char ssid[32]; - char password[32]; + IPAddress ip; ///< The IP address + IPAddress gateway; ///< The gateway address + IPAddress subnet; ///< The subnet mask + IPAddress dns1; ///< The primary DNS server + IPAddress dns2; ///< The secondary DNS server + char hostname[32]; ///< The hostname + bool useStaticIp; ///< Whether to use a static IP, if false, DHCP will be used + bool useWifi; ///< Whether to use WiFi or Ethernet, if false, Ethernet will be used + bool wifiUseAuth; ///< Whether to use WiFi authentication, if false, ssid and password will be ignored + char ssid[32]; ///< The WiFi SSID, even if wifiUseAuth is false, this should be set + char password[32]; ///< The WiFi password, even if wifiUseAuth is false, this should be set }; +/** + * @brief The MQTT configuration struct + * @note This struct will be saved to FRAM when calling saveMqttConfig + */ struct MqttConfig { - char mqtt_server[32]; - uint16_t mqtt_port; - char mqtt_user[32]; - char mqtt_password[32]; - bool mqtt_useauth; - char base_topic[32]; + char mqtt_server[32]; ///< The MQTT server address + uint16_t mqtt_port; ///< The MQTT server port + char mqtt_user[32]; ///< The MQTT username, even if mqtt_useauth is false, this should be set + char mqtt_password[32]; ///< The MQTT password, even if mqtt_useauth is false, this should be set + bool mqtt_useauth; ///< Whether to use MQTT authentication, if false, mqtt_user and mqtt_password will be ignored + char base_topic[32]; ///< The base topic for the MQTT messages }; +/** + * @brief The ESPMegaIoT class is a class that is used to interface with the ESPMegaPRO IoT module + * + * This class allows you to register IoT components and interface with them through MQTT. + * This class also manages the network and MQTT connections for you. + * Supports both WiFi and Ethernet. + * Also allows you to save and load network and MQTT configurations to and from FRAM. + * Also provides MQTT helpers for publishing and subscribing to topics. + */ class ESPMegaIoT { public: @@ -63,7 +81,7 @@ public: void publishRelative(char *topic, char *payload); // Subscribe topic appended with base topic void subscribeRelative(char *topic); - void subscribeToTopic(char *topic); + void subscribe(char *topic); void unsubscribeFromTopic(char *topic); void connectToWifi(char *ssid, char *password); void connectToWifi(char *ssid);