IoT Comment

This commit is contained in:
Siwat Sirichai 2024-01-01 11:14:33 +07:00
parent 9a0f02fcd9
commit d75d098247
2 changed files with 287 additions and 20 deletions

View File

@ -1,6 +1,11 @@
#include <ESPMegaIoT.hpp> #include <ESPMegaIoT.hpp>
#include <ETH.h> #include <ETH.h>
/**
* @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) ESPMegaIoT::ESPMegaIoT() : mqtt(tcpClient)
{ {
tcpClient.setTimeout(TCP_TIMEOUT_SEC); tcpClient.setTimeout(TCP_TIMEOUT_SEC);
@ -13,10 +18,24 @@ ESPMegaIoT::ESPMegaIoT() : mqtt(tcpClient)
mqtt_connected = false; mqtt_connected = false;
} }
/**
* @brief Destroy the ESPMegaIoT object
*/
ESPMegaIoT::~ESPMegaIoT() 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) void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length)
{ {
// Create a null terminated string from the payload // 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); 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) void ESPMegaIoT::setBaseTopic(char *base_topic)
{ {
strcpy(this->mqtt_config.base_topic, base_topic); strcpy(this->mqtt_config.base_topic, base_topic);
base_topic_length = strlen(this->mqtt_config.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[]) void ESPMegaIoT::intr_begin(ExpansionCard *cards[])
{ {
this->cards = cards; this->cards = cards;
active = true; 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() void ESPMegaIoT::loop()
{ {
if (!active) if (!active)
@ -73,7 +108,14 @@ void ESPMegaIoT::loop()
sessionKeepAlive(); 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) void ESPMegaIoT::registerCard(uint8_t card_id)
{ {
// Check if the card is already registered // Check if the card is already registered
@ -127,6 +169,12 @@ void ESPMegaIoT::registerCard(uint8_t card_id)
return; return;
} }
} }
/**
* @brief Unregister a card
*
* @param card_id The id of the card
*/
void ESPMegaIoT::unregisterCard(uint8_t card_id) void ESPMegaIoT::unregisterCard(uint8_t card_id)
{ {
// Check if the card is registered // Check if the card is registered
@ -138,6 +186,10 @@ void ESPMegaIoT::unregisterCard(uint8_t card_id)
delete components[card_id]; delete components[card_id];
components[card_id] = NULL; components[card_id] = NULL;
} }
/**
* @brief Publish all cards's reports
*/
void ESPMegaIoT::publishCard(uint8_t card_id) void ESPMegaIoT::publishCard(uint8_t card_id)
{ {
// Check if the card is registered // Check if the card is registered
@ -148,30 +200,76 @@ void ESPMegaIoT::publishCard(uint8_t card_id)
// Publish the card // Publish the card
components[card_id]->publishReport(); 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); mqtt.subscribe(topic);
} }
/**
* @brief Unsubscribe from a topic
*
* @param topic The topic to unsubscribe from
*/
void ESPMegaIoT::unsubscribeFromTopic(char *topic) void ESPMegaIoT::unsubscribeFromTopic(char *topic)
{ {
mqtt.unsubscribe(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) void ESPMegaIoT::connectToWifi(char *ssid, char *password)
{ {
WiFi.begin(ssid, 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) void ESPMegaIoT::connectToWifi(char *ssid)
{ {
WiFi.begin(ssid); WiFi.begin(ssid);
} }
/**
* @brief Disconnect from the wifi network
*/
void ESPMegaIoT::disconnectFromWifi() void ESPMegaIoT::disconnectFromWifi()
{ {
WiFi.disconnect(); WiFi.disconnect();
} }
/**
* @brief Check if the wifi is connected
*
* @return True if the wifi is connected, false otherwise
*/
bool ESPMegaIoT::wifiConnected() bool ESPMegaIoT::wifiConnected()
{ {
return WiFi.status() == WL_CONNECTED; 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) 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); 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; mqtt_connected = false;
return 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) 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); 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; mqtt_connected = false;
return false; return false;
} }
/**
* @brief Disconnect from the MQTT broker
*/
void ESPMegaIoT::disconnectFromMqtt() void ESPMegaIoT::disconnectFromMqtt()
{ {
mqtt.disconnect(); 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) void ESPMegaIoT::publish(const char *topic, const char *payload)
{ {
mqtt.publish(topic, 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<void(char *, char *)> callback) uint8_t ESPMegaIoT::registerMqttCallback(std::function<void(char *, char *)> callback)
{ {
mqtt_callbacks[mqtt_callbacks_handler_index] = callback; mqtt_callbacks[mqtt_callbacks_handler_index] = callback;
return mqtt_callbacks_handler_index++; return mqtt_callbacks_handler_index++;
} }
/**
* @brief Unregister a callback
*
* @param handler The handler of the callback
*/
void ESPMegaIoT::unregisterMqttCallback(uint8_t handler) void ESPMegaIoT::unregisterMqttCallback(uint8_t handler)
{ {
mqtt_callbacks.erase(handler); mqtt_callbacks.erase(handler);
} }
/**
* @brief Subscribe to all components's topics
*/
void ESPMegaIoT::mqttSubscribe() void ESPMegaIoT::mqttSubscribe()
{ {
ESP_LOGD("ESPMegaIoT", "Begin MQTT Subscription"); 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) void ESPMegaIoT::publishRelative(uint8_t card_id, char *topic, char *payload)
{ {
char absolute_topic[100]; char absolute_topic[100];
@ -278,6 +413,9 @@ void ESPMegaIoT::publishRelative(uint8_t card_id, char *topic, char *payload)
mqtt.publish(absolute_topic, payload); mqtt.publish(absolute_topic, payload);
} }
/**
* @brief Subscribe relative to the base topic
*/
bool ESPMegaIoT::mqttReconnect() bool ESPMegaIoT::mqttReconnect()
{ {
if (this->mqtt_config.mqtt_useauth) 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() void ESPMegaIoT::sessionKeepAlive()
{ {
// This reconnect the MQTT if it disconnect. // 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<void(char *, char *)> callback) uint8_t ESPMegaIoT::registerRelativeMqttCallback(std::function<void(char *, char *)> callback)
{ {
mqtt_relative_callbacks[mqtt_relative_callbacks_handler_index] = callback; mqtt_relative_callbacks[mqtt_relative_callbacks_handler_index] = callback;
return mqtt_relative_callbacks_handler_index++; 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) void ESPMegaIoT::unregisterRelativeMqttCallback(uint8_t handler)
{ {
mqtt_relative_callbacks.erase(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) void ESPMegaIoT::publishRelative(char *topic, char *payload)
{ {
char absolute_topic[100]; char absolute_topic[100];
@ -335,6 +496,11 @@ void ESPMegaIoT::publishRelative(char *topic, char *payload)
mqtt.loop(); mqtt.loop();
} }
/**
* @brief Subscribe to a topic relative to the base topic
*
* @param topic The topic to subscribe to
*/
void ESPMegaIoT::subscribeRelative(char *topic) void ESPMegaIoT::subscribeRelative(char *topic)
{ {
char absolute_topic[100]; char absolute_topic[100];
@ -343,22 +509,41 @@ void ESPMegaIoT::subscribeRelative(char *topic)
mqtt.loop(); 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<void(void)> callback) uint8_t ESPMegaIoT::registerSubscribeCallback(std::function<void(void)> callback)
{ {
subscribe_callbacks[subscribe_callbacks_handler_index] = callback; subscribe_callbacks[subscribe_callbacks_handler_index] = callback;
return subscribe_callbacks_handler_index++; return subscribe_callbacks_handler_index++;
} }
/**
* @brief Unregister a subscribe callback
*
* @param handler The handler of the callback
*/
void ESPMegaIoT::unregisterSubscribeCallback(uint8_t handler) void ESPMegaIoT::unregisterSubscribeCallback(uint8_t handler)
{ {
subscribe_callbacks.erase(handler); subscribe_callbacks.erase(handler);
} }
/**
* @brief Set the network config
*
* @param network_config The network config struct
*/
void ESPMegaIoT::setNetworkConfig(NetworkConfig network_config) void ESPMegaIoT::setNetworkConfig(NetworkConfig network_config)
{ {
this->network_config = network_config; this->network_config = network_config;
} }
/**
* @brief Load the network config from FRAM
*/
void ESPMegaIoT::loadNetworkConfig() void ESPMegaIoT::loadNetworkConfig()
{ {
// Load the network config from FRAM // 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); fram->read(IOT_FRAM_ADDRESS + 87, (uint8_t *)network_config.password, 32);
} }
/**
* @brief Save the network config to FRAM
*/
void ESPMegaIoT::saveNetworkConfig() void ESPMegaIoT::saveNetworkConfig()
{ {
// Save the network config to FRAM // 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); fram->write(IOT_FRAM_ADDRESS + 87, (uint8_t *)network_config.password, 32);
} }
/**
* @brief Begin the ethernet interface
*/
void ESPMegaIoT::ethernetBegin() void ESPMegaIoT::ethernetBegin()
{ {
ethernetIface->setHostname(network_config.hostname); ethernetIface->setHostname(network_config.hostname);
} }
/**
* @brief Load the MQTT config from FRAM
*/
void ESPMegaIoT::loadMqttConfig() void ESPMegaIoT::loadMqttConfig()
{ {
// Load the mqtt config from FRAM // Load the mqtt config from FRAM
@ -409,6 +603,9 @@ void ESPMegaIoT::loadMqttConfig()
this->base_topic_length = strlen(mqtt_config.base_topic); this->base_topic_length = strlen(mqtt_config.base_topic);
} }
/**
* @brief Save the MQTT config to FRAM
*/
void ESPMegaIoT::saveMqttConfig() void ESPMegaIoT::saveMqttConfig()
{ {
fram->write16(IOT_FRAM_ADDRESS + 128, mqtt_config.mqtt_port); 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); fram->write(IOT_FRAM_ADDRESS + 227, (uint8_t *)mqtt_config.base_topic, 32);
} }
/**
* @brief Connect to MQTT with the current config
*/
void ESPMegaIoT::connectToMqtt() void ESPMegaIoT::connectToMqtt()
{ {
if (mqtt_config.mqtt_useauth) if (mqtt_config.mqtt_useauth)
@ -433,6 +633,9 @@ void ESPMegaIoT::connectToMqtt()
} }
} }
/**
* @brief Connect to the network using the current config
*/
void ESPMegaIoT::connectNetwork() void ESPMegaIoT::connectNetwork()
{ {
if (network_config.useWifi) 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) void ESPMegaIoT::setMqttConfig(MqttConfig mqtt_config)
{ {
this->mqtt_config = mqtt_config; this->mqtt_config = mqtt_config;
this->base_topic_length = strlen(mqtt_config.base_topic); 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) void ESPMegaIoT::bindEthernetInterface(ETHClass *ethernetIface)
{ {
this->ethernetIface = 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) IoTComponent *ESPMegaIoT::getComponent(uint8_t card_id)
{ {
return components[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() NetworkConfig *ESPMegaIoT::getNetworkConfig()
{ {
return &network_config; 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() MqttConfig *ESPMegaIoT::getMqttConfig()
{ {
return &mqtt_config; return &mqtt_config;
} }
/**
* @brief Check if the MQTT is connected
*
* @return True if the MQTT is connected, false otherwise
*/
bool ESPMegaIoT::mqttConnected() bool ESPMegaIoT::mqttConnected()
{ {
//return mqtt_connected; //return mqtt_connected;
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() bool ESPMegaIoT::networkConnected()
{ {
if (network_config.useWifi) if (network_config.useWifi)
@ -494,6 +737,12 @@ bool ESPMegaIoT::networkConnected()
return ethernetIface->linkUp(); 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) void ESPMegaIoT::bindFRAM(FRAM *fram)
{ {
this->fram = fram; this->fram = fram;

View File

@ -15,6 +15,7 @@
#include <FRAM.h> #include <FRAM.h>
#include <map> #include <map>
// MQTT Connection Parameters
#define TCP_TIMEOUT_SEC 5 #define TCP_TIMEOUT_SEC 5
#define MQTT_RECONNECT_INTERVAL 30000 #define MQTT_RECONNECT_INTERVAL 30000
@ -24,31 +25,48 @@
// Total of 267 bytes // Total of 267 bytes
#define IOT_FRAM_ADDRESS 34 #define IOT_FRAM_ADDRESS 34
/**
* @brief The network configuration struct
* @note This struct will be saved to FRAM when calling saveNetworkConfig
*/
struct NetworkConfig struct NetworkConfig
{ {
IPAddress ip; IPAddress ip; ///< The IP address
IPAddress gateway; IPAddress gateway; ///< The gateway address
IPAddress subnet; IPAddress subnet; ///< The subnet mask
IPAddress dns1; IPAddress dns1; ///< The primary DNS server
IPAddress dns2; IPAddress dns2; ///< The secondary DNS server
char hostname[32]; char hostname[32]; ///< The hostname
bool useStaticIp; bool useStaticIp; ///< Whether to use a static IP, if false, DHCP will be used
bool useWifi; bool useWifi; ///< Whether to use WiFi or Ethernet, if false, Ethernet will be used
bool wifiUseAuth; bool wifiUseAuth; ///< Whether to use WiFi authentication, if false, ssid and password will be ignored
char ssid[32]; char ssid[32]; ///< The WiFi SSID, even if wifiUseAuth is false, this should be set
char password[32]; 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 struct MqttConfig
{ {
char mqtt_server[32]; char mqtt_server[32]; ///< The MQTT server address
uint16_t mqtt_port; uint16_t mqtt_port; ///< The MQTT server port
char mqtt_user[32]; char mqtt_user[32]; ///< The MQTT username, even if mqtt_useauth is false, this should be set
char mqtt_password[32]; char mqtt_password[32]; ///< The MQTT password, even if mqtt_useauth is false, this should be set
bool mqtt_useauth; bool mqtt_useauth; ///< Whether to use MQTT authentication, if false, mqtt_user and mqtt_password will be ignored
char base_topic[32]; 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 class ESPMegaIoT
{ {
public: public:
@ -63,7 +81,7 @@ public:
void publishRelative(char *topic, char *payload); void publishRelative(char *topic, char *payload);
// Subscribe topic appended with base topic // Subscribe topic appended with base topic
void subscribeRelative(char *topic); void subscribeRelative(char *topic);
void subscribeToTopic(char *topic); void subscribe(char *topic);
void unsubscribeFromTopic(char *topic); void unsubscribeFromTopic(char *topic);
void connectToWifi(char *ssid, char *password); void connectToWifi(char *ssid, char *password);
void connectToWifi(char *ssid); void connectToWifi(char *ssid);