#include ESPMegaIoT::ESPMegaIoT() : mqtt(tcpClient) { tcpClient.setTimeout(1); // 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); payload_buffer[length] = '\0'; // Remove the base topic from the topic char *topic_without_base = topic + strlen(base_topic) + 1; 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); } // Call the respective card's mqtt callback // Note that after the base topic, there should be the card id // /base_topic/card_id/... // First, get the card id in integer form Serial.print("Topic: "); Serial.println(topic_without_base); char *card_id_str = strtok(topic_without_base, "/"); Serial.print("Card ID: "); Serial.println(card_id_str); uint8_t card_id = atoi(card_id_str); // Check if the card is registered if (components[card_id] == NULL) { return; } components[card_id]->handleMqttMessage(topic_without_base + 3, payload_buffer); } void ESPMegaIoT::setBaseTopic(char *base_topic) { strcpy(this->base_topic, base_topic); base_topic_length = strlen(base_topic); } void ESPMegaIoT::intr_begin(ExpansionCard *cards[]) { this->cards = cards; 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(); } // Register Existing Card for use with IoT void ESPMegaIoT::registerCard(uint8_t card_id) { // Check if the card is already registered if (components[card_id] != NULL) { return; } // Get the card type uint8_t card_type = cards[card_id]->getType(); // Create the respective IoT component switch (card_type) { // case CARD_TYPE_ANALOG: // components[card_id] = new AnalogIoT(); // components[card_id]->begin(card_id, cards[card_id], &mqtt, base_topic); // break; // case CARD_TYPE_DIGITAL_INPUT: // components[card_id] = new DigitalInputIoT(); // components[card_id]->begin(card_id, cards[card_id], &mqtt, base_topic); // break; case CARD_TYPE_DIGITAL_OUTPUT: components[card_id] = new DigitalOutputIoT(); components[card_id]->begin(card_id, cards[card_id], &mqtt, base_topic); if (mqtt_connected) { components[card_id]->subscribe(); components[card_id]->publishReport(); } break; default: Serial.println("Invalid card type"); return; } Serial.println("Card registered"); } void ESPMegaIoT::deregisterCard(uint8_t card_id) { // Check if the card is registered if (components[card_id] == NULL) { return; } // Delete the IoT component delete components[card_id]; components[card_id] = NULL; } void ESPMegaIoT::publishCard(uint8_t card_id) { // Check if the card is registered if (components[card_id] == NULL) { return; } // Publish the card components[card_id]->publishReport(); } void ESPMegaIoT::subscribeToTopic(char *topic) { mqtt.subscribe(topic); } void ESPMegaIoT::unsubscribeFromTopic(char *topic) { mqtt.unsubscribe(topic); } void ESPMegaIoT::connectToWifi(char *ssid, char *password) { WiFi.begin(ssid, password); } void ESPMegaIoT::connectToWifi(char *ssid) { WiFi.begin(ssid); } void ESPMegaIoT::disconnectFromWifi() { WiFi.disconnect(); } bool ESPMegaIoT::wifiConnected() { return WiFi.status() == WL_CONNECTED; } 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); 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(); // Publish all cards for (int i = 0; i < 255; i++) { if (components[i] != NULL) { components[i]->publishReport(); } } mqtt_connected = true; return true; } mqtt_connected = false; return false; } bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt_port) { // Store mqtt connection parameters Serial.println("Storing mqtt connection parameters"); this->mqtt_server = mqtt_server; this->mqtt_port = mqtt_port; this->mqtt_useauth = false; this->client_id = client_id; Serial.println("Setting mqtt server"); mqtt.setServer(mqtt_server, mqtt_port); Serial.println("Setting mqtt callback"); auto boundCallback = std::bind(&ESPMegaIoT::mqttCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); mqtt.setCallback(boundCallback); Serial.println("Connecting to mqtt"); if (mqtt.connect(client_id)) { Serial.println("Connected to mqtt"); Serial.println("Calling session keep alive"); sessionKeepAlive(); Serial.println("Subscribing to topics"); mqttSubscribe(); Serial.println("Publishing all cards"); // Publish all cards for (int i = 0; i < 255; i++) { if (components[i] != NULL) { components[i]->publishReport(); } } mqtt_connected = true; return true; } Serial.println("Failed to connect to mqtt"); 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 *)) { user_mqtt_callback = callback; } void ESPMegaIoT::mqttSubscribe() { Serial.println("MQTT Subscribe Activated"); if (user_subscribe_callback != NULL) { user_subscribe_callback(); } // 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); } 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); } void ESPMegaIoT::registerSubscribeCallback(void (*callback)(void)) { user_subscribe_callback = callback; }