working digital output card implementation

This commit is contained in:
Siwat Sirichai 2023-12-28 17:03:47 +07:00
parent ca228f7cd0
commit 63a34afee3
7 changed files with 87 additions and 31 deletions

View File

@ -57,4 +57,16 @@ uint16_t DigitalOutputCard::getValue(uint8_t pin) {
// Get type of card
uint8_t DigitalOutputCard::getType() {
return CARD_TYPE_DIGITAL_OUTPUT;
}
}
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);
}

View File

@ -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

View File

@ -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 <pin>/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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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);
}