From 63a34afee38409f8bac03b0ff8d89a5a3e520227 Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Thu, 28 Dec 2023 17:03:47 +0700 Subject: [PATCH] working digital output card implementation --- .../lib/ESPMegaPRO/DigitalOutputCard.cpp | 14 +++++- .../lib/ESPMegaPRO/DigitalOutputCard.hpp | 6 +++ .../lib/ESPMegaPRO/DigitalOutputIoT.cpp | 48 +++++++++++++++---- .../lib/ESPMegaPRO/ESPMegaIoT.cpp | 23 ++++++++- .../lib/ESPMegaPRO/IoTComponent.cpp | 8 ++-- .../lib/ESPMegaPRO/IoTComponent.hpp | 5 +- Template Project/src/iotdemo.cpp | 14 ------ 7 files changed, 87 insertions(+), 31 deletions(-) diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputCard.cpp b/Template Project/lib/ESPMegaPRO/DigitalOutputCard.cpp index 8c9b2d2..9186844 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputCard.cpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputCard.cpp @@ -57,4 +57,16 @@ uint16_t DigitalOutputCard::getValue(uint8_t pin) { // Get type of card uint8_t DigitalOutputCard::getType() { return CARD_TYPE_DIGITAL_OUTPUT; -} \ No newline at end of file +} + +void DigitalOutputCard::setState(uint8_t pin, bool state) { + this-> state_buffer[pin] = state; + this->pwm.setPin(pin, state*value_buffer[pin]); +} + +void DigitalOutputCard::setValue(uint8_t pin, uint16_t value) { + // If value is greater than 4095, set it to 4095 + if (value > 4095) value = 4095; + this-> value_buffer[pin] = value; + this->pwm.setPin(pin, state_buffer[pin]*value); +} diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputCard.hpp b/Template Project/lib/ESPMegaPRO/DigitalOutputCard.hpp index 48cf2b4..e2bac83 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputCard.hpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputCard.hpp @@ -32,9 +32,15 @@ public: // Dummy loop function void loop(); // Set the output to the specified state + // This function set both the state and the pwm value void digitalWrite(uint8_t pin, bool state); // Set the output to the specified pwm value + // This function set both the state and the pwm value void analogWrite(uint8_t pin, uint16_t value); + // Set the state of the specified pin + void setState(uint8_t pin, bool state); + // Set the pwm value of the specified pin + void setValue(uint8_t pin, uint16_t value); // Get the state of the specified pin bool getState(uint8_t pin); // Get the pwm value of the specified pin diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp index 90574d0..213b596 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp @@ -3,6 +3,7 @@ DigitalOutputIoT::DigitalOutputIoT() { this->state_report_topic = new char[10]; this->value_report_topic = new char[10]; + this->digital_outputs_publish_enabled = true; } DigitalOutputIoT::~DigitalOutputIoT() { @@ -22,6 +23,8 @@ bool DigitalOutputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient this->set_value_length = strlen(SET_VALUE_TOPIC); this->state_length = strlen(STATE_TOPIC); this->value_length = strlen(VALUE_TOPIC); + this->request_state_length = strlen(REQUEST_STATE_TOPIC); + this->publish_enable_length = strlen(PUBLISH_ENABLE_TOPIC); strcpy(this->state_report_topic, "00/state"); strcpy(this->value_report_topic, "00/value"); return true; @@ -36,10 +39,15 @@ bool DigitalOutputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient // Publish all topic: requeststate payload: N/A // Enable/disable publish topic: publish_enable payload: 0/1 void DigitalOutputIoT::handleMqttMessage(char *topic, char *payload) { + Serial.print("Handling mqtt message for digital output IoT component for card "); + Serial.println(card_id); + Serial.print("Topic: "); + Serial.println(topic); uint8_t topic_length = strlen(topic); if(this-> processSetStateMessage(topic, payload, topic_length)) return; if(this-> processSetValueMessage(topic, payload, topic_length)) return; if(this-> processRequestStateMessage(topic, payload, topic_length)) return; + Serial.println("No handler found"); } bool DigitalOutputIoT::processSetStateMessage(char *topic, char *payload, uint8_t topic_length) { @@ -50,26 +58,35 @@ bool DigitalOutputIoT::processSetStateMessage(char *topic, char *payload, uint8_ if(topic[2] != '/') { return false; } + Serial.println("Set state first check passed"); // The topic must be set_state_length + 2 characters long if(topic_length != set_state_length + 2) { return false; } + Serial.println("Set state second check passed"); // Check if the topic ends with /set/state if (!strncmp(topic+2, SET_STATE_TOPIC, set_state_length)) { + Serial.println("Set state third check passed"); // Get the pin number uint8_t pin = (topic[0] - '0')*10 + (topic[1] - '0'); + Serial.print("Pin: "); + Serial.println(pin); // Get the state bool state = false; + Serial.println(payload); char state_char = payload[0]; if (state_char == '0') { - bool state = false; + state = false; } else if (state_char == '1') { - bool state = true; + state = true; } else { return false; } + Serial.print("State: "); + Serial.println(state); // Set the state - card->digitalWrite(pin, state); + card->setState(pin, state); + publishDigitalOutputState(pin); return true; } return false; @@ -80,21 +97,26 @@ bool DigitalOutputIoT::processSetValueMessage(char *topic, char *payload, uint8_ // The correct format is /set/value // This mean that the topic must end with /set/value // Check if the 3rd character is / + Serial.println("Set value first check"); if(topic[2] != '/') { return false; } + Serial.println("Set value first check passed"); // The topic must be set_value_length + 2 characters long if(topic_length != set_value_length + 2) { return false; } + Serial.println("Set value second check passed"); // Check if the topic ends with /set/value if (!strncmp(topic+2, SET_VALUE_TOPIC, set_value_length)) { + Serial.println("Set value third check passed"); // Get the pin number uint8_t pin = (topic[0] - '0')*10 + (topic[1] - '0'); // Get the value uint16_t value = atoi(payload); // Set the value - card->analogWrite(pin, value); + card->setValue(pin, value); + publishDigitalOutputValue(pin); return true; } return false; @@ -105,11 +127,18 @@ bool DigitalOutputIoT::processRequestStateMessage(char *topic, char *payload, ui // The correct format is requeststate // This mean that the topic must be request_state_length characters long // The topic must be request_state_length characters long + Serial.println("Request state first check"); + Serial.print("Topic length: "); + Serial.println(topic_length); + Serial.print("Request state length: "); + Serial.println(request_state_length); if(topic_length != request_state_length) { return false; } + Serial.println("Request state first check passed"); // Check if the topic is requeststate if (!strncmp(topic, REQUEST_STATE_TOPIC, request_state_length)) { + Serial.println("Request state second check passed"); // Publish the state of all pins publishDigitalOutputs(); return true; @@ -119,7 +148,8 @@ bool DigitalOutputIoT::processRequestStateMessage(char *topic, char *payload, ui void DigitalOutputIoT::publishDigitalOutputs() { if(!digital_outputs_publish_enabled) return; - for(int i = 1; i <= 16; i++) { + Serial.println("Publishing digital outputs"); + for(int i = 0; i < 16; i++) { publishDigitalOutput(i); } } @@ -133,7 +163,7 @@ void DigitalOutputIoT::publishDigitalOutputState(uint8_t pin) { if(!digital_outputs_publish_enabled) return; state_report_topic[0] = pin / 10 + '0'; state_report_topic[1] = pin % 10 + '0'; - mqtt->publish(state_report_topic, card->getState(pin) ? "1" : "0"); + publishRelative(state_report_topic, card->getState(pin) ? "1" : "0"); } void DigitalOutputIoT::publishDigitalOutputValue(uint8_t pin) { @@ -142,7 +172,7 @@ void DigitalOutputIoT::publishDigitalOutputValue(uint8_t pin) { value_report_topic[1] = pin % 10 + '0'; char payload[5]; sprintf(payload, "%d", card->getValue(pin)); - mqtt->publish(value_report_topic, payload); + publishRelative(value_report_topic, payload); } void DigitalOutputIoT::setDigitalOutputsPublishEnabled(bool enabled) { @@ -177,13 +207,13 @@ void DigitalOutputIoT::subscribe() { char topic[20]; Serial.println("Subscribe to all set state topics"); // Subscribe to all set state topics - for(int i = 1; i <= 16; i++) { + for(int i = 0; i < 16; i++) { sprintf(topic, "%02d/set/state", i); subscribeRelative(topic); } Serial.println("Subscribe to all set value topics"); // Subscribe to all set value topics - for(int i = 1; i <= 16; i++) { + for(int i = 0; i < 16; i++) { sprintf(topic, "%02d/set/value", i); subscribeRelative(topic); } diff --git a/Template Project/lib/ESPMegaPRO/ESPMegaIoT.cpp b/Template Project/lib/ESPMegaPRO/ESPMegaIoT.cpp index ebc5eb9..16a7709 100644 --- a/Template Project/lib/ESPMegaPRO/ESPMegaIoT.cpp +++ b/Template Project/lib/ESPMegaPRO/ESPMegaIoT.cpp @@ -31,7 +31,11 @@ void ESPMegaIoT::mqttCallback(char *topic, byte *payload, unsigned int length) // 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) @@ -95,7 +99,7 @@ void ESPMegaIoT::registerCard(uint8_t card_id) if (mqtt_connected) { components[card_id]->subscribe(); - Serial.println("Back to register card"); + components[card_id]->publishReport(); } break; default: @@ -165,6 +169,14 @@ bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt { sessionKeepAlive(); mqttSubscribe(); + // Publish all cards + for (int i = 0; i < 255; i++) + { + if (components[i] != NULL) + { + components[i]->publishReport(); + } + } mqtt_connected = true; return true; } @@ -192,6 +204,15 @@ bool ESPMegaIoT::connectToMqtt(char *client_id, char *mqtt_server, uint16_t mqtt 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; } diff --git a/Template Project/lib/ESPMegaPRO/IoTComponent.cpp b/Template Project/lib/ESPMegaPRO/IoTComponent.cpp index b11a434..a4a7cc4 100644 --- a/Template Project/lib/ESPMegaPRO/IoTComponent.cpp +++ b/Template Project/lib/ESPMegaPRO/IoTComponent.cpp @@ -4,16 +4,16 @@ void IoTComponent::setMqttClient(PubSubClient *mqtt) { this->mqtt = mqtt; } -void IoTComponent::publishRelative(char *topic, char *payload) { +void IoTComponent::publishRelative(const char *topic, const char *payload) { char absolute_topic[100]; - sprintf(absolute_topic, "%s/%d/%s", base_topic, card_id, topic); + sprintf(absolute_topic, "%s/%02d/%s", base_topic, card_id, topic); mqtt->publish(absolute_topic, payload); } -void IoTComponent::subscribeRelative(char *topic) { +void IoTComponent::subscribeRelative(const char *topic) { Serial.println("Subscribe relative"); char absolute_topic[50]; - sprintf(absolute_topic, "%s/%d/%s", base_topic, card_id, topic); + sprintf(absolute_topic, "%s/%02d/%s", base_topic, card_id, topic); mqtt->subscribe(absolute_topic); } diff --git a/Template Project/lib/ESPMegaPRO/IoTComponent.hpp b/Template Project/lib/ESPMegaPRO/IoTComponent.hpp index 5159530..947bc79 100644 --- a/Template Project/lib/ESPMegaPRO/IoTComponent.hpp +++ b/Template Project/lib/ESPMegaPRO/IoTComponent.hpp @@ -9,11 +9,12 @@ class IoTComponent { 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); + void publishRelative(const char *topic, const char *payload); + void subscribeRelative(const char *topic); PubSubClient *mqtt; uint8_t card_id; }; diff --git a/Template Project/src/iotdemo.cpp b/Template Project/src/iotdemo.cpp index 3734239..d2846a0 100644 --- a/Template Project/src/iotdemo.cpp +++ b/Template Project/src/iotdemo.cpp @@ -19,28 +19,14 @@ void setup() { // Initialize ESPMega espmega.begin(); - Serial.println("ESPMega initialized"); - Serial.println("Initializing ESPMega IoT"); espmega.enableIotModule(); - Serial.println("ESPMega IoT module enabled"); - Serial.println("Setting static IP"); - Serial.println("Connecting to Ethernet"); ETH.begin(); ETH.config(ip, gateway, subnet); - Serial.println("Static IP set"); - Serial.println("Begin MQTT Initialization Routine"); - Serial.println("Setting MQTT Base Topic"); espmega.iot.setBaseTopic("/testmegaoop"); - Serial.println("Initializing MQTT"); espmega.iot.connectToMqtt("espmega", "192.168.0.26", 1883); - Serial.println("Publishing a test message"); espmega.iot.publishRelative("test", "test"); - Serial.println("Subscribing to test topic"); espmega.iot.subscribeRelative("test"); - Serial.println("Registering MQTT Callback"); espmega.iot.registerMqttCallback(mqtt_callback); - Serial.println("ESPMega IoT initialized"); - Serial.println("Enable IoT Module for Digital Output Card"); espmega.iot.registerCard(1); espmega.iot.publishCard(1); }