diff --git a/Template Project/lib/ESPMegaPRO/AnalogIoT.hpp b/Template Project/lib/ESPMegaPRO/AnalogIoT.hpp index 6514569..79fe92e 100644 --- a/Template Project/lib/ESPMegaPRO/AnalogIoT.hpp +++ b/Template Project/lib/ESPMegaPRO/AnalogIoT.hpp @@ -11,6 +11,7 @@ class AnalogIoT : public IoTComponent { void setADCsPublishInterval(uint32_t interval); void setADCsPublishEnabled(bool enabled); void publishReport(); + void subscribe(); uint8_t getType(); private: char *adc_topic; diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp index 6535f55..7221bd7 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp @@ -168,4 +168,26 @@ void DigitalOutputIoT::publishReport() { uint8_t DigitalOutputIoT::getType() { return CARD_TYPE_DIGITAL_OUTPUT; +} + +void DigitalOutputIoT::subscribe() { + // Subscribe to all set state topics + for(int i = 1; i <= 16; i++) { + char topic[10]; + sprintf(topic, "%02d/set/state", i); + subscribeRelative(topic); + } + // Subscribe to all set value topics + for(int i = 1; i <= 16; i++) { + char topic[10]; + sprintf(topic, "%02d/set/value", i); + subscribeRelative(topic); + } + // Subscribe to request state topic + subscribeRelative(REQUEST_STATE_TOPIC); + // Subscribe to publish enable topic + subscribeRelative(PUBLISH_ENABLE_TOPIC); +} + +void DigitalOutputIoT::loop() { } \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.hpp b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.hpp index 1ac761b..a166998 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.hpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.hpp @@ -18,6 +18,8 @@ class DigitalOutputIoT : public IoTComponent { void registerValueChangeCallback(void (*callback)(uint8_t, bool, uint16_t)); void deregisterValueChangeCallback(); void publishReport(); + void subscribe(); + void loop(); uint8_t getType(); private: bool digital_outputs_publish_enabled = false; diff --git a/Template Project/lib/ESPMegaPRO/ESPMegaIoT.cpp b/Template Project/lib/ESPMegaPRO/ESPMegaIoT.cpp index 6e3de7e..c0bf4d0 100644 --- a/Template Project/lib/ESPMegaPRO/ESPMegaIoT.cpp +++ b/Template Project/lib/ESPMegaPRO/ESPMegaIoT.cpp @@ -1,5 +1,14 @@ #include +ESPMegaIoT::ESPMegaIoT() { + // Initialize the components array + for (int i = 0; i < 255; i++) { + components[i] = NULL; + } + active = false; + mqtt_connected = false; +} + void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length) { // Create a null terminated string from the payload memcpy(payload_buffer, payload, length); @@ -17,6 +26,12 @@ void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length) { return; } components[card_id]->handleMqttMessage(topic_without_base + 3, payload_buffer); + if (user_relative_mqtt_callback != NULL) { + user_relative_mqtt_callback(topic_without_base + 3, payload_buffer); + } + if (user_mqtt_callback != NULL) { + user_mqtt_callback(topic, payload_buffer); + } } void ESPMegaIoT::setBaseTopic(char *base_topic) { @@ -24,10 +39,23 @@ void ESPMegaIoT::setBaseTopic(char *base_topic) { base_topic_length = strlen(base_topic); } -void ESPMegaIoT::begin(ExpansionCard *cards[]) { +void ESPMegaIoT::intr_begin(ExpansionCard *cards[]) { this->cards = cards; + ETH.begin(); + active = true; +} +void ESPMegaIoT::loop() { + if(!active) + return; + // Call each component's loop function + for (int i = 0; i < 255; i++) { + if (components[i] != NULL) { + components[i]->loop(); + } + } + mqtt.loop(); + sessionKeepAlive(); } -void ESPMegaIoT::loop(); // Register Existing Card for use with IoT void ESPMegaIoT::registerCard(uint8_t card_id) { @@ -54,11 +82,6 @@ void ESPMegaIoT::registerCard(uint8_t card_id) { default: return; } - // Initialize the IoT component - - - // Enable publishing for the card - card_publish_enabled[card_id] = true; } void ESPMegaIoT::deregisterCard(uint8_t card_id) { // Check if the card is registered @@ -68,8 +91,6 @@ void ESPMegaIoT::deregisterCard(uint8_t card_id) { // Delete the IoT component delete components[card_id]; components[card_id] = NULL; - // Disable publishing for the card - card_publish_enabled[card_id] = false; } void ESPMegaIoT::publishCard(uint8_t card_id) { // Check if the card is registered @@ -103,13 +124,114 @@ void ESPMegaIoT::disconnectFromWifi() { bool ESPMegaIoT::wifiConnected() { return WiFi.status() == WL_CONNECTED; } -void 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) { + // Store mqtt connection parameters + this->mqtt_server = mqtt_server; + this->mqtt_port = mqtt_port; + this->mqtt_user = mqtt_user; + this->mqtt_password = mqtt_password; + this-> mqtt_useauth = true; + this->client_id = client_id; mqtt.setServer(mqtt_server, mqtt_port); - mqtt.setCallback(mqttCallback); - mqtt.connect(client_id, mqtt_user, mqtt_password); - sessionKeepAlive(); + auto boundCallback = std::bind(&ESPMegaIoT::mqttCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + mqtt.setCallback(boundCallback); + if (mqtt.connect(client_id, mqtt_user, mqtt_password)) { + sessionKeepAlive(); + mqttSubscribe(); + mqtt_connected = true; + return true; + } + mqtt_connected = false; + return false; } -void ESPMegaIoT::connectToMqtt(char* client_id, char *mqtt_server, uint16_t mqtt_port); -void ESPMegaIoT::disconnectFromMqtt(); -void ESPMegaIoT::publishToTopic(char *topic, char *payload); -void ESPMegaIoT::registerMqttCallback(void (*callback)(char *, char *)); +bool ESPMegaIoT::connectToMqtt(char* client_id, char *mqtt_server, uint16_t mqtt_port) { + // Store mqtt connection parameters + this->mqtt_server = mqtt_server; + this->mqtt_port = mqtt_port; + this->mqtt_useauth = false; + this->client_id = client_id; + mqtt.setServer(mqtt_server, mqtt_port); + auto boundCallback = std::bind(&ESPMegaIoT::mqttCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + mqtt.setCallback(boundCallback); + if(mqtt.connect(client_id)) { + sessionKeepAlive(); + mqttSubscribe(); + mqtt_connected = true; + return true; + } + mqtt_connected = false; + return false; + + +} +void ESPMegaIoT::disconnectFromMqtt() { + mqtt.disconnect(); +} +void ESPMegaIoT::publishToTopic(char *topic, char *payload) { + mqtt.publish(topic, payload); +} +void ESPMegaIoT::registerMqttCallback(void (*callback)(char *, char *)) { +} +void ESPMegaIoT::mqttSubscribe() { + // Subscribe to all topics + for (int i = 0; i < 255; i++) { + if (components[i] != NULL) { + components[i]->subscribe(); + } + } +} + +void ESPMegaIoT::publishRelative(uint8_t card_id, char *topic, char *payload) { + char absolute_topic[100]; + sprintf(absolute_topic, "%s/%d/%s", base_topic, card_id, topic); + mqtt.publish(absolute_topic, payload); +} + +void ESPMegaIoT::setETHStaticIp(IPAddress ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2) { + ETH.config(ip, gateway, subnet, dns1, dns2); +} +void ESPMegaIoT::setETHStaticIp(IPAddress ip, IPAddress gateway, IPAddress subnet, IPAddress dns1) { + ETH.config(ip, gateway, subnet, dns1); +} +void ESPMegaIoT::setETHStaticIp(IPAddress ip, IPAddress gateway, IPAddress subnet) { + ETH.config(ip, gateway, subnet); +} +IPAddress ESPMegaIoT::getETHIp() { + return ETH.localIP(); +} + +bool ESPMegaIoT::mqttReconnect() { + if (mqtt_useauth) { + return this->connectToMqtt(client_id, mqtt_server, mqtt_port, mqtt_user, mqtt_password); + } else { + return this->connectToMqtt(client_id, mqtt_server, mqtt_port); + } +} + +void ESPMegaIoT::sessionKeepAlive() { + static unsigned long lastSessionKeepAlive = 0; + if (millis() - lastSessionKeepAlive > 30000) { + lastSessionKeepAlive = millis(); + // Check if mqtt is connected + if (!mqtt.connected()) { + // Try to reconnect + mqtt_connected = mqttReconnect(); + } + } +} + +void ESPMegaIoT::registerRelativeMqttCallback(void (*callback)(char *, char *)) { + user_relative_mqtt_callback = callback; +} + +void ESPMegaIoT::publishRelative(char *topic, char *payload) { + char absolute_topic[100]; + sprintf(absolute_topic, "%s/%s", base_topic, topic); + mqtt.publish(absolute_topic, payload); +} + +void ESPMegaIoT::subscribeRelative(char *topic) { + char absolute_topic[100]; + sprintf(absolute_topic, "%s/%s", base_topic, topic); + mqtt.subscribe(absolute_topic); +} \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/ESPMegaIoT.hpp b/Template Project/lib/ESPMegaPRO/ESPMegaIoT.hpp index 8ad4487..baf7779 100644 --- a/Template Project/lib/ESPMegaPRO/ESPMegaIoT.hpp +++ b/Template Project/lib/ESPMegaPRO/ESPMegaIoT.hpp @@ -11,37 +11,58 @@ class ESPMegaIoT { - public: - void begin(ExpansionCard *cards[]); - void loop(); - void registerCard(uint8_t card_id); - void deregisterCard(uint8_t card_id); - void publishCard(uint8_t card_id); - void subscribeToTopic(char *topic); - void unsubscribeFromTopic(char *topic); - void connectToEthernet(); - bool ethernetConnected(); - void connectToWifi(char *ssid, char *password); - void connectToWifi(char *ssid); - void disconnectFromWifi(); - bool wifiConnected(); - void connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port, char *mqtt_user, char *mqtt_password); - void connectToMqtt(char* client_id, char *mqtt_server, uint16_t mqtt_port); - void disconnectFromMqtt(); - void publishToTopic(char *topic, char *payload); - void registerMqttCallback(void (*callback)(char *, char *)); - void setBaseTopic(char *base_topic); - private: - void sessionKeepAlive(); - void mqttReconnect(); - void wifiReconnect(); - void mqttCallback(char *topic, byte *payload, unsigned int length); - void publishRelative(uint8_t card_id, char *topic, char *payload); - PubSubClient mqtt; - IoTComponent *components[255]; - bool card_publish_enabled[255]; - char payload_buffer[200]; - char base_topic[100]; - uint8_t base_topic_length; - ExpansionCard **cards; // Points to card array in ESPMegaPRO Core +public: + void intr_begin(ExpansionCard *cards[]); + void loop(); + void registerCard(uint8_t card_id); + void deregisterCard(uint8_t card_id); + void publishCard(uint8_t card_id); + // Publish topic appended with base topic + void publishRelative(char *topic, char *payload); + // Subscribe topic appended with base topic + void subscribeRelative(char *topic); + void subscribeToTopic(char *topic); + void unsubscribeFromTopic(char *topic); + void connectToEthernet(); + bool ethernetConnected(); + void connectToWifi(char *ssid, char *password); + void connectToWifi(char *ssid); + void disconnectFromWifi(); + bool wifiConnected(); + bool connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port, char *mqtt_user, char *mqtt_password); + bool connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port); + void disconnectFromMqtt(); + void publishToTopic(char *topic, char *payload); + void registerMqttCallback(void (*callback)(char *, char *)); + void registerRelativeMqttCallback(void (*callback)(char *, char *)); + void setBaseTopic(char *base_topic); + void setETHStaticIp(IPAddress ip, IPAddress gateway, IPAddress subnet, IPAddress dns1, IPAddress dns2); + void setETHStaticIp(IPAddress ip, IPAddress gateway, IPAddress subnet, IPAddress dns1); + void setETHStaticIp(IPAddress ip, IPAddress gateway, IPAddress subnet); + IPAddress getETHIp(); + +private: + void sessionKeepAlive(); + bool mqttReconnect(); + void wifiReconnect(); + void mqttSubscribe(); + void mqttCallback(char *topic, byte *payload, unsigned int length); + void (*user_mqtt_callback)(char *, char *); + void (*user_relative_mqtt_callback)(char *, char *); + void publishRelative(uint8_t card_id, char *topic, char *payload); + bool active; + PubSubClient mqtt; + IoTComponent *components[255]; + char payload_buffer[200]; + char base_topic[100]; + uint8_t base_topic_length; + ExpansionCard **cards; // Points to card array in ESPMegaPRO Core + // MQTT Connection Parameters + char *mqtt_server; + uint16_t mqtt_port; + char *mqtt_user; + char *mqtt_password; + char *client_id; + bool mqtt_useauth; + bool mqtt_connected; }; \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/ESPMegaPRO_OOP.cpp b/Template Project/lib/ESPMegaPRO/ESPMegaPRO_OOP.cpp index f800c1d..5e4a4c4 100644 --- a/Template Project/lib/ESPMegaPRO/ESPMegaPRO_OOP.cpp +++ b/Template Project/lib/ESPMegaPRO/ESPMegaPRO_OOP.cpp @@ -27,7 +27,8 @@ void ESPMegaPRO::loop() { if (cardInstalled[i]) { cards[i]->loop(); } - } + } + iot.loop(); } bool ESPMegaPRO::installCard(uint8_t slot, ExpansionCard* card) { if (slot > 255) return false; @@ -88,3 +89,6 @@ void ESPMegaPRO::setTime(int hours, int minutes, int seconds, int day, int month RTC.write(timeElement); } +void ESPMegaPRO::enableIotModule() { + iot.intr_begin(cards); +} \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp b/Template Project/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp index e23c5fa..fb65de2 100644 --- a/Template Project/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp +++ b/Template Project/lib/ESPMegaPRO/ESPMegaPRO_OOP.hpp @@ -32,6 +32,7 @@ class ESPMegaPRO { void loop(); bool installCard(uint8_t slot, ExpansionCard* card); bool updateTimeFromNTP(); + void enableIotModule(); rtctime_t getTime(); void setTime(int hours, int minutes, int seconds, int day, int month, int year); FRAM fram; diff --git a/Template Project/lib/ESPMegaPRO/IoTComponent.cpp b/Template Project/lib/ESPMegaPRO/IoTComponent.cpp index ac7882f..3017e9f 100644 --- a/Template Project/lib/ESPMegaPRO/IoTComponent.cpp +++ b/Template Project/lib/ESPMegaPRO/IoTComponent.cpp @@ -8,4 +8,14 @@ void IoTComponent::publishRelative(char *topic, char *payload) { char absolute_topic[100]; sprintf(absolute_topic, "%s/%d/%s", base_topic, card_id, topic); mqtt->publish(absolute_topic, payload); +} + +void IoTComponent::subscribeRelative(char *topic) { + char absolute_topic[100]; + sprintf(absolute_topic, "%s/%d/%s", base_topic, card_id, topic); + mqtt->subscribe(absolute_topic); +} + +void IoTComponent::loop() { + // Placeholder, Do nothing } \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/IoTComponent.hpp b/Template Project/lib/ESPMegaPRO/IoTComponent.hpp index 7e9fc09..5159530 100644 --- a/Template Project/lib/ESPMegaPRO/IoTComponent.hpp +++ b/Template Project/lib/ESPMegaPRO/IoTComponent.hpp @@ -8,9 +8,12 @@ class IoTComponent { void setMqttClient(PubSubClient *mqtt); virtual void publishReport(); virtual uint8_t getType(); + virtual void subscribe(); + void loop(); protected: char *base_topic; void publishRelative(char *topic, char *payload); + void subscribeRelative(char *topic); PubSubClient *mqtt; uint8_t card_id; }; diff --git a/Template Project/src/iotdemo.cpp b/Template Project/src/iotdemo.cpp index efbb182..7c6851c 100644 --- a/Template Project/src/iotdemo.cpp +++ b/Template Project/src/iotdemo.cpp @@ -3,36 +3,35 @@ // Instantiate ESPMega ESPMegaPRO espmega = ESPMegaPRO(); +void mqtt_callback(char *topic, char *payload) +{ + Serial.print("MQTT Callback: "); + Serial.print(topic); + Serial.print(" "); + Serial.println(payload); +} + void setup() { // Initialize ESPMega espmega.begin(); Serial.println("ESPMega initialized"); + espmega.enableIotModule(); + espmega.iot.connectToEthernet(); + IPAddress ip(192, 168, 0, 11); + IPAddress gateway(192, 168, 0, 1); + IPAddress subnet(255, 255, 255, 0); + espmega.iot.setETHStaticIp(ip, gateway, subnet); + espmega.iot.setBaseTopic("/testmegaoop"); + espmega.iot.connectToMqtt("espmega", "192.168.0.26", 1883); + espmega.iot.publishRelative("test", "test"); + espmega.iot.subscribeRelative("test"); + espmega.iot.registerMqttCallback(mqtt_callback); + Serial.println("ESPMega IoT initialized"); } void loop() { - // Read all the inputs and print them - for (int i = 0; i < 16; i++) - { - Serial.print("Input "); - Serial.print(i); - Serial.print(": "); - Serial.print(espmega.inputs.digitalRead(i)); - Serial.println(); - } - // Turn on all the outputs - for (int i = 0; i < 16; i++) - { - espmega.outputs.digitalWrite(i, true); - } - // Wait 1 second - delay(1000); - // Turn off all the outputs - for (int i = 0; i < 16; i++) - { - espmega.outputs.digitalWrite(i, false); - } - // Wait 1 second - delay(1000); + // Call ESPMega loop + espmega.loop(); } \ No newline at end of file