diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/DigitalOutputRTU.cpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/DigitalOutputRTU.cpp new file mode 100644 index 0000000..11f5cb1 --- /dev/null +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/DigitalOutputRTU.cpp @@ -0,0 +1,145 @@ +#include +DigitalOutputRTU::DigitalOutputRTU() +{ + // constructor implementation +} + +DigitalOutputRTU::~DigitalOutputRTU() +{ + // destructor implementation +} + +void DigitalOutputRTU::begin(char *remoteBaseTopic, uint8_t remote_card_slot, ESPMegaIoT *iot) +{ + // begin implementation + this->remoteBaseTopic = remoteBaseTopic; + this->remoteBaseTopicLength = strlen(remoteBaseTopic); + this->cardSlot = remote_card_slot; + this->iot = iot; + +} + +void DigitalOutputRTU::subscribe() +{ + // Subscribe to all pins + // State Topic format: ///state + // Value Topic format: ///value + char buffer[100]; + for (uint8_t i = 0; i < 16; i++) + { + sprintf(buffer, "%s/%02d/%02d/state", this->remoteBaseTopic, this->cardSlot, i); + this->iot->subscribe(buffer); + sprintf(buffer, "%s/%02d/%02d/value", this->remoteBaseTopic, this->cardSlot, i); + this->iot->subscribe(buffer); + } +} + +void DigitalOutputRTU::digitalWrite(uint8_t pin, bool state) +{ + // Publish state to topic ///set/state + // Publish 4095 to topic ///set/value + char buffer[100]; + sprintf(buffer, "%s/%02d/%02d/set/state", this->remoteBaseTopic, this->cardSlot, pin); + this->iot->publish(buffer, state ? "1" : "0"); + sprintf(buffer, "%s/%02d/%02d/set/value", this->remoteBaseTopic, this->cardSlot, pin); + this->iot->publish(buffer, "4095"); +} + +void DigitalOutputRTU::analogWrite(uint8_t pin, uint16_t value) +{ + // Publish 1 to topic ///set/state + // Publish value to topic ///set/value + char buffer[100]; + sprintf(buffer, "%s/%02d/%02d/set/state", this->remoteBaseTopic, this->cardSlot, pin); + this->iot->publish(buffer, "1"); + sprintf(buffer, "%s/%02d/%02d/set/value", this->remoteBaseTopic, this->cardSlot, pin); + char valueBuffer[10]; + itoa(value, valueBuffer, 10); + this->iot->publish(buffer, valueBuffer); +} + +void DigitalOutputRTU::setState(uint8_t pin, bool state) +{ + // Publish state to topic ///set/state + char buffer[100]; + sprintf(buffer, "%s/%02d/%02d/set/state", this->remoteBaseTopic, this->cardSlot, pin); + this->iot->publish(buffer, state ? "1" : "0"); +} + +void DigitalOutputRTU::setValue(uint8_t pin, uint16_t value) +{ + // Publish value to topic ///set/value + char buffer[100]; + sprintf(buffer, "%s/%02d/%02d/set/value", this->remoteBaseTopic, this->cardSlot, pin); + char valueBuffer[10]; + itoa(value, valueBuffer, 10); + this->iot->publish(buffer, valueBuffer); +} + +bool DigitalOutputRTU::getState(uint8_t pin) +{ + return this->outputStateBuffer[pin]; +} + +uint16_t DigitalOutputRTU::getValue(uint8_t pin) +{ + return this->outputValueBuffer[pin]; +} + +uint8_t DigitalOutputRTU::registerChangeCallback(std::function callback) +{ + this->change_callbacks[callbackCount] = callback; + return callbackCount++; +} + +void DigitalOutputRTU::unregisterChangeCallback(uint8_t handler) +{ + this->change_callbacks.erase(handler); +} + +void DigitalOutputRTU::mqttCallback(char *topic, char *payload) { + // First trim base topic from topic + char* topicWithoutBase = topic + this->remoteBaseTopicLength; + // Then check if topic is in format ///state or ///value + // Example trimmed topic: /01/00/state, /01/00/value + // Card slot range from 00 to 99 + // Pin range from 00 to 15 + // Note that both card and pin are padded with 0 if they are less than 10 + if(strlen(topicWithoutBase) != 12) + return; + if(topicWithoutBase[0] != '/') + return; + if(topicWithoutBase[3] != '/') + return; + if(topicWithoutBase[6] != '/') + return; + // Extract card slot and pin + uint8_t cardSlot = (topicWithoutBase[1] - '0') * 10 + (topicWithoutBase[2] - '0'); + // Check if card slot is correct + if(cardSlot != this->cardSlot) + return; + uint8_t pin = (topicWithoutBase[4] - '0') * 10 + (topicWithoutBase[5] - '0'); + // Extract value + uint16_t value = atoi(payload); + // Is it state or value? + if(!strcmp(topicWithoutBase + 7, "state")) + { + // Update state buffer + this->outputStateBuffer[pin] = strcmp(payload, "1") == 0; + // Call callbacks + for(auto const& callback : this->change_callbacks) + { + callback.second(pin, this->outputStateBuffer[pin], this->outputValueBuffer[pin]); + } + } + else if(!strcmp(topicWithoutBase + 7, "value")) + { + // Update value buffer + this->outputValueBuffer[pin] = value; + // Call callbacks + for(auto const& callback : this->change_callbacks) + { + callback.second(pin, this->outputStateBuffer[pin], this->outputValueBuffer[pin]); + } + } +} \ No newline at end of file diff --git a/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/DigitalOutputRTU.hpp b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/DigitalOutputRTU.hpp new file mode 100644 index 0000000..d908a04 --- /dev/null +++ b/ESPMegaPRO-OS-SDK/lib/ESPMegaPRO/DigitalOutputRTU.hpp @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +class DigitalOutputRTU : public ESPMegaRTU +{ +public: + DigitalOutputRTU(); + ~DigitalOutputRTU(); + void begin(char *remoteBaseTopic, uint8_t remote_card_slot, ESPMegaIoT *iot); + void subscribe(); + void digitalWrite(uint8_t pin, bool state); + void analogWrite(uint8_t pin, uint16_t value); + void setState(uint8_t pin, bool state); + void setValue(uint8_t pin, uint16_t value); + bool getState(uint8_t pin); + uint16_t getValue(uint8_t pin); + uint8_t registerChangeCallback(std::function callback); + void unregisterChangeCallback(uint8_t handler); + +private: + void mqttCallback(char *topic, char *payload); + ESPMegaIoT *iot; + size_t size; + uint16_t callbackCount; + std::map> change_callbacks; + bool outputStateBuffer[16]; + uint16_t outputValueBuffer[16]; +}; \ No newline at end of file