#include DigitalOutputIoT::DigitalOutputIoT() { this->state_report_topic = new char[10]; this->value_report_topic = new char[10]; this->digital_outputs_publish_enabled = true; } DigitalOutputIoT::~DigitalOutputIoT() { delete[] this->state_report_topic; delete[] this->value_report_topic; } bool DigitalOutputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) { Serial.print("Initializing digital output IoT component for card "); Serial.println(card_id); this->mqtt = mqtt; this->base_topic = base_topic; this->card = (DigitalOutputCard *) card; if(!card->begin()) return false; this-> card_id = card_id; 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); 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"); // Register callbacks auto bindedCallback = std::bind(&DigitalOutputIoT::handleValueChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); this->card->registerChangeCallback(bindedCallback); 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) { 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) { // 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; } 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') { state = false; } else if (state_char == '1') { state = true; } else { return false; } Serial.print("State: "); Serial.println(state); // Set the state card->setState(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 / 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->setValue(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 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; } return false; } void DigitalOutputIoT::publishDigitalOutputs() { if(!digital_outputs_publish_enabled) return; Serial.println("Publishing digital outputs"); for(int i = 0; i < 16; i++) { publishDigitalOutput(i); } } void DigitalOutputIoT::publishDigitalOutput(uint8_t pin) { publishDigitalOutputState(pin); publishDigitalOutputValue(pin); } 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'; publishRelative(state_report_topic, card->getState(pin) ? "1" : "0"); } void DigitalOutputIoT::publishDigitalOutputValue(uint8_t pin) { if(!digital_outputs_publish_enabled) return; value_report_topic[0] = pin / 10 + '0'; value_report_topic[1] = pin % 10 + '0'; char payload[5]; sprintf(payload, "%d", card->getValue(pin)); publishRelative(value_report_topic, payload); } void DigitalOutputIoT::setDigitalOutputsPublishEnabled(bool enabled) { digital_outputs_publish_enabled = enabled; } void DigitalOutputIoT::handleValueChange(uint8_t pin, bool state, uint16_t value) { publishDigitalOutput(pin); if(value_change_callback != NULL) { value_change_callback(pin, state, value); } } void DigitalOutputIoT::registerChangeCallback(std::function callback) { value_change_callback = callback; } void DigitalOutputIoT::deregisterChangeCallback() { value_change_callback = NULL; } void DigitalOutputIoT::publishReport() { publishDigitalOutputs(); } uint8_t DigitalOutputIoT::getType() { return CARD_TYPE_DIGITAL_OUTPUT; } void DigitalOutputIoT::subscribe() { Serial.println("Subscribing to digital output topics"); char topic[20]; Serial.println("Subscribe to all set state topics"); // Subscribe to all set state topics 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 = 0; i < 16; i++) { sprintf(topic, "%02d/set/value", i); subscribeRelative(topic); } Serial.println("Subscribe to request state topic"); // Subscribe to request state topic subscribeRelative(REQUEST_STATE_TOPIC); Serial.println("Subscribe to publish enable topic"); // Subscribe to publish enable topic subscribeRelative(PUBLISH_ENABLE_TOPIC); Serial.println("Subscriptions complete"); } void DigitalOutputIoT::loop() { }