From d3497b671e5e383e6a637f882a6408492f5b10b1 Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Thu, 28 Dec 2023 12:46:39 +0700 Subject: [PATCH] IoT Component --- Template Project/lib/ESPMegaPRO/AnalogIoT.hpp | 3 +- .../lib/ESPMegaPRO/DigitalInputIoT.hpp | 6 +- .../lib/ESPMegaPRO/DigitalOutputCard.cpp | 14 ++ .../lib/ESPMegaPRO/DigitalOutputCard.hpp | 22 +++ .../lib/ESPMegaPRO/DigitalOutputIoT.cpp | 127 ++++++++++++++++++ .../lib/ESPMegaPRO/DigitalOutputIoT.hpp | 25 +++- .../lib/ESPMegaPRO/IoTComponent.cpp | 5 + .../lib/ESPMegaPRO/IoTComponent.hpp | 13 +- 8 files changed, 209 insertions(+), 6 deletions(-) create mode 100644 Template Project/lib/ESPMegaPRO/IoTComponent.cpp diff --git a/Template Project/lib/ESPMegaPRO/AnalogIoT.hpp b/Template Project/lib/ESPMegaPRO/AnalogIoT.hpp index bd7ae09..a64a1b7 100644 --- a/Template Project/lib/ESPMegaPRO/AnalogIoT.hpp +++ b/Template Project/lib/ESPMegaPRO/AnalogIoT.hpp @@ -1,6 +1,7 @@ #pragma once +#include #include -class AnalogIoT { +class AnalogIoT : public IoTComponent { public: bool begin(AnalogCard *card); void handleMqttMessage(char *topic, char *payload); diff --git a/Template Project/lib/ESPMegaPRO/DigitalInputIoT.hpp b/Template Project/lib/ESPMegaPRO/DigitalInputIoT.hpp index 46ee16d..706bddc 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalInputIoT.hpp +++ b/Template Project/lib/ESPMegaPRO/DigitalInputIoT.hpp @@ -1,6 +1,9 @@ +#pragma once +#include #include -class DigitalInputIoT { + +class DigitalInputIoT : public IoTComponent { public: bool begin(DigitalInputCard *card); void handleMqttMessage(char *topic, char *payload); @@ -9,7 +12,6 @@ class DigitalInputIoT { void handleValueChange(uint8_t pin, uint8_t value); void registerValueChangeCallback(void (*callback)(uint8_t, uint8_t)); private: - char *digital_inputs_topic; bool digital_inputs_publish_enabled = false; DigitalInputCard *card; }; \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputCard.cpp b/Template Project/lib/ESPMegaPRO/DigitalOutputCard.cpp index fba7868..8c9b2d2 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputCard.cpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputCard.cpp @@ -27,6 +27,8 @@ bool DigitalOutputCard::begin() { // Set the output to the specified state void DigitalOutputCard::digitalWrite(uint8_t pin, bool state) { this->pwm.setPin(pin, state ? 4095 : 0); + this->state_buffer[pin] = state; + this->value_buffer[pin] = state ? 4095 : 0; } // Set the output to the specified pwm value void DigitalOutputCard::analogWrite(uint8_t pin, uint16_t value) { @@ -34,12 +36,24 @@ void DigitalOutputCard::analogWrite(uint8_t pin, uint16_t value) { if (value > 4095) value = 4095; // Set the pwm value this->pwm.setPin(pin, value); + this->state_buffer[pin] = value > 0; + this->value_buffer[pin] = value; } // Dummy loop function void DigitalOutputCard::loop() { } +// Get the state of the specified pin +bool DigitalOutputCard::getState(uint8_t pin) { + return this->state_buffer[pin]; +} + +// Get the pwm value of the specified pin +uint16_t DigitalOutputCard::getValue(uint8_t pin) { + return this->value_buffer[pin]; +} + // Get type of card uint8_t DigitalOutputCard::getType() { return CARD_TYPE_DIGITAL_OUTPUT; diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputCard.hpp b/Template Project/lib/ESPMegaPRO/DigitalOutputCard.hpp index 0ef1912..48cf2b4 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputCard.hpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputCard.hpp @@ -2,6 +2,22 @@ #include #include +// Protocol for digital output card +// Note that pin is always 2 characters long and padded with 0 if necessary +// Set pin state topic: /set/state payload: 0/1 +// Set pin pwm topic: /set/value payload: 0-4095 +// Publish pin state topic: /state payload: 0/1 +// Publish pin pwm topic: /value payload: 0-4095 +// Publish all topic: requeststate payload: N/A +// Enable/disable publish topic: publish_enable payload: 0/1 + +#define SET_STATE_TOPIC "/set/state" +#define SET_VALUE_TOPIC "/set/value" +#define STATE_TOPIC "/state" +#define VALUE_TOPIC "/value" +#define REQUEST_STATE_TOPIC "requeststate" +#define PUBLISH_ENABLE_TOPIC "publish_enable" + #define CARD_TYPE_DIGITAL_OUTPUT 0x00 class DigitalOutputCard : public ExpansionCard @@ -19,9 +35,15 @@ public: void digitalWrite(uint8_t pin, bool state); // Set the output to the specified pwm value void analogWrite(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 + uint16_t getValue(uint8_t pin); // Get type of card uint8_t getType(); private: Adafruit_PWMServoDriver pwm; uint8_t address; + bool state_buffer[16]; + uint16_t value_buffer[16]; }; \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp index e69de29..b354c51 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.cpp @@ -0,0 +1,127 @@ +#include + +bool DigitalOutputIoT::begin(ExpansionCard *card) { + this->card = (DigitalOutputCard *) card; + this->set_state_length = strlen(SET_STATE_TOPIC); + this->set_value_length = strlen(SET_VALUE_TOPIC); + this->state_length = strlen(STATE_TOPIC); + this->value_length = strlen(VALUE_TOPIC); + return true; +} + +// Protocol for digital output card +// Note that pin is always 2 characters long and padded with 0 if necessary +// Set pin state topic: /set/state payload: 0/1 +// Set pin pwm topic: /set/value payload: 0-4095 +// Publish pin state topic: /state payload: 0/1 +// Publish pin pwm topic: /value payload: 0-4095 +// Publish all topic: requeststate payload: N/A +// Enable/disable publish topic: publish_enable payload: 0/1 +void DigitalOutputIoT::handleMqttMessage(char *topic, char *payload) { + 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; +} + +bool DigitalOutputIoT::processSetStateMessage(char *topic, char *payload, uint8_t topic_length) { + // Check if the topic is a set state topic + // The correct format is /set/state + // This mean that the topic must end with /set/state + // Check if the 3rd character is / + if(topic[2] != '/') { + return false; + } + // The topic must be set_state_length + 2 characters long + if(topic_length != set_state_length + 2) { + return false; + } + // Check if the topic ends with /set/state + if (!strncmp(topic+2, SET_STATE_TOPIC, set_state_length)) { + // Get the pin number + uint8_t pin = (topic[0] - '0')*10 + (topic[1] - '0'); + // Get the state + bool state = false; + char state_char = payload[0]; + if (state_char == '0') { + bool state = false; + } else if (state_char == '1') { + bool state = true; + } else { + return false; + } + // Set the state + card->digitalWrite(pin, state); + return true; + } + return false; +} + +bool DigitalOutputIoT::processSetValueMessage(char *topic, char *payload, uint8_t topic_length) { + // Check if the topic is a set value topic + // The correct format is /set/value + // This mean that the topic must end with /set/value + // Check if the 3rd character is / + if(topic[2] != '/') { + return false; + } + // The topic must be set_value_length + 2 characters long + if(topic_length != set_value_length + 2) { + return false; + } + // Check if the topic ends with /set/value + if (!strncmp(topic+2, SET_VALUE_TOPIC, set_value_length)) { + // 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); + return true; + } + return false; +} + +bool DigitalOutputIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) { + // Check if the topic is a request state topic + // 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 + if(topic_length != request_state_length) { + return false; + } + // Check if the topic is requeststate + if (!strncmp(topic, REQUEST_STATE_TOPIC, request_state_length)) { + // Publish the state of all pins + publishDigitalOutputs(); + return true; + } + return false; +} + +void DigitalOutputIoT::publishDigitalOutputs() { + +} + +void DigitalOutputIoT::publishDigitalOutput(uint8_t pin) { + char ]; +} + +void DigitalOutputIoT::setDigitalOutputsPublishEnabled(bool enabled) { + digital_outputs_publish_enabled = enabled; +} + +void DigitalOutputIoT::handleValueChange(uint8_t pin, bool value, uint16_t time) { + if (digital_outputs_publish_enabled) { + char payload[17]; + for (uint8_t i = 1; i <= 16; i++) { + payload[i - 1] = card->getDigitalOutput(i) ? '1' : '0'; + } + payload[16] = '\0'; + mqtt->publish(digital_outputs_topic, payload); + } +} + +void DigitalOutputIoT::registerValueChangeCallback(void (*callback)(uint8_t, bool, uint16_t)) { + card->registerDigitalOutputValueChangeCallback(callback); +} diff --git a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.hpp b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.hpp index adc2658..b92cee7 100644 --- a/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.hpp +++ b/Template Project/lib/ESPMegaPRO/DigitalOutputIoT.hpp @@ -1,4 +1,27 @@ #pragma once +#include #include +#include -class DigitalOutputIoT : p \ No newline at end of file +class DigitalOutputIoT : public IoTComponent { + public: + bool begin(ExpansionCard *card); + void handleMqttMessage(char *topic, char *payload); + void publishDigitalOutputs(); + void publishDigitalOutput(uint8_t pin); + void setDigitalOutputsPublishEnabled(bool enabled); + void handleValueChange(uint8_t pin, uint8_t value); + void registerValueChangeCallback(void (*callback)(uint8_t, bool, uint16_t)); + private: + bool digital_outputs_publish_enabled = false; + bool processSetStateMessage(char *topic, char *payload, uint8_t topic_length); + bool processSetValueMessage(char *topic, char *payload, uint8_t topic_length); + bool processRequestStateMessage(char *topic, char *payload, uint8_t topic_length); + DigitalOutputCard *card; + uint8_t set_state_length; + uint8_t set_value_length; + uint8_t state_length; + uint8_t value_length; + uint8_t request_state_length; + uint8_t publish_enable_length; +}; \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/IoTComponent.cpp b/Template Project/lib/ESPMegaPRO/IoTComponent.cpp new file mode 100644 index 0000000..e347523 --- /dev/null +++ b/Template Project/lib/ESPMegaPRO/IoTComponent.cpp @@ -0,0 +1,5 @@ +#include + +void IoTComponent::setMqttClient(PubSubClient *mqtt) { + this->mqtt = mqtt; +} \ No newline at end of file diff --git a/Template Project/lib/ESPMegaPRO/IoTComponent.hpp b/Template Project/lib/ESPMegaPRO/IoTComponent.hpp index e7310ac..582ffc8 100644 --- a/Template Project/lib/ESPMegaPRO/IoTComponent.hpp +++ b/Template Project/lib/ESPMegaPRO/IoTComponent.hpp @@ -1,2 +1,11 @@ - -class \ No newline at end of file +#pragma once +#include +#include +class IoTComponent { + public: + virtual bool begin(ExpansionCard *card); + virtual void handleMqttMessage(char *topic, char *payload); + void setMqttClient(PubSubClient *mqtt); + private: + PubSubClient *mqtt; +};