#include DigitalOutputIoT::DigitalOutputIoT() { this->state_report_topic = new char[10]; this->value_report_topic = new char[10]; } 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); strcpy(this->state_report_topic, "00/state"); strcpy(this->value_report_topic, "00/value"); 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() { if(!digital_outputs_publish_enabled) return; for(int i = 1; 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'; mqtt->publish(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)); mqtt->publish(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::registerValueChangeCallback(void (*callback)(uint8_t, bool, uint16_t)) { value_change_callback = callback; } void DigitalOutputIoT::deregisterValueChangeCallback() { 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 = 1; 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++) { 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() { }