half comment done
This commit is contained in:
parent
d62ffa3606
commit
039e65e6df
|
@ -1,6 +1,14 @@
|
||||||
|
/**
|
||||||
|
* @file AnalogCard.cpp
|
||||||
|
* @brief Implementation of the AnalogCard class.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <AnalogCard.hpp>
|
#include <AnalogCard.hpp>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default constructor for the AnalogCard class.
|
||||||
|
*/
|
||||||
AnalogCard::AnalogCard() : dac0(DAC0_ADDRESS),
|
AnalogCard::AnalogCard() : dac0(DAC0_ADDRESS),
|
||||||
dac1(DAC1_ADDRESS),
|
dac1(DAC1_ADDRESS),
|
||||||
dac2(DAC2_ADDRESS),
|
dac2(DAC2_ADDRESS),
|
||||||
|
@ -12,6 +20,11 @@ AnalogCard::AnalogCard() : dac0(DAC0_ADDRESS),
|
||||||
this->handler_count = 0;
|
this->handler_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Writes a value to the specified DAC pin.
|
||||||
|
* @param pin The DAC pin to write to.
|
||||||
|
* @param value The value to write.
|
||||||
|
*/
|
||||||
void AnalogCard::dacWrite(uint8_t pin, uint16_t value)
|
void AnalogCard::dacWrite(uint8_t pin, uint16_t value)
|
||||||
{
|
{
|
||||||
ESP_LOGV("AnalogCard", "DAC Write: %d, %d", pin, value);
|
ESP_LOGV("AnalogCard", "DAC Write: %d, %d", pin, value);
|
||||||
|
@ -19,6 +32,11 @@ void AnalogCard::dacWrite(uint8_t pin, uint16_t value)
|
||||||
this->setDACValue(pin, value);
|
this->setDACValue(pin, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the state of the specified DAC pin.
|
||||||
|
* @param pin The DAC pin to set the state of.
|
||||||
|
* @param state The state to set (true = on, false = off).
|
||||||
|
*/
|
||||||
void AnalogCard::setDACState(uint8_t pin, bool state)
|
void AnalogCard::setDACState(uint8_t pin, bool state)
|
||||||
{
|
{
|
||||||
ESP_LOGD("AnalogCard", "Setting DAC state: %d, %d", pin, state);
|
ESP_LOGD("AnalogCard", "Setting DAC state: %d, %d", pin, state);
|
||||||
|
@ -30,6 +48,11 @@ void AnalogCard::setDACState(uint8_t pin, bool state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the value of the specified DAC pin.
|
||||||
|
* @param pin The DAC pin to set the value of.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
void AnalogCard::setDACValue(uint8_t pin, uint16_t value)
|
void AnalogCard::setDACValue(uint8_t pin, uint16_t value)
|
||||||
{
|
{
|
||||||
ESP_LOGD("AnalogCard", "Setting DAC value: %d, %d", pin, value);
|
ESP_LOGD("AnalogCard", "Setting DAC value: %d, %d", pin, value);
|
||||||
|
@ -40,18 +63,33 @@ void AnalogCard::setDACValue(uint8_t pin, uint16_t value)
|
||||||
callback.second(pin, this->dac_state[pin], value);
|
callback.second(pin, this->dac_state[pin], value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the value of the specified DAC pin.
|
||||||
|
* @param pin The DAC pin to get the value of.
|
||||||
|
* @return The value of the DAC pin.
|
||||||
|
*/
|
||||||
uint16_t AnalogCard::getDACValue(uint8_t pin)
|
uint16_t AnalogCard::getDACValue(uint8_t pin)
|
||||||
{
|
{
|
||||||
return this->dac_value[pin];
|
return this->dac_value[pin];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the state of the specified DAC pin.
|
||||||
|
* @param pin The DAC pin to get the state of.
|
||||||
|
* @return The state of the DAC pin (true = on, false = off).
|
||||||
|
*/
|
||||||
bool AnalogCard::getDACState(uint8_t pin)
|
bool AnalogCard::getDACState(uint8_t pin)
|
||||||
{
|
{
|
||||||
return this->dac_state[pin];
|
return this->dac_state[pin];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sends data to the specified DAC pin.
|
||||||
|
* @param pin The DAC pin to send data to.
|
||||||
|
* @param value The data to send.
|
||||||
|
* @note This function does not call the DAC change callbacks.
|
||||||
|
*/
|
||||||
void AnalogCard::sendDataToDAC(uint8_t pin, uint16_t value)
|
void AnalogCard::sendDataToDAC(uint8_t pin, uint16_t value)
|
||||||
{
|
{
|
||||||
switch (pin)
|
switch (pin)
|
||||||
|
@ -70,6 +108,12 @@ void AnalogCard::sendDataToDAC(uint8_t pin, uint16_t value)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads the value from the specified analog pin.
|
||||||
|
* @param pin The analog pin to read from.
|
||||||
|
* @return The value read from the analog pin.
|
||||||
|
*/
|
||||||
uint16_t AnalogCard::analogRead(uint8_t pin)
|
uint16_t AnalogCard::analogRead(uint8_t pin)
|
||||||
{
|
{
|
||||||
if (pin >= 0 && pin <= 3)
|
if (pin >= 0 && pin <= 3)
|
||||||
|
@ -82,6 +126,11 @@ uint16_t AnalogCard::analogRead(uint8_t pin)
|
||||||
}
|
}
|
||||||
return 65535;
|
return 65535;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the AnalogCard.
|
||||||
|
* @return True if initialization is successful, false otherwise.
|
||||||
|
*/
|
||||||
bool AnalogCard::begin()
|
bool AnalogCard::begin()
|
||||||
{
|
{
|
||||||
if (!this->dac0.begin())
|
if (!this->dac0.begin())
|
||||||
|
@ -117,21 +166,38 @@ bool AnalogCard::begin()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The main loop of the AnalogCard.
|
||||||
|
* @note This function does nothing.
|
||||||
|
*/
|
||||||
void AnalogCard::loop()
|
void AnalogCard::loop()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the type of the AnalogCard.
|
||||||
|
* @return The type of the AnalogCard.
|
||||||
|
*/
|
||||||
uint8_t AnalogCard::getType()
|
uint8_t AnalogCard::getType()
|
||||||
{
|
{
|
||||||
return CARD_TYPE_ANALOG;
|
return CARD_TYPE_ANALOG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers a callback function to be called when the state or value of a DAC pin changes.
|
||||||
|
* @param callback The callback function to register.
|
||||||
|
* @return The handler ID of the registered callback.
|
||||||
|
*/
|
||||||
uint8_t AnalogCard::registerDACChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback)
|
uint8_t AnalogCard::registerDACChangeCallback(std::function<void(uint8_t, bool, uint16_t)> callback)
|
||||||
{
|
{
|
||||||
this->dac_change_callbacks[this->handler_count] = callback;
|
this->dac_change_callbacks[this->handler_count] = callback;
|
||||||
return this->handler_count++;
|
return this->handler_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregisters a previously registered DAC change callback.
|
||||||
|
* @param handler The handler ID of the callback to unregister.
|
||||||
|
*/
|
||||||
void AnalogCard::unregisterDACChangeCallback(uint8_t handler)
|
void AnalogCard::unregisterDACChangeCallback(uint8_t handler)
|
||||||
{
|
{
|
||||||
this->dac_change_callbacks.erase(handler);
|
this->dac_change_callbacks.erase(handler);
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
// Analog Card
|
||||||
#define CARD_TYPE_ANALOG 0x02
|
#define CARD_TYPE_ANALOG 0x02
|
||||||
|
|
||||||
|
// Analog Card FRAM Address
|
||||||
#define ANALOG_INPUT_BANK_A_ADDRESS 0x48
|
#define ANALOG_INPUT_BANK_A_ADDRESS 0x48
|
||||||
#define ANALOG_INPUT_BANK_B_ADDRESS 0x49
|
#define ANALOG_INPUT_BANK_B_ADDRESS 0x49
|
||||||
#define DAC0_ADDRESS 0x60
|
#define DAC0_ADDRESS 0x60
|
||||||
|
@ -31,6 +33,7 @@ class AnalogCard : public ExpansionCard {
|
||||||
uint8_t getType();
|
uint8_t getType();
|
||||||
private:
|
private:
|
||||||
uint8_t handler_count;
|
uint8_t handler_count;
|
||||||
|
// Map of handler IDs to callback functions
|
||||||
std::map<uint8_t, std::function<void(uint8_t, bool, uint16_t)>> dac_change_callbacks;
|
std::map<uint8_t, std::function<void(uint8_t, bool, uint16_t)>> dac_change_callbacks;
|
||||||
bool dac_state[4];
|
bool dac_state[4];
|
||||||
uint16_t dac_value[4];
|
uint16_t dac_value[4];
|
||||||
|
|
|
@ -1,5 +1,19 @@
|
||||||
|
/**
|
||||||
|
* @file AnalogIoT.cpp
|
||||||
|
* @brief Implementation of the AnalogIoT class.
|
||||||
|
*
|
||||||
|
* This file contains the implementation of the AnalogIoT class, which provides functionality for handling analog input and output operations in an IoT system.
|
||||||
|
* The class allows for setting the state and value of digital-to-analog converters (DACs), as well as reading the value of analog-to-digital converters (ADCs).
|
||||||
|
* It also supports publishing the state and value of DACs and ADCs over MQTT.
|
||||||
|
*/
|
||||||
#include <AnalogIoT.hpp>
|
#include <AnalogIoT.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default constructor for the AnalogIoT class.
|
||||||
|
*
|
||||||
|
* This constructor initializes the AnalogIoT object and sets up the ADC conversion callbacks.
|
||||||
|
*/
|
||||||
AnalogIoT::AnalogIoT() : adc_conversion_callbacks() {
|
AnalogIoT::AnalogIoT() : adc_conversion_callbacks() {
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
adc_publish_enabled[i] = false;
|
adc_publish_enabled[i] = false;
|
||||||
|
@ -8,8 +22,23 @@ AnalogIoT::AnalogIoT() : adc_conversion_callbacks() {
|
||||||
this->adc_conversion_callback_index = 0;
|
this->adc_conversion_callback_index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default destructor for the AnalogIoT class.
|
||||||
|
*/
|
||||||
AnalogIoT::~AnalogIoT() {
|
AnalogIoT::~AnalogIoT() {
|
||||||
|
this->adc_conversion_callbacks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the AnalogIoT object.
|
||||||
|
* @param card_id The ID of the card.
|
||||||
|
* @param card A pointer to the card object.
|
||||||
|
* @param mqtt A pointer to the MQTT client object.
|
||||||
|
* @param base_topic The base MQTT topic.
|
||||||
|
* @return True if the initialization was successful, false otherwise.
|
||||||
|
* @note This function can be called from the main program but it is recommended to use ESPMegaIoT to initialize the IoT Components.
|
||||||
|
* This function initializes the AnalogIoT object and registers the callbacks for handling DAC changes.
|
||||||
|
*/
|
||||||
bool AnalogIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
bool AnalogIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||||
this->mqtt = mqtt;
|
this->mqtt = mqtt;
|
||||||
this->base_topic = base_topic;
|
this->base_topic = base_topic;
|
||||||
|
@ -26,6 +55,11 @@ bool AnalogIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt,
|
||||||
this->card->registerDACChangeCallback(bindedCallback);
|
this->card->registerDACChangeCallback(bindedCallback);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the state of all DACs.
|
||||||
|
* @note This function is called when a request state message is received.
|
||||||
|
*/
|
||||||
void AnalogIoT::handleMqttMessage(char *topic, char *payload){
|
void AnalogIoT::handleMqttMessage(char *topic, char *payload){
|
||||||
uint8_t topic_length = strlen(topic);
|
uint8_t topic_length = strlen(topic);
|
||||||
if(this-> processDACSetStateMessage(topic, payload, topic_length)) return;
|
if(this-> processDACSetStateMessage(topic, payload, topic_length)) return;
|
||||||
|
@ -34,11 +68,20 @@ void AnalogIoT::handleMqttMessage(char *topic, char *payload){
|
||||||
if(this-> processADCSetConversionIntervalMessage(topic, payload, topic_length)) return;
|
if(this-> processADCSetConversionIntervalMessage(topic, payload, topic_length)) return;
|
||||||
if(this-> processADCSetConversionEnabledMessage(topic, payload, topic_length)) return;
|
if(this-> processADCSetConversionEnabledMessage(topic, payload, topic_length)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the state of all DACs.
|
||||||
|
*/
|
||||||
void AnalogIoT::publishADCs() {
|
void AnalogIoT::publishADCs() {
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
this->publishADC(i);
|
this->publishADC(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the state of a DAC.
|
||||||
|
* @param pin The pin of the DAC.
|
||||||
|
*/
|
||||||
void AnalogIoT::publishADC(uint8_t pin) {
|
void AnalogIoT::publishADC(uint8_t pin) {
|
||||||
if (this->adc_publish_enabled[pin]) {
|
if (this->adc_publish_enabled[pin]) {
|
||||||
uint16_t value = this->card->analogRead(pin);
|
uint16_t value = this->card->analogRead(pin);
|
||||||
|
@ -55,29 +98,71 @@ void AnalogIoT::publishADC(uint8_t pin) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the interval at which the state of all DACs is published.
|
||||||
|
* @param interval The interval in milliseconds.
|
||||||
|
*/
|
||||||
void AnalogIoT::setADCsPublishInterval(uint32_t interval) {
|
void AnalogIoT::setADCsPublishInterval(uint32_t interval) {
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
adc_conversion_interval[i] = interval;
|
adc_conversion_interval[i] = interval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets whether the state of all DACs is published.
|
||||||
|
* @param enabled True if the state of all DACs should be published, false otherwise.
|
||||||
|
*/
|
||||||
void AnalogIoT::setADCsPublishEnabled(bool enabled) {
|
void AnalogIoT::setADCsPublishEnabled(bool enabled) {
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
adc_publish_enabled[i] = enabled;
|
adc_publish_enabled[i] = enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Registers a callback for handling ADC conversions.
|
||||||
|
* @param callback The callback function.
|
||||||
|
* @return The handler of the callback.
|
||||||
|
*/
|
||||||
uint8_t AnalogIoT::registerADCConversionCallback(std::function<void(uint8_t, uint16_t)> callback) {
|
uint8_t AnalogIoT::registerADCConversionCallback(std::function<void(uint8_t, uint16_t)> callback) {
|
||||||
this->adc_conversion_callbacks[this->adc_conversion_callback_index] = callback;
|
this->adc_conversion_callbacks[this->adc_conversion_callback_index] = callback;
|
||||||
return this->adc_conversion_callback_index++;
|
return this->adc_conversion_callback_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregisters a callback for handling ADC conversions.
|
||||||
|
* @param handler The handler of the callback.
|
||||||
|
*/
|
||||||
void AnalogIoT::unregisterADCConversionCallback(uint8_t handler) {
|
void AnalogIoT::unregisterADCConversionCallback(uint8_t handler) {
|
||||||
this->adc_conversion_callbacks.erase(handler);
|
this->adc_conversion_callbacks.erase(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the interval at which the value of an ADC channel is read.
|
||||||
|
* @param pin The pin of the ADC channel.
|
||||||
|
* @param interval The interval in milliseconds.
|
||||||
|
*/
|
||||||
void AnalogIoT::setADCConversionInterval(uint8_t pin, uint16_t interval) {
|
void AnalogIoT::setADCConversionInterval(uint8_t pin, uint16_t interval) {
|
||||||
adc_conversion_interval[pin] = interval;
|
adc_conversion_interval[pin] = interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enables or disables the periodic reading of the value of an ADC channel.
|
||||||
|
* @param pin The pin of the ADC channel.
|
||||||
|
* @param enabled True if the value of the ADC channel should be read, false otherwise.
|
||||||
|
*/
|
||||||
void AnalogIoT::setADCConversionEnabled(uint8_t pin, bool enabled) {
|
void AnalogIoT::setADCConversionEnabled(uint8_t pin, bool enabled) {
|
||||||
adc_publish_enabled[pin] = enabled;
|
adc_publish_enabled[pin] = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes a message received on the MQTT topic for setting the state of a DAC.
|
||||||
|
* @param topic The topic of the message.
|
||||||
|
* @param payload The payload of the message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @note This function is not meant to be called from user code.
|
||||||
|
* @return True if the message was processed, false otherwise.
|
||||||
|
*/
|
||||||
bool AnalogIoT::processADCSetConversionIntervalMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool AnalogIoT::processADCSetConversionIntervalMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
// TODO: Process payload matching the criteria
|
// TODO: Process payload matching the criteria
|
||||||
// Topic: adc/<%02d>/set/conversion_interval
|
// Topic: adc/<%02d>/set/conversion_interval
|
||||||
|
@ -102,6 +187,15 @@ bool AnalogIoT::processADCSetConversionIntervalMessage(char *topic, char *payloa
|
||||||
this->setADCConversionInterval(pin, interval);
|
this->setADCConversionInterval(pin, interval);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes a message received on the MQTT topic for setting the value of a DAC.
|
||||||
|
* @param topic The topic of the message.
|
||||||
|
* @param payload The payload of the message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @note This function is not meant to be called from user code.
|
||||||
|
* @return True if the message was processed, false otherwise.
|
||||||
|
*/
|
||||||
bool AnalogIoT::processADCSetConversionEnabledMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool AnalogIoT::processADCSetConversionEnabledMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
// Topic: adc/<%02d>/set/conversion_enabled
|
// Topic: adc/<%02d>/set/conversion_enabled
|
||||||
// The first 4 characters are "adc/"
|
// The first 4 characters are "adc/"
|
||||||
|
@ -125,6 +219,15 @@ bool AnalogIoT::processADCSetConversionEnabledMessage(char *topic, char *payload
|
||||||
this->setADCConversionEnabled(pin, enabled);
|
this->setADCConversionEnabled(pin, enabled);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes a message received on the MQTT topic for setting the state of a DAC.
|
||||||
|
* @param topic The topic of the message.
|
||||||
|
* @param payload The payload of the message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @note This function is not meant to be called from user code.
|
||||||
|
* @return True if the message was processed, false otherwise.
|
||||||
|
*/
|
||||||
bool AnalogIoT::processDACSetStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool AnalogIoT::processDACSetStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
// Topic: dac/<%02d>/set/state
|
// Topic: dac/<%02d>/set/state
|
||||||
// The first 4 characters are "dac/"
|
// The first 4 characters are "dac/"
|
||||||
|
@ -148,6 +251,15 @@ bool AnalogIoT::processDACSetStateMessage(char *topic, char *payload, uint8_t to
|
||||||
this->card->setDACState(pin, state);
|
this->card->setDACState(pin, state);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes a message received on the MQTT topic for setting the value of a DAC.
|
||||||
|
* @param topic The topic of the message.
|
||||||
|
* @param payload The payload of the message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @note This function is not meant to be called from user code.
|
||||||
|
* @return True if the message was processed, false otherwise.
|
||||||
|
*/
|
||||||
bool AnalogIoT::processDACSetValueMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool AnalogIoT::processDACSetValueMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
// Topic: dac/<%02d>/set/value
|
// Topic: dac/<%02d>/set/value
|
||||||
// The first 4 characters are "dac/"
|
// The first 4 characters are "dac/"
|
||||||
|
@ -171,6 +283,15 @@ bool AnalogIoT::processDACSetValueMessage(char *topic, char *payload, uint8_t to
|
||||||
this->card->setDACValue(pin, value);
|
this->card->setDACValue(pin, value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes a message received on the MQTT topic for requesting the state of all DACs.
|
||||||
|
* @param topic The topic of the message.
|
||||||
|
* @param payload The payload of the message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @note This function is not meant to be called from user code.
|
||||||
|
* @return True if the message was processed, false otherwise.
|
||||||
|
*/
|
||||||
bool AnalogIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool AnalogIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
// Topic: requeststate
|
// Topic: requeststate
|
||||||
// The length of the topic must be 12 characters
|
// The length of the topic must be 12 characters
|
||||||
|
@ -187,6 +308,11 @@ bool AnalogIoT::processRequestStateMessage(char *topic, char *payload, uint8_t t
|
||||||
this->publishADCs();
|
this->publishADCs();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subscribes to all MQTT topics used by the AnalogIoT object.
|
||||||
|
* @note This function is called when the MQTT client connects.
|
||||||
|
*/
|
||||||
void AnalogIoT::subscribe() {
|
void AnalogIoT::subscribe() {
|
||||||
// There are 4 DACs and 8 ADCs
|
// There are 4 DACs and 8 ADCs
|
||||||
// DACs: dac/<%02d>/set/state, dac/<%02d>/set/value, dac/publish_enable
|
// DACs: dac/<%02d>/set/state, dac/<%02d>/set/value, dac/publish_enable
|
||||||
|
@ -226,23 +352,45 @@ void AnalogIoT::loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the state of all DACs.
|
||||||
|
*/
|
||||||
void AnalogIoT::publishReport() {
|
void AnalogIoT::publishReport() {
|
||||||
publishADCs();
|
publishADCs();
|
||||||
publishDACs();
|
publishDACs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the type of the card.
|
||||||
|
* @return The type of the card.
|
||||||
|
*/
|
||||||
uint8_t AnalogIoT::getType() {
|
uint8_t AnalogIoT::getType() {
|
||||||
return CARD_TYPE_ANALOG;
|
return CARD_TYPE_ANALOG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the state of all DACs.
|
||||||
|
*/
|
||||||
void AnalogIoT::publishDACs() {
|
void AnalogIoT::publishDACs() {
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
for (uint8_t i = 0; i < 4; i++) {
|
||||||
this->publishDAC(i);
|
this->publishDAC(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the state of a DAC.
|
||||||
|
* @param pin The pin of the DAC.
|
||||||
|
*/
|
||||||
void AnalogIoT::publishDAC(uint8_t pin) {
|
void AnalogIoT::publishDAC(uint8_t pin) {
|
||||||
this->publishDACState(pin);
|
this->publishDACState(pin);
|
||||||
this->publishDACValue(pin);
|
this->publishDACValue(pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the state of a DAC.
|
||||||
|
* @param pin The pin of the DAC.
|
||||||
|
*/
|
||||||
void AnalogIoT::publishDACState(uint8_t pin) {
|
void AnalogIoT::publishDACState(uint8_t pin) {
|
||||||
char *topic = new char[15];
|
char *topic = new char[15];
|
||||||
sprintf(topic, "dac/%02d/state", pin);
|
sprintf(topic, "dac/%02d/state", pin);
|
||||||
|
@ -252,6 +400,11 @@ void AnalogIoT::publishDACState(uint8_t pin) {
|
||||||
delete[] topic;
|
delete[] topic;
|
||||||
delete[] payload;
|
delete[] payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the value of a DAC.
|
||||||
|
* @param pin The pin of the DAC.
|
||||||
|
*/
|
||||||
void AnalogIoT::publishDACValue(uint8_t pin) {
|
void AnalogIoT::publishDACValue(uint8_t pin) {
|
||||||
char *topic = new char[15];
|
char *topic = new char[15];
|
||||||
sprintf(topic, "dac/%02d/value", pin);
|
sprintf(topic, "dac/%02d/value", pin);
|
||||||
|
@ -262,6 +415,10 @@ void AnalogIoT::publishDACValue(uint8_t pin) {
|
||||||
delete[] payload;
|
delete[] payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the state of a DAC.
|
||||||
|
* @param pin The pin of the DAC.
|
||||||
|
*/
|
||||||
void AnalogIoT::handleDACChange(uint8_t pin, uint16_t value) {
|
void AnalogIoT::handleDACChange(uint8_t pin, uint16_t value) {
|
||||||
this->publishDAC(pin);
|
this->publishDAC(pin);
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#include <AnalogCard.hpp>
|
#include <AnalogCard.hpp>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
// MQTT Topics
|
||||||
#define DAC_SET_STATE_TOPIC "/set/state"
|
#define DAC_SET_STATE_TOPIC "/set/state"
|
||||||
#define DAC_SET_VALUE_TOPIC "/set/value"
|
#define DAC_SET_VALUE_TOPIC "/set/value"
|
||||||
#define DAC_STATE_TOPIC "/dac/00/state"
|
#define DAC_STATE_TOPIC "/dac/00/state"
|
||||||
|
@ -39,7 +40,9 @@ class AnalogIoT : public IoTComponent {
|
||||||
void loop();
|
void loop();
|
||||||
uint8_t getType();
|
uint8_t getType();
|
||||||
private:
|
private:
|
||||||
|
// The index of the next callback to be registered
|
||||||
uint8_t adc_conversion_callback_index = 0;
|
uint8_t adc_conversion_callback_index = 0;
|
||||||
|
// We keep track of the length of the topics so we don't have to calculate it every time
|
||||||
uint8_t dac_set_state_length;
|
uint8_t dac_set_state_length;
|
||||||
uint8_t dac_set_value_length;
|
uint8_t dac_set_value_length;
|
||||||
uint8_t dac_state_length;
|
uint8_t dac_state_length;
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
|
/**
|
||||||
|
* @file ClimateCard.cpp
|
||||||
|
* @brief Implementation file for the ClimateCard class.
|
||||||
|
*
|
||||||
|
* This file contains the implementation of the ClimateCard class, which represents a climate control card.
|
||||||
|
* The ClimateCard class provides methods for controlling an air conditioner, reading temperature and humidity
|
||||||
|
* from sensors, and saving and loading the state to/from FRAM memory.
|
||||||
|
*/
|
||||||
#include <ClimateCard.hpp>
|
#include <ClimateCard.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new ClimateCard object.
|
||||||
|
*
|
||||||
|
* @param ir_pin The GPIO pin number of the IR transmitter.
|
||||||
|
* @param ac The AirConditioner object that represents the air conditioner.
|
||||||
|
* @param sensor_type The type of the sensor connected to the card.
|
||||||
|
* @param sensor_pin The GPIO pin number of the sensor.
|
||||||
|
*/
|
||||||
ClimateCard::ClimateCard(uint8_t ir_pin, AirConditioner ac, uint8_t sensor_type, uint8_t sensor_pin)
|
ClimateCard::ClimateCard(uint8_t ir_pin, AirConditioner ac, uint8_t sensor_type, uint8_t sensor_pin)
|
||||||
{
|
{
|
||||||
this->ir_pin = ir_pin;
|
this->ir_pin = ir_pin;
|
||||||
|
@ -24,11 +40,21 @@ ClimateCard::ClimateCard(uint8_t ir_pin, AirConditioner ac, uint8_t sensor_type,
|
||||||
this->state.ac_fan_speed = 0;
|
this->state.ac_fan_speed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Construct a new ClimateCard object.
|
||||||
|
*
|
||||||
|
* @param ir_pin The GPIO pin number of the IR transmitter.
|
||||||
|
* @param ac The AirConditioner object that represents the air conditioner.
|
||||||
|
*
|
||||||
|
* @note This constructor can be used when no sensor is connected to the card.
|
||||||
|
*/
|
||||||
ClimateCard::ClimateCard(uint8_t ir_pin, AirConditioner ac) : ClimateCard(ir_pin, ac, AC_SENSOR_TYPE_NONE, 0)
|
ClimateCard::ClimateCard(uint8_t ir_pin, AirConditioner ac) : ClimateCard(ir_pin, ac, AC_SENSOR_TYPE_NONE, 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The destructor of the ClimateCard class.
|
||||||
|
*/
|
||||||
ClimateCard::~ClimateCard()
|
ClimateCard::~ClimateCard()
|
||||||
{
|
{
|
||||||
delete dht;
|
delete dht;
|
||||||
|
@ -36,6 +62,12 @@ ClimateCard::~ClimateCard()
|
||||||
rmt_driver_uninstall(RMT_TX_CHANNEL);
|
rmt_driver_uninstall(RMT_TX_CHANNEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the ClimateCard object.
|
||||||
|
*
|
||||||
|
* @return true if initialization was successful.
|
||||||
|
* @return false if initialization failed.
|
||||||
|
*/
|
||||||
bool ClimateCard::begin()
|
bool ClimateCard::begin()
|
||||||
{
|
{
|
||||||
switch (sensor_type)
|
switch (sensor_type)
|
||||||
|
@ -49,6 +81,7 @@ bool ClimateCard::begin()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
updateAirConditioner();
|
updateAirConditioner();
|
||||||
|
// We are returning here because sending IR signals is not working yet
|
||||||
return true;
|
return true;
|
||||||
if (sensor_pin != 0)
|
if (sensor_pin != 0)
|
||||||
{
|
{
|
||||||
|
@ -61,6 +94,11 @@ bool ClimateCard::begin()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Loop function of the ClimateCard class.
|
||||||
|
*
|
||||||
|
* @note When this card is installed in an ESPMega, this function is called automatically by the ESPMega class.
|
||||||
|
*/
|
||||||
void ClimateCard::loop()
|
void ClimateCard::loop()
|
||||||
{
|
{
|
||||||
static uint32_t last_sensor_update = 0;
|
static uint32_t last_sensor_update = 0;
|
||||||
|
@ -71,26 +109,54 @@ void ClimateCard::loop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief bind FRAM memory to the ClimateCard object at the specified address.
|
||||||
|
*
|
||||||
|
* @note This function must be called before calling loadStateFromFRAM() or saveStateToFRAM().
|
||||||
|
* @note This card takes up 3 bytes of FRAM memory.
|
||||||
|
*
|
||||||
|
* @param fram The FRAM object.
|
||||||
|
* @param fram_address The starting address of the card in FRAM memory.
|
||||||
|
*/
|
||||||
void ClimateCard::bindFRAM(FRAM *fram, uint16_t fram_address)
|
void ClimateCard::bindFRAM(FRAM *fram, uint16_t fram_address)
|
||||||
{
|
{
|
||||||
this->fram = fram;
|
this->fram = fram;
|
||||||
this->fram_address = fram_address;
|
this->fram_address = fram_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set whether the state should be automatically saved to FRAM memory.
|
||||||
|
*
|
||||||
|
* @note This function has no effect if bindFRAM() has not been called.
|
||||||
|
* @param autoSave Whether the state should be automatically saved to FRAM memory.
|
||||||
|
*/
|
||||||
void ClimateCard::setFRAMAutoSave(bool autoSave)
|
void ClimateCard::setFRAMAutoSave(bool autoSave)
|
||||||
{
|
{
|
||||||
this->fram_auto_save = autoSave;
|
this->fram_auto_save = autoSave;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Save the state to FRAM memory.
|
||||||
|
* @note This function has no effect if bindFRAM() has not been called.
|
||||||
|
*/
|
||||||
void ClimateCard::saveStateToFRAM()
|
void ClimateCard::saveStateToFRAM()
|
||||||
{
|
{
|
||||||
|
if (fram == nullptr)
|
||||||
|
return;
|
||||||
fram->write8(fram_address, state.ac_temperature);
|
fram->write8(fram_address, state.ac_temperature);
|
||||||
fram->write8(fram_address + 1, state.ac_mode);
|
fram->write8(fram_address + 1, state.ac_mode);
|
||||||
fram->write8(fram_address + 2, state.ac_fan_speed);
|
fram->write8(fram_address + 2, state.ac_fan_speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load the state from FRAM memory.
|
||||||
|
*
|
||||||
|
* @note This function has no effect if bindFRAM() has not been called.
|
||||||
|
*/
|
||||||
void ClimateCard::loadStateFromFRAM()
|
void ClimateCard::loadStateFromFRAM()
|
||||||
{
|
{
|
||||||
|
if (fram == nullptr)
|
||||||
|
return;
|
||||||
if (state.ac_temperature > ac.max_temperature)
|
if (state.ac_temperature > ac.max_temperature)
|
||||||
state.ac_temperature = ac.max_temperature;
|
state.ac_temperature = ac.max_temperature;
|
||||||
else if (state.ac_temperature < ac.min_temperature)
|
else if (state.ac_temperature < ac.min_temperature)
|
||||||
|
@ -102,12 +168,18 @@ void ClimateCard::loadStateFromFRAM()
|
||||||
if (state.ac_fan_speed > ac.fan_speeds)
|
if (state.ac_fan_speed > ac.fan_speeds)
|
||||||
state.ac_fan_speed = 0;
|
state.ac_fan_speed = 0;
|
||||||
updateAirConditioner();
|
updateAirConditioner();
|
||||||
for (const auto& callback : callbacks)
|
for (const auto &callback : callbacks)
|
||||||
{
|
{
|
||||||
callback.second(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature);
|
callback.second(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the temperature of the air conditioner.
|
||||||
|
*
|
||||||
|
* @param temperature The temperature to set.
|
||||||
|
* @note If the temperature is out of range, it will be set to its respective maximum or minimum.
|
||||||
|
*/
|
||||||
void ClimateCard::setTemperature(uint8_t temperature)
|
void ClimateCard::setTemperature(uint8_t temperature)
|
||||||
{
|
{
|
||||||
// If temperature is out of range, set to its respective maximum or minimum
|
// If temperature is out of range, set to its respective maximum or minimum
|
||||||
|
@ -121,33 +193,64 @@ void ClimateCard::setTemperature(uint8_t temperature)
|
||||||
saveStateToFRAM();
|
saveStateToFRAM();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the mode of the air conditioner.
|
||||||
|
*
|
||||||
|
* @note If the mode is out of range, it will be set to 0.
|
||||||
|
* @param mode The mode to set.
|
||||||
|
*/
|
||||||
void ClimateCard::setMode(uint8_t mode)
|
void ClimateCard::setMode(uint8_t mode)
|
||||||
{
|
{
|
||||||
|
if (mode > ac.modes)
|
||||||
|
mode = 0;
|
||||||
this->state.ac_mode = mode;
|
this->state.ac_mode = mode;
|
||||||
updateAirConditioner();
|
updateAirConditioner();
|
||||||
if (fram_auto_save)
|
if (fram_auto_save)
|
||||||
saveStateToFRAM();
|
saveStateToFRAM();
|
||||||
}
|
}
|
||||||
|
|
||||||
char* ClimateCard::getModeName()
|
/**
|
||||||
|
* @brief Get the name of the current mode.
|
||||||
|
* @return The name of the current mode.
|
||||||
|
*/
|
||||||
|
char *ClimateCard::getModeName()
|
||||||
{
|
{
|
||||||
return (char*)ac.mode_names[state.ac_mode];
|
return (char *)ac.mode_names[state.ac_mode];
|
||||||
}
|
}
|
||||||
|
|
||||||
char* ClimateCard::getFanSpeedName()
|
/**
|
||||||
|
* @brief Get the name of the current fan speed.
|
||||||
|
*
|
||||||
|
* @return The name of the current fan speed.
|
||||||
|
*/
|
||||||
|
char *ClimateCard::getFanSpeedName()
|
||||||
{
|
{
|
||||||
return (char*)ac.fan_speed_names[state.ac_fan_speed];
|
return (char *)ac.fan_speed_names[state.ac_fan_speed];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the fan speed of the air conditioner.
|
||||||
|
*
|
||||||
|
* @note If the fan speed is out of range, it will be set to 0.
|
||||||
|
* @param fan_speed The fan speed to set.
|
||||||
|
*/
|
||||||
void ClimateCard::setFanSpeed(uint8_t fan_speed)
|
void ClimateCard::setFanSpeed(uint8_t fan_speed)
|
||||||
{
|
{
|
||||||
|
if (fan_speed > ac.fan_speeds)
|
||||||
|
fan_speed = 0;
|
||||||
this->state.ac_fan_speed = fan_speed;
|
this->state.ac_fan_speed = fan_speed;
|
||||||
updateAirConditioner();
|
updateAirConditioner();
|
||||||
if (fram_auto_save)
|
if (fram_auto_save)
|
||||||
saveStateToFRAM();
|
saveStateToFRAM();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClimateCard::setFanSpeedByName(const char* fan_speed_name)
|
/**
|
||||||
|
* @brief Set fan speed by name.
|
||||||
|
*
|
||||||
|
* @param fan_speed_name The name of the fan speed to set.
|
||||||
|
* @note If the fan speed is not found, the function will not do anything.
|
||||||
|
*/
|
||||||
|
void ClimateCard::setFanSpeedByName(const char *fan_speed_name)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < ac.fan_speeds; i++)
|
for (uint8_t i = 0; i < ac.fan_speeds; i++)
|
||||||
{
|
{
|
||||||
|
@ -159,7 +262,13 @@ void ClimateCard::setFanSpeedByName(const char* fan_speed_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClimateCard::setModeByName(const char* mode_name)
|
/**
|
||||||
|
* @brief Set mode by name.
|
||||||
|
*
|
||||||
|
* @param mode_name The name of the mode to set.
|
||||||
|
* @note If the mode is not found, the function will not do anything.
|
||||||
|
*/
|
||||||
|
void ClimateCard::setModeByName(const char *mode_name)
|
||||||
{
|
{
|
||||||
for (uint8_t i = 0; i < ac.modes; i++)
|
for (uint8_t i = 0; i < ac.modes; i++)
|
||||||
{
|
{
|
||||||
|
@ -171,17 +280,36 @@ void ClimateCard::setModeByName(const char* mode_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a callback function that will be called when the state of the air conditioner changes.
|
||||||
|
*
|
||||||
|
* @param callback The callback function to register.
|
||||||
|
*
|
||||||
|
* @return uint8_t The handler of the callback function.
|
||||||
|
*/
|
||||||
uint8_t ClimateCard::registerChangeCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback)
|
uint8_t ClimateCard::registerChangeCallback(std::function<void(uint8_t, uint8_t, uint8_t)> callback)
|
||||||
{
|
{
|
||||||
callbacks[callbacks_handler_count] = callback;
|
callbacks[callbacks_handler_count] = callback;
|
||||||
return callbacks_handler_count++;
|
return callbacks_handler_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the type of the card.
|
||||||
|
*
|
||||||
|
* @return The handler of the callback function.
|
||||||
|
*/
|
||||||
uint8_t ClimateCard::getType()
|
uint8_t ClimateCard::getType()
|
||||||
{
|
{
|
||||||
return CARD_TYPE_CLIMATE;
|
return CARD_TYPE_CLIMATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief update environmental sensor data.
|
||||||
|
*
|
||||||
|
* @note This function is called automatically by the loop() function.
|
||||||
|
* @note This function has no effect if no sensor is connected to the card.
|
||||||
|
* @note This function also calls the sensor callbacks.
|
||||||
|
*/
|
||||||
void ClimateCard::updateSensor()
|
void ClimateCard::updateSensor()
|
||||||
{
|
{
|
||||||
if (sensor_type == AC_SENSOR_TYPE_NONE)
|
if (sensor_type == AC_SENSOR_TYPE_NONE)
|
||||||
|
@ -209,17 +337,23 @@ void ClimateCard::updateSensor()
|
||||||
room_temperature = ds18b20->getTempC();
|
room_temperature = ds18b20->getTempC();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (const auto& callback : sensor_callbacks)
|
for (const auto &callback : sensor_callbacks)
|
||||||
{
|
{
|
||||||
callback.second(room_temperature, humidity);
|
callback.second(room_temperature, humidity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the air conditioner state to match the state of the card.
|
||||||
|
*
|
||||||
|
* @warning This function is not working yet.
|
||||||
|
*/
|
||||||
void ClimateCard::updateAirConditioner()
|
void ClimateCard::updateAirConditioner()
|
||||||
{
|
{
|
||||||
|
// // The IR Transmissions are not working yet so we just return
|
||||||
// const uint16_t* ir_code_ptr = nullptr;
|
// const uint16_t* ir_code_ptr = nullptr;
|
||||||
// size_t itemCount = (*(this->ac.getInfraredCode))(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature, &ir_code_ptr);
|
// size_t itemCount = (*(this->ac.getInfraredCode))(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature, &ir_code_ptr);
|
||||||
|
|
||||||
// if (ir_code_ptr == nullptr)
|
// if (ir_code_ptr == nullptr)
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
|
@ -236,53 +370,100 @@ void ClimateCard::updateAirConditioner()
|
||||||
// rmt_write_items(RMT_TX_CHANNEL, items, itemCount, true);
|
// rmt_write_items(RMT_TX_CHANNEL, items, itemCount, true);
|
||||||
// rmt_wait_tx_done(RMT_TX_CHANNEL, portMAX_DELAY);
|
// rmt_wait_tx_done(RMT_TX_CHANNEL, portMAX_DELAY);
|
||||||
// // Publish state
|
// // Publish state
|
||||||
for (const auto& callback : callbacks)
|
for (const auto &callback : callbacks)
|
||||||
{
|
{
|
||||||
callback.second(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature);
|
callback.second(this->state.ac_mode, this->state.ac_fan_speed, this->state.ac_temperature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the type of the sensor connected to the card.
|
||||||
|
*
|
||||||
|
* @return The type of the sensor connected to the card.
|
||||||
|
*/
|
||||||
uint8_t ClimateCard::getSensorType()
|
uint8_t ClimateCard::getSensorType()
|
||||||
{
|
{
|
||||||
return sensor_type;
|
return sensor_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the room temperature in degrees Celsius.
|
||||||
|
*
|
||||||
|
* @return The room temperature.
|
||||||
|
*/
|
||||||
float ClimateCard::getRoomTemperature()
|
float ClimateCard::getRoomTemperature()
|
||||||
{
|
{
|
||||||
return room_temperature;
|
return room_temperature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the humidity in percent.
|
||||||
|
*
|
||||||
|
* @return The humidity.
|
||||||
|
*/
|
||||||
float ClimateCard::getHumidity()
|
float ClimateCard::getHumidity()
|
||||||
{
|
{
|
||||||
return humidity;
|
return humidity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the temperature of the air conditioner.
|
||||||
|
*
|
||||||
|
* @return The temperature of the air conditioner.
|
||||||
|
*/
|
||||||
uint8_t ClimateCard::getTemperature()
|
uint8_t ClimateCard::getTemperature()
|
||||||
{
|
{
|
||||||
return state.ac_temperature;
|
return state.ac_temperature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the mode of the air conditioner.
|
||||||
|
*
|
||||||
|
* @return The mode of the air conditioner.
|
||||||
|
*/
|
||||||
uint8_t ClimateCard::getMode()
|
uint8_t ClimateCard::getMode()
|
||||||
{
|
{
|
||||||
return state.ac_mode;
|
return state.ac_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the fan speed of the air conditioner.
|
||||||
|
*
|
||||||
|
* @return The fan speed of the air conditioner.
|
||||||
|
*/
|
||||||
uint8_t ClimateCard::getFanSpeed()
|
uint8_t ClimateCard::getFanSpeed()
|
||||||
{
|
{
|
||||||
return state.ac_fan_speed;
|
return state.ac_fan_speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a callback function that will be called when the sensor data changes.
|
||||||
|
*
|
||||||
|
* @param callback The callback function to register.
|
||||||
|
*
|
||||||
|
* @return The handler of the callback function
|
||||||
|
*/
|
||||||
uint8_t ClimateCard::registerSensorCallback(std::function<void(float, float)> callback)
|
uint8_t ClimateCard::registerSensorCallback(std::function<void(float, float)> callback)
|
||||||
{
|
{
|
||||||
sensor_callbacks[sensor_callbacks_handler_count] = callback;
|
sensor_callbacks[sensor_callbacks_handler_count] = callback;
|
||||||
return sensor_callbacks_handler_count++;
|
return sensor_callbacks_handler_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregister a callback function.
|
||||||
|
*
|
||||||
|
* @param handler The handler of the callback function to unregister.
|
||||||
|
*/
|
||||||
void ClimateCard::unregisterChangeCallback(uint8_t handler)
|
void ClimateCard::unregisterChangeCallback(uint8_t handler)
|
||||||
{
|
{
|
||||||
callbacks.erase(handler);
|
callbacks.erase(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregister a sensor callback function.
|
||||||
|
*
|
||||||
|
* @param handler The handler of the callback function to unregister.
|
||||||
|
*/
|
||||||
void ClimateCard::unregisterSensorCallback(uint8_t handler)
|
void ClimateCard::unregisterSensorCallback(uint8_t handler)
|
||||||
{
|
{
|
||||||
sensor_callbacks.erase(handler);
|
sensor_callbacks.erase(handler);
|
||||||
|
|
|
@ -18,22 +18,38 @@
|
||||||
#define AC_SENSOR_READ_INTERVAL 5000
|
#define AC_SENSOR_READ_INTERVAL 5000
|
||||||
#define AC_SENSOR_READ_TIMEOUT 250
|
#define AC_SENSOR_READ_TIMEOUT 250
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The struct is used to store the state of the air conditioner
|
||||||
|
*
|
||||||
|
* @note This struct is stored in FRAM if it is used
|
||||||
|
* @note This struct is 3 bytes long
|
||||||
|
*/
|
||||||
struct ClimateCardData {
|
struct ClimateCardData {
|
||||||
uint8_t ac_temperature;
|
uint8_t ac_temperature; ///< Temperature of the air conditioner
|
||||||
uint8_t ac_mode;
|
uint8_t ac_mode; ///< Mode of the air conditioner
|
||||||
uint8_t ac_fan_speed;
|
uint8_t ac_fan_speed;///< Fan speed of the air conditioner
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This struct is used to store information about an air conditioner
|
||||||
|
*/
|
||||||
struct AirConditioner {
|
struct AirConditioner {
|
||||||
uint8_t max_temperature;
|
uint8_t max_temperature; ///< Maximum temperature
|
||||||
uint8_t min_temperature;
|
uint8_t min_temperature; ///< Minimum temperature
|
||||||
uint8_t modes;
|
uint8_t modes; ///< Number of modes
|
||||||
const char **mode_names;
|
const char **mode_names; ///< Names of modes in the form of an array of strings
|
||||||
uint8_t fan_speeds;
|
uint8_t fan_speeds; ///< Number of fan speeds
|
||||||
const char **fan_speed_names;
|
const char **fan_speed_names; ///< Names of fan speeds in the form of an array of strings
|
||||||
// function to get IR code
|
/**
|
||||||
// takes 3 arguments: mode, fan speed, temperature, all uint8_t
|
* @brief Function to get IR code
|
||||||
// return size of IR code array
|
*
|
||||||
|
* @param mode Mode of the air conditioner
|
||||||
|
* @param fan_speed Fan speed of the air conditioner
|
||||||
|
* @param temperature Temperature of the air conditioner
|
||||||
|
* @param code Pointer to the IR code array
|
||||||
|
*
|
||||||
|
* @return Size of the IR code array
|
||||||
|
*/
|
||||||
size_t (*getInfraredCode)(uint8_t, uint8_t, uint8_t, const uint16_t**);
|
size_t (*getInfraredCode)(uint8_t, uint8_t, uint8_t, const uint16_t**);
|
||||||
};
|
};
|
||||||
// This requires 3 bytes of FRAM
|
// This requires 3 bytes of FRAM
|
||||||
|
|
|
@ -1,13 +1,29 @@
|
||||||
#include <ClimateIoT.hpp>
|
#include <ClimateIoT.hpp>
|
||||||
|
|
||||||
|
|
||||||
ClimateIoT::ClimateIoT() {
|
ClimateIoT::ClimateIoT() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor for the ClimateIoT class.
|
||||||
|
*/
|
||||||
ClimateIoT::~ClimateIoT() {
|
ClimateIoT::~ClimateIoT() {
|
||||||
// Destructor implementation
|
// Destructor implementation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the ClimateIoT component.
|
||||||
|
*
|
||||||
|
* This function sets the MQTT client, base topic, card ID, and card pointer.
|
||||||
|
* It also registers the sensor and air conditioner update callbacks.
|
||||||
|
*
|
||||||
|
* @param card_id The ID of the expansion card.
|
||||||
|
* @param card A pointer to the ExpansionCard object.
|
||||||
|
* @param mqtt A pointer to the PubSubClient object.
|
||||||
|
* @param base_topic The base topic for MQTT communication.
|
||||||
|
* @return True if the initialization is successful, false otherwise.
|
||||||
|
*/
|
||||||
bool ClimateIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
bool ClimateIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||||
this->mqtt = mqtt;
|
this->mqtt = mqtt;
|
||||||
this->base_topic = base_topic;
|
this->base_topic = base_topic;
|
||||||
|
@ -22,6 +38,12 @@ bool ClimateIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles MQTT messages for the ClimateIoT component.
|
||||||
|
*
|
||||||
|
* @param topic The trimmed topic of the MQTT message.
|
||||||
|
* @param payload The null-terminated payload of the MQTT message.
|
||||||
|
*/
|
||||||
void ClimateIoT::handleMqttMessage(char *topic, char *payload) {
|
void ClimateIoT::handleMqttMessage(char *topic, char *payload) {
|
||||||
uint8_t topic_length = strlen(topic);
|
uint8_t topic_length = strlen(topic);
|
||||||
if (this->processSetTemperatureMessage(topic, payload, topic_length))
|
if (this->processSetTemperatureMessage(topic, payload, topic_length))
|
||||||
|
@ -34,31 +56,49 @@ void ClimateIoT::handleMqttMessage(char *topic, char *payload) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the temperature of the air conditioner to the MQTT broker.
|
||||||
|
*/
|
||||||
void ClimateIoT::publishClimateTemperature() {
|
void ClimateIoT::publishClimateTemperature() {
|
||||||
char payload[5];
|
char payload[5];
|
||||||
itoa(this->card->getTemperature(), payload, 10);
|
itoa(this->card->getTemperature(), payload, 10);
|
||||||
this->publishRelative(AC_TEMPERATURE_REPORT_TOPIC, payload);
|
this->publishRelative(AC_TEMPERATURE_REPORT_TOPIC, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the mode of the air conditioner to the MQTT broker.
|
||||||
|
*/
|
||||||
void ClimateIoT::publishClimateMode() {
|
void ClimateIoT::publishClimateMode() {
|
||||||
this->publishRelative(AC_MODE_REPORT_TOPIC, this->card->getModeName());
|
this->publishRelative(AC_MODE_REPORT_TOPIC, this->card->getModeName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the fan speed of the air conditioner to the MQTT broker.
|
||||||
|
*/
|
||||||
void ClimateIoT::publishClimateFanSpeed() {
|
void ClimateIoT::publishClimateFanSpeed() {
|
||||||
this->publishRelative(AC_FAN_SPEED_REPORT_TOPIC, this->card->getFanSpeedName());
|
this->publishRelative(AC_FAN_SPEED_REPORT_TOPIC, this->card->getFanSpeedName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the temperature and humidity of the room to the MQTT broker.
|
||||||
|
*/
|
||||||
void ClimateIoT::publishSensor() {
|
void ClimateIoT::publishSensor() {
|
||||||
this->publishRoomTemperature();
|
this->publishRoomTemperature();
|
||||||
this->publishHumidity();
|
this->publishHumidity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the climate data (temperature, mode, fan speed) to the MQTT broker.
|
||||||
|
*/
|
||||||
void ClimateIoT::publishClimate() {
|
void ClimateIoT::publishClimate() {
|
||||||
this->publishClimateTemperature();
|
this->publishClimateTemperature();
|
||||||
this->publishClimateMode();
|
this->publishClimateMode();
|
||||||
this->publishClimateFanSpeed();
|
this->publishClimateFanSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the room temperature to the MQTT broker.
|
||||||
|
*/
|
||||||
void ClimateIoT::publishRoomTemperature() {
|
void ClimateIoT::publishRoomTemperature() {
|
||||||
if (this->card->getSensorType() == AC_SENSOR_TYPE_NONE ) {
|
if (this->card->getSensorType() == AC_SENSOR_TYPE_NONE ) {
|
||||||
return;
|
return;
|
||||||
|
@ -68,6 +108,9 @@ void ClimateIoT::publishRoomTemperature() {
|
||||||
this->publishRelative(AC_ROOM_TEMPERATURE_REPORT_TOPIC, payload);
|
this->publishRelative(AC_ROOM_TEMPERATURE_REPORT_TOPIC, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the humidity of the room to the MQTT broker.
|
||||||
|
*/
|
||||||
void ClimateIoT::publishHumidity() {
|
void ClimateIoT::publishHumidity() {
|
||||||
if (this->card->getSensorType() == AC_SENSOR_TYPE_DHT22) {
|
if (this->card->getSensorType() == AC_SENSOR_TYPE_DHT22) {
|
||||||
char payload[5];
|
char payload[5];
|
||||||
|
@ -76,15 +119,30 @@ void ClimateIoT::publishHumidity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handle Air Conditioner state change.
|
||||||
|
*
|
||||||
|
* @note This function is called by the underlying ClimateCard object and is not meant to be called manually.
|
||||||
|
*
|
||||||
|
* @param temperature Temperature of the air conditioner
|
||||||
|
* @param mode Mode of the air conditioner
|
||||||
|
* @param fan_speed Fan speed of the air conditioner
|
||||||
|
*/
|
||||||
void ClimateIoT::handleStateChange(uint8_t temperature, uint8_t mode, uint8_t fan_speed) {
|
void ClimateIoT::handleStateChange(uint8_t temperature, uint8_t mode, uint8_t fan_speed) {
|
||||||
this->publishClimate();
|
this->publishClimate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publishes the climate and sensor data to the MQTT broker.
|
||||||
|
*/
|
||||||
void ClimateIoT::publishReport() {
|
void ClimateIoT::publishReport() {
|
||||||
this->publishClimate();
|
this->publishClimate();
|
||||||
this->publishSensor();
|
this->publishSensor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subscribes to MQTT topics.
|
||||||
|
*/
|
||||||
void ClimateIoT::subscribe() {
|
void ClimateIoT::subscribe() {
|
||||||
ESP_LOGD("ClimateIoT", " topics");
|
ESP_LOGD("ClimateIoT", " topics");
|
||||||
this->subscribeRelative(AC_TEMPERATURE_SET_TOPIC);
|
this->subscribeRelative(AC_TEMPERATURE_SET_TOPIC);
|
||||||
|
@ -93,14 +151,32 @@ void ClimateIoT::subscribe() {
|
||||||
ESP_LOGD("ClimateIoT", "Subscribed to topics");
|
ESP_LOGD("ClimateIoT", "Subscribed to topics");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The loop function for the ClimateIoT component.
|
||||||
|
*
|
||||||
|
* @note This function does nothing.
|
||||||
|
*/
|
||||||
void ClimateIoT::loop() {
|
void ClimateIoT::loop() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the type of the expansion card.
|
||||||
|
*
|
||||||
|
* @return The type of the expansion card.
|
||||||
|
*/
|
||||||
uint8_t ClimateIoT::getType() {
|
uint8_t ClimateIoT::getType() {
|
||||||
return CARD_TYPE_CLIMATE;
|
return CARD_TYPE_CLIMATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes the set temperature MQTT message.
|
||||||
|
*
|
||||||
|
* @param topic The trimmed topic of the MQTT message.
|
||||||
|
* @param payload The null-terminated payload of the MQTT message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @return True if the message is processed, false otherwise.
|
||||||
|
*/
|
||||||
bool ClimateIoT::processSetTemperatureMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool ClimateIoT::processSetTemperatureMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
if (!strcmp(topic, AC_TEMPERATURE_SET_TOPIC)) {
|
if (!strcmp(topic, AC_TEMPERATURE_SET_TOPIC)) {
|
||||||
uint8_t temperature = atoi(payload);
|
uint8_t temperature = atoi(payload);
|
||||||
|
@ -110,6 +186,14 @@ bool ClimateIoT::processSetTemperatureMessage(char *topic, char *payload, uint8_
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes the set mode MQTT message.
|
||||||
|
*
|
||||||
|
* @param topic The trimmed topic of the MQTT message.
|
||||||
|
* @param payload The null-terminated payload of the MQTT message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @return True if the message is processed, false otherwise.
|
||||||
|
*/
|
||||||
bool ClimateIoT::processSetModeMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool ClimateIoT::processSetModeMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
if (!strcmp(topic, AC_MODE_SET_TOPIC)) {
|
if (!strcmp(topic, AC_MODE_SET_TOPIC)) {
|
||||||
this->card->setModeByName(payload);
|
this->card->setModeByName(payload);
|
||||||
|
@ -118,6 +202,14 @@ bool ClimateIoT::processSetModeMessage(char *topic, char *payload, uint8_t topic
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes the set fan speed MQTT message.
|
||||||
|
*
|
||||||
|
* @param topic The trimmed topic of the MQTT message.
|
||||||
|
* @param payload The null-terminated payload of the MQTT message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @return True if the message is processed, false otherwise.
|
||||||
|
*/
|
||||||
bool ClimateIoT::processSetFanSpeedMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool ClimateIoT::processSetFanSpeedMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
if (!strcmp(topic, AC_FAN_SPEED_SET_TOPIC)) {
|
if (!strcmp(topic, AC_FAN_SPEED_SET_TOPIC)) {
|
||||||
this->card->setFanSpeedByName(payload);
|
this->card->setFanSpeedByName(payload);
|
||||||
|
@ -125,6 +217,14 @@ bool ClimateIoT::processSetFanSpeedMessage(char *topic, char *payload, uint8_t t
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Processes the request state MQTT message.
|
||||||
|
*
|
||||||
|
* @param topic The trimmed topic of the MQTT message.
|
||||||
|
* @param payload The null-terminated payload of the MQTT message.
|
||||||
|
* @param topic_length The length of the topic.
|
||||||
|
* @return True if the message is processed, false otherwise.
|
||||||
|
*/
|
||||||
bool ClimateIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
bool ClimateIoT::processRequestStateMessage(char *topic, char *payload, uint8_t topic_length) {
|
||||||
if (!strcmp(topic, AC_REQUEST_STATE_TOPIC)) {
|
if (!strcmp(topic, AC_REQUEST_STATE_TOPIC)) {
|
||||||
this->publishReport();
|
this->publishReport();
|
||||||
|
@ -133,10 +233,25 @@ bool ClimateIoT::processRequestStateMessage(char *topic, char *payload, uint8_t
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is a callback function registered with the Climate card to be called when the sensor data is updated.
|
||||||
|
*
|
||||||
|
* @param temperature The room temperature.
|
||||||
|
* @param humidity The room humidity.
|
||||||
|
*
|
||||||
|
* @note The temperature and humidity are not used in this function but are required by the ClimateCard class to match the signature of the callback function.
|
||||||
|
*/
|
||||||
void ClimateIoT::handleSensorUpdate(float temperature, float humidity) {
|
void ClimateIoT::handleSensorUpdate(float temperature, float humidity) {
|
||||||
this->publishSensor();
|
this->publishSensor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function is a callback function registered with the Climate card to be called when the air conditioner state is updated.
|
||||||
|
*
|
||||||
|
* @param mode The mode of the air conditioner.
|
||||||
|
* @param fan_speed The fan speed of the air conditioner.
|
||||||
|
* @param temperature The temperature of the air conditioner.
|
||||||
|
*/
|
||||||
void ClimateIoT::handleAirConditionerUpdate(uint8_t mode, uint8_t fan_speed, uint8_t temperature) {
|
void ClimateIoT::handleAirConditionerUpdate(uint8_t mode, uint8_t fan_speed, uint8_t temperature) {
|
||||||
this->publishClimate();
|
this->publishClimate();
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#include <ExpansionCard.hpp>
|
#include <ExpansionCard.hpp>
|
||||||
#include <ClimateCard.hpp>
|
#include <ClimateCard.hpp>
|
||||||
|
|
||||||
|
// MQTT Topics
|
||||||
#define AC_MODE_REPORT_TOPIC "mode"
|
#define AC_MODE_REPORT_TOPIC "mode"
|
||||||
#define AC_MODE_SET_TOPIC "set/mode"
|
#define AC_MODE_SET_TOPIC "set/mode"
|
||||||
#define AC_TEMPERATURE_REPORT_TOPIC "temperature"
|
#define AC_TEMPERATURE_REPORT_TOPIC "temperature"
|
||||||
|
|
|
@ -1,15 +1,33 @@
|
||||||
#include <DigitalInputCard.hpp>
|
#include <DigitalInputCard.hpp>
|
||||||
|
|
||||||
// Instantiate the card with the specified address
|
/**
|
||||||
|
* @brief Create a new Digital Input Card object with the specified address
|
||||||
|
* @note If you are using the ESPMegaI/O board, you should use the dip switch constructor
|
||||||
|
*
|
||||||
|
* @param address_a The ESPMegaI/O address of bank A
|
||||||
|
* @param address_b The ESPMegaI/O address of bank B
|
||||||
|
*/
|
||||||
DigitalInputCard::DigitalInputCard(uint8_t address_a, uint8_t address_b) : callbacks()
|
DigitalInputCard::DigitalInputCard(uint8_t address_a, uint8_t address_b) : callbacks()
|
||||||
{
|
{
|
||||||
this->address_a = address_a;
|
this->address_a = address_a;
|
||||||
this->address_b = address_b;
|
this->address_b = address_b;
|
||||||
this->callbacks_handler_index = 0;
|
this->callbacks_handler_index = 0;
|
||||||
}
|
}
|
||||||
// Instantiate the card with the specified position on the dip switch
|
|
||||||
// Bit 0,1,2 are for bank A
|
/**
|
||||||
// Bit 3,4,5 are for bank B
|
* @brief Create a new Digital Input Card object with the specified position on the dip switch
|
||||||
|
*
|
||||||
|
* @note The bit 0 are at the left of the dip switch
|
||||||
|
*
|
||||||
|
* @warning There are 6 switches on the dip switch, 3 for bank A and 3 for bank B, They should be unique for each bank accross all the cards
|
||||||
|
*
|
||||||
|
* @param bit0 The position of the first switch on the dip switch
|
||||||
|
* @param bit1 The position of the second switch on the dip switch
|
||||||
|
* @param bit2 The position of the third switch on the dip switch
|
||||||
|
* @param bit3 The position of the fourth switch on the dip switch
|
||||||
|
* @param bit4 The position of the fifth switch on the dip switch
|
||||||
|
* @param bit5 The position of the sixth switch on the dip switch
|
||||||
|
*/
|
||||||
DigitalInputCard::DigitalInputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4, bool bit5)
|
DigitalInputCard::DigitalInputCard(bool bit0, bool bit1, bool bit2, bool bit3, bool bit4, bool bit5)
|
||||||
{
|
{
|
||||||
this->address_a = 0x20;
|
this->address_a = 0x20;
|
||||||
|
@ -29,7 +47,12 @@ DigitalInputCard::DigitalInputCard(bool bit0, bool bit1, bool bit2, bool bit3, b
|
||||||
if (bit5)
|
if (bit5)
|
||||||
this->address_b += 4;
|
this->address_b += 4;
|
||||||
}
|
}
|
||||||
// Initialize the card
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the Digital Input Card
|
||||||
|
*
|
||||||
|
* @return True if the initialization is successful, false otherwise
|
||||||
|
*/
|
||||||
bool DigitalInputCard::begin()
|
bool DigitalInputCard::begin()
|
||||||
{
|
{
|
||||||
this->inputBankA = PCF8574(this->address_a);
|
this->inputBankA = PCF8574(this->address_a);
|
||||||
|
@ -56,12 +79,25 @@ bool DigitalInputCard::begin()
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Refresh and Read the input from the specified pin, always refresh the input buffers
|
|
||||||
|
/**
|
||||||
|
* @brief Read the input from the specified pin, always refresh the input buffers
|
||||||
|
*
|
||||||
|
* @param pin The pin to read from
|
||||||
|
* @return True if the pin is HIGH, false if the pin is LOW
|
||||||
|
*/
|
||||||
bool DigitalInputCard::digitalRead(uint8_t pin)
|
bool DigitalInputCard::digitalRead(uint8_t pin)
|
||||||
{
|
{
|
||||||
return this->digitalRead(pin, true);
|
return this->digitalRead(pin, true);
|
||||||
}
|
}
|
||||||
// Read the input from the specified pin, also refresh the input buffers if refresh is true
|
|
||||||
|
/**
|
||||||
|
* @brief Read the input from the specified pin, also refresh the input buffers if refresh is true
|
||||||
|
*
|
||||||
|
* @param pin The pin to read from
|
||||||
|
* @param refresh If true, the input buffers will be refreshed before reading the pin
|
||||||
|
* @return True if the pin is HIGH, false if the pin is LOW
|
||||||
|
*/
|
||||||
bool DigitalInputCard::digitalRead(uint8_t pin, bool refresh)
|
bool DigitalInputCard::digitalRead(uint8_t pin, bool refresh)
|
||||||
{
|
{
|
||||||
pin = pinMap[pin];
|
pin = pinMap[pin];
|
||||||
|
@ -85,6 +121,15 @@ bool DigitalInputCard::digitalRead(uint8_t pin, bool refresh)
|
||||||
return 255;
|
return 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the specified pin changed since the last call to this function
|
||||||
|
*
|
||||||
|
* @note This function compares the current input buffer with the previous input buffer to detect changes
|
||||||
|
*
|
||||||
|
* @param pin The pin to check
|
||||||
|
* @param currentBuffer The current input buffer
|
||||||
|
* @param previousBuffer The previous input buffer
|
||||||
|
*/
|
||||||
void DigitalInputCard::handlePinChange(int pin, uint8_t ¤tBuffer, uint8_t &previousBuffer)
|
void DigitalInputCard::handlePinChange(int pin, uint8_t ¤tBuffer, uint8_t &previousBuffer)
|
||||||
{
|
{
|
||||||
// Get the index of the pin in the pin map
|
// Get the index of the pin in the pin map
|
||||||
|
@ -113,7 +158,11 @@ void DigitalInputCard::handlePinChange(int pin, uint8_t ¤tBuffer, uint8_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A loop to refresh the input buffers and check for pin changes
|
||||||
|
*
|
||||||
|
* @note Although this function can be called in the main loop, it is recommended install the card in ESPMega to automatically manage the loop
|
||||||
|
*/
|
||||||
// Preform a loop to refresh the input buffers
|
// Preform a loop to refresh the input buffers
|
||||||
void DigitalInputCard::loop()
|
void DigitalInputCard::loop()
|
||||||
{
|
{
|
||||||
|
@ -134,7 +183,12 @@ void DigitalInputCard::loop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Get the input buffer for bank A
|
|
||||||
|
/**
|
||||||
|
* @brief Get the input buffer for bank A (the first 8 pins)
|
||||||
|
*
|
||||||
|
* @return The input buffer for bank A where the first bit is the first pin and the last bit is the last pin
|
||||||
|
*/
|
||||||
uint8_t DigitalInputCard::getInputBufferA()
|
uint8_t DigitalInputCard::getInputBufferA()
|
||||||
{
|
{
|
||||||
// Rearrange the bits to match the pin map
|
// Rearrange the bits to match the pin map
|
||||||
|
@ -145,7 +199,12 @@ uint8_t DigitalInputCard::getInputBufferA()
|
||||||
}
|
}
|
||||||
return inputBufferA_rearranged;
|
return inputBufferA_rearranged;
|
||||||
}
|
}
|
||||||
// Get the input buffer for bank B
|
|
||||||
|
/**
|
||||||
|
* @brief Get the input buffer for bank B (the last 8 pins)
|
||||||
|
*
|
||||||
|
* @return The input buffer for bank B where the first bit is the first pin and the last bit is the last pin
|
||||||
|
*/
|
||||||
uint8_t DigitalInputCard::getInputBufferB()
|
uint8_t DigitalInputCard::getInputBufferB()
|
||||||
{
|
{
|
||||||
// Rearrange the bits to match the pin map
|
// Rearrange the bits to match the pin map
|
||||||
|
@ -156,35 +215,71 @@ uint8_t DigitalInputCard::getInputBufferB()
|
||||||
}
|
}
|
||||||
return inputBufferB_rearranged;
|
return inputBufferB_rearranged;
|
||||||
}
|
}
|
||||||
// Register a callback function to be called when a pin changes
|
|
||||||
|
/**
|
||||||
|
* @brief Register a callback function to be called when a pin changes
|
||||||
|
*
|
||||||
|
* @param callback The callback function to be called
|
||||||
|
* @return The handler of the callback function
|
||||||
|
*/
|
||||||
uint8_t DigitalInputCard::registerCallback(std::function<void(uint8_t, bool)> callback)
|
uint8_t DigitalInputCard::registerCallback(std::function<void(uint8_t, bool)> callback)
|
||||||
{
|
{
|
||||||
callbacks[this->callbacks_handler_index] = callback;
|
callbacks[this->callbacks_handler_index] = callback;
|
||||||
return this->callbacks_handler_index++;
|
return this->callbacks_handler_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh the input buffer for bank A
|
/**
|
||||||
|
* @brief Read the input state from the input ic and store it in the input buffer for bank A
|
||||||
|
*/
|
||||||
void DigitalInputCard::refreshInputBankA()
|
void DigitalInputCard::refreshInputBankA()
|
||||||
{
|
{
|
||||||
inputBufferA = inputBankA.read8();
|
inputBufferA = inputBankA.read8();
|
||||||
}
|
}
|
||||||
// Refresh the input buffer for bank B
|
|
||||||
|
/**
|
||||||
|
* @brief Read the input state from the input ic and store it in the input buffer for bank B
|
||||||
|
*/
|
||||||
void DigitalInputCard::refreshInputBankB()
|
void DigitalInputCard::refreshInputBankB()
|
||||||
{
|
{
|
||||||
inputBufferB = inputBankB.read8();
|
inputBufferB = inputBankB.read8();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the debounce time for the specified pin
|
||||||
|
*
|
||||||
|
* Debounce is the time in milliseconds that the pin should be stable before the callback function is called
|
||||||
|
* This is useful to prevent false triggers when the input is noisy
|
||||||
|
* An example of this is when the input is connected to a mechanical switch
|
||||||
|
*
|
||||||
|
* @param pin The pin to set the debounce time for
|
||||||
|
* @param debounceTime The debounce time in milliseconds
|
||||||
|
*/
|
||||||
void DigitalInputCard::setDebounceTime(uint8_t pin, uint32_t debounceTime)
|
void DigitalInputCard::setDebounceTime(uint8_t pin, uint32_t debounceTime)
|
||||||
{
|
{
|
||||||
pin = pinMap[pin];
|
pin = pinMap[pin];
|
||||||
this->debounceTime[pin] = debounceTime;
|
this->debounceTime[pin] = debounceTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregister a callback function
|
||||||
|
*
|
||||||
|
* @param handler The handler of the callback function to unregister
|
||||||
|
*/
|
||||||
void DigitalInputCard::unregisterCallback(uint8_t handler)
|
void DigitalInputCard::unregisterCallback(uint8_t handler)
|
||||||
{
|
{
|
||||||
callbacks.erase(handler);
|
callbacks.erase(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Load the pin map for the card
|
||||||
|
*
|
||||||
|
* A pin map is an array of 16 elements that maps the physical pins to virtual pins
|
||||||
|
* The virtual pins are the pins that are used in the callback functions and are used for all the functions in this class
|
||||||
|
* The physical pins are the pins on the Input IC, This can be found on the schematic of the ESPMegaI/O board
|
||||||
|
* This function is useful if you want to change the number identification of the pins to match your project needs
|
||||||
|
*
|
||||||
|
* @param pinMap The pin map to load
|
||||||
|
*/
|
||||||
void DigitalInputCard::loadPinMap(uint8_t pinMap[16])
|
void DigitalInputCard::loadPinMap(uint8_t pinMap[16])
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
|
@ -196,6 +291,11 @@ void DigitalInputCard::loadPinMap(uint8_t pinMap[16])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the type of the card
|
||||||
|
*
|
||||||
|
* @return The type of the card
|
||||||
|
*/
|
||||||
uint8_t DigitalInputCard::getType()
|
uint8_t DigitalInputCard::getType()
|
||||||
{
|
{
|
||||||
return CARD_TYPE_DIGITAL_INPUT;
|
return CARD_TYPE_DIGITAL_INPUT;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <PCF8574.h>
|
#include <PCF8574.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
// Card Type
|
||||||
#define CARD_TYPE_DIGITAL_INPUT 0x01
|
#define CARD_TYPE_DIGITAL_INPUT 0x01
|
||||||
|
|
||||||
class DigitalInputCard : public ExpansionCard {
|
class DigitalInputCard : public ExpansionCard {
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
#include <DigitalInputIoT.hpp>
|
#include <DigitalInputIoT.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initializes the DigitalInputIoT object.
|
||||||
|
*
|
||||||
|
* This function sets the necessary parameters for the DigitalInputIoT object, such as the card ID, expansion card, MQTT client, and base topic.
|
||||||
|
* It also enables the publishing of digital input values and registers a callback function for handling value changes.
|
||||||
|
*
|
||||||
|
* @note Although this function can be called in the main program, it is recommended to use ESPMegaIoT::registerCard() to automatically manage the instantiation and initialization of this component.
|
||||||
|
*
|
||||||
|
* @param card_id The ID of the card.
|
||||||
|
* @param card Pointer to the DigitalInputCard object.
|
||||||
|
* @param mqtt Pointer to the PubSubClient object.
|
||||||
|
* @param base_topic The base topic for MQTT communication.
|
||||||
|
* @return True if the initialization is successful, false otherwise.
|
||||||
|
*/
|
||||||
bool DigitalInputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
bool DigitalInputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *mqtt, char *base_topic) {
|
||||||
this->card = (DigitalInputCard *)card;
|
this->card = (DigitalInputCard *)card;
|
||||||
this->card_id = card_id;
|
this->card_id = card_id;
|
||||||
|
@ -12,10 +26,19 @@ bool DigitalInputIoT::begin(uint8_t card_id, ExpansionCard *card, PubSubClient *
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subscribes to the MQTT topics for the DigitalInputIoT component.
|
||||||
|
*/
|
||||||
void DigitalInputIoT::subscribe() {
|
void DigitalInputIoT::subscribe() {
|
||||||
this->subscribeRelative(PUBLISH_ENABLE_TOPIC);
|
this->subscribeRelative(PUBLISH_ENABLE_TOPIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles MQTT messages for the DigitalInputIoT component.
|
||||||
|
*
|
||||||
|
* @param topic The trimmed topic of the MQTT message.
|
||||||
|
* @param payload The null-terminated payload of the MQTT message.
|
||||||
|
*/
|
||||||
void DigitalInputIoT::handleMqttMessage(char *topic, char *payload) {
|
void DigitalInputIoT::handleMqttMessage(char *topic, char *payload) {
|
||||||
// payload is char '0' or '1'
|
// payload is char '0' or '1'
|
||||||
if (!strcmp(topic, PUBLISH_ENABLE_TOPIC)) {
|
if (!strcmp(topic, PUBLISH_ENABLE_TOPIC)) {
|
||||||
|
@ -26,6 +49,10 @@ void DigitalInputIoT::handleMqttMessage(char *topic, char *payload) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publish all digital inputs to the MQTT broker.
|
||||||
|
*/
|
||||||
void DigitalInputIoT::publishDigitalInputs() {
|
void DigitalInputIoT::publishDigitalInputs() {
|
||||||
if (!this->digital_inputs_publish_enabled) {
|
if (!this->digital_inputs_publish_enabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -34,18 +61,41 @@ void DigitalInputIoT::publishDigitalInputs() {
|
||||||
this->publishDigitalInput(i);
|
this->publishDigitalInput(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set if the digital inputs should be published to the MQTT broker.
|
||||||
|
*
|
||||||
|
* @param enabled True if the digital inputs should be published, false otherwise.
|
||||||
|
*/
|
||||||
void DigitalInputIoT::setDigitalInputsPublishEnabled(bool enabled) {
|
void DigitalInputIoT::setDigitalInputsPublishEnabled(bool enabled) {
|
||||||
this->digital_inputs_publish_enabled = enabled;
|
this->digital_inputs_publish_enabled = enabled;
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
this->publishDigitalInputs();
|
this->publishDigitalInputs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles a value change for a digital input.
|
||||||
|
*
|
||||||
|
* @note This function is registered as a callback function for the DigitalInputCard object.
|
||||||
|
*
|
||||||
|
* @param pin The pin that changed.
|
||||||
|
* @param value The new value of the pin.
|
||||||
|
*/
|
||||||
void DigitalInputIoT::handleValueChange(uint8_t pin, uint8_t value) {
|
void DigitalInputIoT::handleValueChange(uint8_t pin, uint8_t value) {
|
||||||
if (this->digital_inputs_publish_enabled) {
|
if (this->digital_inputs_publish_enabled) {
|
||||||
this->publishDigitalInput(pin);
|
this->publishDigitalInput(pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publish all inputs to the MQTT Broker
|
||||||
|
*
|
||||||
|
* @note This function is overriden from the IoTComponent class and is called by ESPMegaIoT.
|
||||||
|
*
|
||||||
|
* @param pin The pin to publish.
|
||||||
|
*/
|
||||||
void DigitalInputIoT::publishReport() {
|
void DigitalInputIoT::publishReport() {
|
||||||
this->publishDigitalInputs();
|
this->publishDigitalInputs();
|
||||||
}
|
}
|
||||||
|
@ -53,7 +103,11 @@ uint8_t DigitalInputIoT::getType() {
|
||||||
return CARD_TYPE_DIGITAL_INPUT;
|
return CARD_TYPE_DIGITAL_INPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Publish a digital input to the MQTT broker.
|
||||||
|
*
|
||||||
|
* @param pin The pin to publish.
|
||||||
|
*/
|
||||||
void DigitalInputIoT::publishDigitalInput(uint8_t pin) {
|
void DigitalInputIoT::publishDigitalInput(uint8_t pin) {
|
||||||
char topic[20] = {0};
|
char topic[20] = {0};
|
||||||
char payload[20] = {0};
|
char payload[20] = {0};
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <DigitalInputCard.hpp>
|
#include <DigitalInputCard.hpp>
|
||||||
#include <FRAM.h>
|
#include <FRAM.h>
|
||||||
|
|
||||||
|
// MQTT Topics
|
||||||
#define PUBLISH_ENABLE_TOPIC "publish_enable"
|
#define PUBLISH_ENABLE_TOPIC "publish_enable"
|
||||||
|
|
||||||
class DigitalInputIoT : public IoTComponent {
|
class DigitalInputIoT : public IoTComponent {
|
||||||
|
|
Loading…
Reference in New Issue