From 3bb09d3a33d844496d50d543c6e66321f0bd4ff3 Mon Sep 17 00:00:00 2001 From: Scott Perkins Date: Thu, 4 Jul 2024 23:43:45 -0600 Subject: [PATCH] Added Subscriber --- src/common/thermostat_common.hpp | 30 +++++++----- src/main.cpp | 36 +++++++++----- src/mqtt/mqtt.cpp | 42 +++++++++++++++- src/mqtt/mqtt.hpp | 8 ++- src/repl/repl.cpp | 2 +- src/test_main.cpp | 49 ------------------- src/thermostat/thermostat.cpp | 48 ++++++++---------- src/thermostat/thermostat.hpp | 16 +++--- src/thermostat_context/thermostat_context.hpp | 4 +- 9 files changed, 124 insertions(+), 111 deletions(-) delete mode 100644 src/test_main.cpp diff --git a/src/common/thermostat_common.hpp b/src/common/thermostat_common.hpp index dade2b8..0ead132 100644 --- a/src/common/thermostat_common.hpp +++ b/src/common/thermostat_common.hpp @@ -5,8 +5,8 @@ #define THERMOSTAT_VERSION "1.0.0" - -typedef enum ThermostatError { +typedef enum ThermostatError +{ THERMOSTAT_OK = 0, THERMOSTAT_NO_DATA, THERMOSTAT_ERROR, @@ -35,7 +35,8 @@ inline const char *thermostatErrorToString(ThermostatError error) } } -enum ThermostatCommandType { +enum ThermostatCommandType +{ SET_TEMPERATURE, SET_MODE, SET_UNITS, @@ -48,14 +49,19 @@ enum ThermostatCommandType { INVALID_COMMAND }; -struct ThermostatCommand { +class ThermostatCommand +{ +public: ThermostatCommandType command_type; double parameter; std::string command_string; std::string resultString; }; -typedef enum TemperatureState { +typedef void (*CommandCallback)(ThermostatCommand *command, void *arg); + +typedef enum TemperatureState +{ OVER_TEMPERATURE, UNDER_TEMPERATURE, UNDER_TEMPERATURE_IN_RANGE, @@ -63,7 +69,6 @@ typedef enum TemperatureState { IN_RANGE } TemperatureState; - inline const char *temperatureStateToString(TemperatureState state) { switch (state) @@ -83,7 +88,8 @@ inline const char *temperatureStateToString(TemperatureState state) } } -typedef enum ThermostatState { +typedef enum ThermostatState +{ HEATING, COOLING, FAN_ON, @@ -108,7 +114,8 @@ inline const char *hvacStateToString(ThermostatState state) } } -typedef enum ThermostatMode { +typedef enum ThermostatMode +{ HEAT, COOL, FAN_ONLY, @@ -146,7 +153,8 @@ inline const char *thermostatModeToString(ThermostatMode mode) } } -typedef enum TemperatureUnits { +typedef enum TemperatureUnits +{ FAHRENHEIT, CELSIUS } TemperatureUnits; @@ -164,7 +172,6 @@ inline const char *temperatureUnitsToString(TemperatureUnits units) } } - double convertFahrenheitToCelsius(double fahrenheit); double convertCelsiusToFahrenheit(double celsius); @@ -178,7 +185,8 @@ inline double convertCelsiusToFahrenheit(double celsius) return celsius * 9.0 / 5.0 + 32; } -struct ThermostatData { +struct ThermostatData +{ double currentTemperature; double targetTemperature; double temperatureRange; diff --git a/src/main.cpp b/src/main.cpp index bc1fd0d..4fa1c85 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,11 +19,21 @@ #include "wifi.hpp" #include "mqtt.hpp" #include "watchdog.hpp" +#include "thermostat_context.hpp" +bool thermostat_timer_callback(struct repeating_timer *t) { + ThermostatContext *ctx = (ThermostatContext *) t->user_data; + ctx->thermostat->update(); + ThermostatData data; + ctx->thermostat->getData(&data); + ctx->producer->update(&data); + return true; +} -void commandLoop() -{ - +void command_callback(ThermostatCommand *command, void *arg) { + ThermostatContext *ctx = (ThermostatContext *) arg; + ctx->commandParser->parseString(command); + ctx->thermostat->executeCommand(command); } int main() @@ -45,9 +55,9 @@ int main() Mqtt mqtt; Watchdog watchdog; Producer producer; - Thermostat thermostat; CommandParser commandParser; Repl repl; + Thermostat thermostat; // Set up the application context @@ -67,24 +77,28 @@ int main() context.producer = &producer; context.commandParser = &commandParser; context.repl = &repl; + context.thermostat = &thermostat; ThermostatError err = context.initialize(); - thermostat.initialize(&context); + + + err = wifi.connect(); + err = mqtt.connect(); + + err = mqtt.subscribe("home/thermostat/command", command_callback, &context); - err = thermostat.connect(); + struct repeating_timer timer; + add_repeating_timer_ms(-5000, thermostat_timer_callback, &context, &timer); while (true) { ThermostatCommand command; ThermostatError err = repl.read(&command); + sleep_ms(10); if (err == THERMOSTAT_OK) { thermostat.executeCommand(&command); repl.print(&command); } - thermostat.update(); - ThermostatData currentData; - thermostat.getData(¤tData); - producer.update(¤tData); - sleep_ms(1000); + } } diff --git a/src/mqtt/mqtt.cpp b/src/mqtt/mqtt.cpp index e94b095..4013181 100644 --- a/src/mqtt/mqtt.cpp +++ b/src/mqtt/mqtt.cpp @@ -1,10 +1,40 @@ #include "mqtt.hpp" #include "lwip/apps/mqtt.h" #include "pico/cyw43_arch.h" +#include "pico/stdlib.h" static mqtt_client_t *client; + +static void publish_callback(void *arg, const char *topic, u32_t tot_len) +{ + Mqtt *mqtt = (Mqtt *)arg; + printf("Publish callback\n"); + mqtt->commandBuffer[0] = '\0'; + mqtt->messageLength = tot_len; + mqtt->currentWriteIndex = 0; +} + +static void data_callback(void *arg, const u8_t *data, u16_t len, u8_t flags) +{ + Mqtt *mqtt = (Mqtt *)arg; + printf("Data callback\n"); + for (int i = 0; i < len; i++) + { + mqtt->commandBuffer[mqtt->currentWriteIndex++] = data[i]; + } + mqtt->commandBuffer[mqtt->currentWriteIndex] = '\0'; + if (flags & MQTT_DATA_FLAG_LAST) + { + ThermostatCommand *command = new ThermostatCommand(); + std::string inputString = std::string((char *)mqtt->commandBuffer); + command->command_string = inputString; + mqtt->executeCallback(command); + } +} + + Mqtt::Mqtt() { @@ -19,6 +49,7 @@ ThermostatError Mqtt::initialize(Configuration *newConfig) { configuration = newConfig; client = mqtt_client_new(); + mqtt_set_inpub_callback(client, publish_callback, data_callback, this); return THERMOSTAT_OK; } @@ -64,11 +95,20 @@ ThermostatError Mqtt::publish(const char *topic, char *message) return THERMOSTAT_OK; } -ThermostatError Mqtt::subscribe(const char *topic) +ThermostatError Mqtt::subscribe(const char *topic, CommandCallback callback, void *arg) { + commandCallback = callback; + callbackArg = arg; + err_t err = mqtt_subscribe(client, topic, 0, NULL, NULL); return THERMOSTAT_OK; } +void Mqtt::executeCallback(ThermostatCommand *command) +{ + commandCallback(command, callbackArg); +} + + bool Mqtt::isConnected() { return mqtt_client_is_connected(client); diff --git a/src/mqtt/mqtt.hpp b/src/mqtt/mqtt.hpp index e35c8e4..0a1f04b 100644 --- a/src/mqtt/mqtt.hpp +++ b/src/mqtt/mqtt.hpp @@ -11,11 +11,17 @@ class Mqtt { ThermostatError initialize(Configuration *configuration); ThermostatError connect(); ThermostatError publish(const char *topic, char *message); - ThermostatError subscribe(const char *topic); + ThermostatError subscribe(const char *topic, CommandCallback, void *arg); bool isConnected(); + void executeCallback(ThermostatCommand *command); + uint8_t commandBuffer[1024]; + uint32_t messageLength; + uint32_t currentWriteIndex; private: bool initalized; Configuration *configuration; + CommandCallback commandCallback; + void *callbackArg; }; diff --git a/src/repl/repl.cpp b/src/repl/repl.cpp index 3139f5c..2fb91d4 100644 --- a/src/repl/repl.cpp +++ b/src/repl/repl.cpp @@ -66,8 +66,8 @@ ThermostatError Repl::read(ThermostatCommand *command) std::string inputString = std::string((char *)buffer); command->command_string = inputString; - commandParser->parseString(command); + return THERMOSTAT_OK; } return THERMOSTAT_NO_DATA; diff --git a/src/test_main.cpp b/src/test_main.cpp deleted file mode 100644 index 385c328..0000000 --- a/src/test_main.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - - -#include -#include "pico/stdlib.h" -#include "hardware/uart.h" - - -#define UART_ID uart0 -#define BAUD_RATE 115200 - -// We are using pins 0 and 1, but see the GPIO function select table in the -// datasheet for information on which other pins can be used. -#define UART_TX_PIN 0 -#define UART_RX_PIN 1 - -int main() { - stdio_init_all(); - - // Set up our UART with the required speed. - uart_init(UART_ID, BAUD_RATE); - - // Set the TX and RX pins by using the function select on the GPIO - // Set datasheet for more information on function select - gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART); - gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); - - - - while (true) { - if(uart_is_readable(UART_ID)) { - int i = 0; - uint8_t buffer[100]; - while (uart_is_readable(UART_ID)) { - uint8_t character = uart_getc(UART_ID); - buffer[i] = character; - i++; - } - - printf("Received: %s\n", buffer); - } - sleep_ms(1000); - } -} - diff --git a/src/thermostat/thermostat.cpp b/src/thermostat/thermostat.cpp index abfc2b2..77c4b07 100644 --- a/src/thermostat/thermostat.cpp +++ b/src/thermostat/thermostat.cpp @@ -15,10 +15,12 @@ Thermostat::~Thermostat() { } -ThermostatError Thermostat::initialize(ThermostatContext *context) +ThermostatError Thermostat::initialize(EnvironmentSensor *environmentSensor, TemperatureController *tempController, Hvac *hvac) { - this->context = context; + this->environmentSensor = environmentSensor; + this->tempController = tempController; + this->hvac = hvac; temperatureUnits = FAHRENHEIT; initialized = false; @@ -38,19 +40,6 @@ bool Thermostat::isInitialized() return initialized; } -ThermostatError Thermostat::connect() { - int num_retries = 0; - while (context->wifi->connect() != THERMOSTAT_OK) { - if (num_retries > 3) { - return THERMOSTAT_CONNECTION_ERROR; - } - num_retries++; - } - - context->mqtt->connect(); - - return THERMOSTAT_OK; -} ThermostatError Thermostat::setTemperatureUnits(TemperatureUnits newUnits) { @@ -95,12 +84,12 @@ ThermostatError Thermostat::setMode(ThermostatMode newMode) ThermostatError Thermostat::setTargetTemperature(double targetTemperature) { double temperatureInStandardUnits = getTemperatureInStandardUnits(targetTemperature); - return context->tempController->setTargetTemperature(temperatureInStandardUnits); + return tempController->setTargetTemperature(temperatureInStandardUnits); } double Thermostat::getTargetTemperature() { - double temperatureInStandardUnits = context->tempController->getTargetTemperature(); + double temperatureInStandardUnits = tempController->getTargetTemperature(); return getTemperatureInCurrentUnits(temperatureInStandardUnits); } @@ -170,7 +159,7 @@ ThermostatError Thermostat::updateTemperatureHumidity() return currentError; } - context->sensor->readTemperatureHumidity(¤tTemperature, ¤tHumidity); + environmentSensor->readTemperatureHumidity(¤tTemperature, ¤tHumidity); return validateReading(); } @@ -211,11 +200,11 @@ ThermostatError Thermostat::update() return currentError; } - TemperatureState temperatureState = context->tempController->checkTemperature(currentTemperature); + TemperatureState temperatureState = tempController->checkTemperature(currentTemperature); - ThermostatState desiredState = getDesiredHVACState(temperatureState, context->hvac->getCurrentState()); + ThermostatState desiredState = getDesiredHVACState(temperatureState, hvac->getCurrentState()); - currentError = context->hvac->setDesiredState(desiredState); + currentError = hvac->setDesiredState(desiredState); return currentError; } @@ -227,11 +216,11 @@ ThermostatError Thermostat::getData(ThermostatData *currentState) currentState->currentTemperature = this->getTemperatureInCurrentUnits(currentTemperature); currentState->currentTemperatureStandardUnits = this->currentTemperature; currentState->targetTemperature = this->getTargetTemperature(); - currentState->targetTemperatureStandardUnits = context->tempController->getTargetTemperature(); - currentState->temperatureRange = context->tempController->getTemperatureRange(); + currentState->targetTemperatureStandardUnits = tempController->getTargetTemperature(); + currentState->temperatureRange = tempController->getTemperatureRange(); currentState->currentHumidity = this->currentHumidity; - currentState->temperatureState = context->tempController->checkTemperature(currentTemperature); - currentState->hvacState = context->hvac->getCurrentState(); + currentState->temperatureState = tempController->checkTemperature(currentTemperature); + currentState->hvacState = hvac->getCurrentState(); currentState->error = currentError; return THERMOSTAT_OK; @@ -243,9 +232,9 @@ ThermostatError Thermostat::printState(std::string *output) std::ostringstream oss; oss << "Current Temperature: " << getTemperatureInCurrentUnits(currentTemperature) << std::endl; - oss << "Target Temperature " << getTemperatureInCurrentUnits(context->tempController->getTargetTemperature()) << std::endl; + oss << "Target Temperature " << getTemperatureInCurrentUnits(tempController->getTargetTemperature()) << std::endl; oss << "Current Humidity: " << currentHumidity << std::endl; - oss << "Current HVAC State: " << hvacStateToString(context->hvac->getCurrentState()) << std::endl; + oss << "Current HVAC State: " << hvacStateToString(hvac->getCurrentState()) << std::endl; oss << "Current Thermostat Mode: " << thermostatModeToString(mode) << std::endl; oss << "Current Temperature Units: " << temperatureUnitsToString(temperatureUnits) << std::endl; @@ -309,11 +298,14 @@ ThermostatError Thermostat::printState(std::string *output) ThermostatError Thermostat::executeCommand(ThermostatCommand *command) { + ThermostatError error = THERMOSTAT_OK; + + if (command->command_type == INVALID_COMMAND) { command->resultString = "[ERROR] Invalid command"; return THERMOSTAT_OK; } - ThermostatError error = THERMOSTAT_OK; + std::ostringstream oss; switch (command->command_type) { case HELP: diff --git a/src/thermostat/thermostat.hpp b/src/thermostat/thermostat.hpp index 8a86f80..7cac90f 100644 --- a/src/thermostat/thermostat.hpp +++ b/src/thermostat/thermostat.hpp @@ -1,23 +1,21 @@ #ifndef THERMOSTAT_HPP #define THERMOSTAT_HPP +#include + #include "environment_sensor.hpp" #include "hvac.hpp" -#include "wifi.hpp" -#include "mqtt.hpp" -#include "watchdog.hpp" +#include "temperature_controller.hpp" #include "thermostat_common.hpp" -#include "thermostat_context.hpp" -#include + class Thermostat { public: Thermostat(); ~Thermostat(); - ThermostatError initialize(ThermostatContext *context); + ThermostatError initialize(EnvironmentSensor *environmentSensor, TemperatureController *tempController, Hvac *hvac); bool isInitialized(); - ThermostatError connect(); ThermostatError setTemperatureUnits(TemperatureUnits temperatureUnits); TemperatureUnits getTemperatureUnits(); ThermostatState getDesiredHVACState(TemperatureState temperatureState, ThermostatState currentHVACState); @@ -39,7 +37,9 @@ class Thermostat { ThermostatMode mode; bool initialized; - ThermostatContext *context; + EnvironmentSensor *environmentSensor; + TemperatureController *tempController; + Hvac *hvac; double currentTemperature; double currentHumidity; diff --git a/src/thermostat_context/thermostat_context.hpp b/src/thermostat_context/thermostat_context.hpp index 41b2f61..6d95fc2 100644 --- a/src/thermostat_context/thermostat_context.hpp +++ b/src/thermostat_context/thermostat_context.hpp @@ -38,9 +38,9 @@ class ThermostatContext Repl *repl; Producer *producer; + Thermostat *thermostat; ThermostatError initialize() { - sensor->initialize(i2cDevice); tempController->initialize(); hvac->initialize(heatSwitch, coolSwitch, fanSwitch); wifi->initialize(config); @@ -51,6 +51,8 @@ class ThermostatContext i2cBus->initialize(); i2cDevice->initialize(i2cBus, 0x38); repl->initialize(commandParser); + sensor->initialize(i2cDevice); + thermostat->initialize(sensor, tempController, hvac); return THERMOSTAT_OK; } };