diff --git a/src/App/App.cpp b/src/App/App.cpp index a0f86a3..a9f4a0c 100644 --- a/src/App/App.cpp +++ b/src/App/App.cpp @@ -9,7 +9,7 @@ namespace Dough } App::App() : config(), - ui(), + ui(onoffButtonInterruptCallback, setupButtonInterruptCallback), wifi(), mqtt(&wifi, mqttOnConnectCallback, mqttOnMessageCallback), temperatureSensor( @@ -32,9 +32,6 @@ namespace Dough void App::setup() { ui.setup(); - ui.onoffButton.onInterrupt(::onoffButtonInterruptCallback); - ui.setupButton.onInterrupt(::setupButtonInterruptCallback); - wifi.setup(); mqtt.setup(); temperatureSensor.setup(); @@ -63,60 +60,4 @@ namespace Dough humiditySensor.clearHistory(); distanceSensor.clearHistory(); } -} // namespace Dough - -Dough::Logger callbackLogger("CALLBACK"); - -void mqttOnConnectCallback(Dough::MQTT *mqtt) -{ - callbackLogger.log("s", "MQTT connection establish, subscribing to topics"); - mqtt->subscribe("container_height"); - mqtt->subscribe("temperature_offset"); -} - -void mqttOnMessageCallback(String &topic, String &payload) -{ - callbackLogger.log("ssss", "MQTT message received: ", topic.c_str(), " = ", payload.c_str()); - - if (topic.endsWith("/container_height")) - { - Dough::App::Instance()->config.setContainerHeight(payload.toInt()); - } - else if (topic.endsWith("/temperature_offset")) - { - Dough::App::Instance()->config.setTemperatureOffset(payload.toInt()); - } - else - { - callbackLogger.log("ss", "ERROR - Unhandled MQTT message, topic = ", topic.c_str()); - } -} - -void onoffButtonInterruptCallback() -{ - Dough::App::Instance()->ui.onoffButton.handleButtonState(); -} - -void setupButtonInterruptCallback() -{ - Dough::App::Instance()->ui.setupButton.handleButtonState(); -} - -void sensorOnMeasureCallback() -{ - Dough::App::Instance()->ui.notifySensorActivity(); -} - -void sensorOnPublishCallback() -{ - Dough::App::Instance()->ui.notifyNetworkActivity(); -} - -// This callback is called when the TC4 timer from the UI code hits an overflow -// interrupt. It is defined outside the Dough namespace, because TC4_Handler is -// a hard-coded root namespace function name. -void TC4_Handler() -{ - Dough::App::Instance()->ui.updateLEDs(); - REG_TC4_INTFLAG = TC_INTFLAG_OVF; // Clear the OVF interrupt flag. -} \ No newline at end of file +} // namespace Dough \ No newline at end of file diff --git a/src/App/App.h b/src/App/App.h index ed50aac..2b88529 100644 --- a/src/App/App.h +++ b/src/App/App.h @@ -4,6 +4,7 @@ #include #include "UI/UI.h" #include "App/Configuration.h" +#include "App/callbacks.h" #include "Data/SensorController.h" #include "Sensors/TemperatureSensor.h" #include "Sensors/HumiditySensor.h" @@ -34,12 +35,4 @@ namespace Dough }; } -// Callback functions that need to live in the global namespace. -void mqttOnConnectCallback(Dough::MQTT *mqtt); -void mqttOnMessageCallback(String &topic, String &payload); -void onoffButtonInterruptCallback(); -void setupButtonInterruptCallback(); -void sensorOnMeasureCallback(); -void sensorOnPublishCallback(); - #endif \ No newline at end of file diff --git a/src/App/callbacks.cpp b/src/App/callbacks.cpp new file mode 100644 index 0000000..3e3b2ae --- /dev/null +++ b/src/App/callbacks.cpp @@ -0,0 +1,57 @@ +#include "App/callbacks.h" + +Dough::Logger callbackLogger("CALLBACK"); + +void mqttOnConnectCallback(Dough::MQTT *mqtt) +{ + callbackLogger.log("s", "MQTT connection establish, subscribing to topics"); + mqtt->subscribe("container_height"); + mqtt->subscribe("temperature_offset"); +} + +void mqttOnMessageCallback(String &topic, String &payload) +{ + callbackLogger.log("ssss", "MQTT message received: ", topic.c_str(), " = ", payload.c_str()); + + if (topic.endsWith("/container_height")) + { + Dough::App::Instance()->config.setContainerHeight(payload.toInt()); + } + else if (topic.endsWith("/temperature_offset")) + { + Dough::App::Instance()->config.setTemperatureOffset(payload.toInt()); + } + else + { + callbackLogger.log("ss", "ERROR - Unhandled MQTT message, topic = ", topic.c_str()); + } +} + +void onoffButtonInterruptCallback() +{ + Dough::App::Instance()->ui.onoffButton.handleButtonState(); +} + +void setupButtonInterruptCallback() +{ + Dough::App::Instance()->ui.setupButton.handleButtonState(); +} + +void sensorOnMeasureCallback() +{ + Dough::App::Instance()->ui.notifySensorActivity(); +} + +void sensorOnPublishCallback() +{ + Dough::App::Instance()->ui.notifyNetworkActivity(); +} + +// This callback is called when the TC4 timer from the UI code hits an overflow +// interrupt. It is defined outside the Dough namespace, because TC4_Handler is +// a hard-coded root namespace function name. +void TC4_Handler() +{ + Dough::App::Instance()->ui.updateLEDs(); + REG_TC4_INTFLAG = TC_INTFLAG_OVF; // Clear the OVF interrupt flag. +} \ No newline at end of file diff --git a/src/App/callbacks.h b/src/App/callbacks.h new file mode 100644 index 0000000..6f47a98 --- /dev/null +++ b/src/App/callbacks.h @@ -0,0 +1,24 @@ +#ifndef DOUGH_APP_CALLBACKS_H +#define DOUGH_APP_CALLBACKS_H + +#include "Network/MQTT.h" +#include "UI/Logger.h" +#include "App/App.h" + +// This header file defines various callback functions that +// live in the global namespace. All callbacks are bundled here +// to have a clear overview of them. + +// Callbacks from the Dough::MQTT module. +void mqttOnConnectCallback(Dough::MQTT *mqtt); +void mqttOnMessageCallback(String &topic, String &payload); + +// Callbacks from the Dough::UI module. +void onoffButtonInterruptCallback(); +void setupButtonInterruptCallback(); + +// Callbacks from the Dough::SensorController module. +void sensorOnMeasureCallback(); +void sensorOnPublishCallback(); + +#endif \ No newline at end of file diff --git a/src/UI/Button.cpp b/src/UI/Button.cpp index d10a175..ec4d9b2 100644 --- a/src/UI/Button.cpp +++ b/src/UI/Button.cpp @@ -9,19 +9,17 @@ namespace Dough // working. An interrupt service routine (ISR) function must be created // and linked to the button to get the interrupts working. Pattern: // - // // Construct the button instance. - // Button myButton(MYBUTTON_PIN); - // // // A function for handling interrupts. // void myButtonISR() { // myButton.handleButtonState(); // } // - // // Linking the function ot button interrupts. - // myButton.onInterrupt(myButtonISR); - Button::Button(int pin) + // // Construct the button instance. + // Button myButton(MYBUTTON_PIN, myButtonISR); + Button::Button(int pin, ButtonISR isr) { _pin = pin; + attachInterrupt(digitalPinToInterrupt(_pin), isr, CHANGE); } void Button::setup() @@ -29,30 +27,22 @@ namespace Dough pinMode(_pin, INPUT_PULLUP); } - // Assign an interrupt service routine (ISR) for handling button - // interrupts. The provided isr should relay interrupts to the - // handleButtonState() method of this class (see constructor docs). - void Button::onInterrupt(ButtonHandler isr) - { - attachInterrupt(digitalPinToInterrupt(_pin), isr, CHANGE); - } - // Assign an event handler for short and long button presses. // When specific handlers for long and/or short presses are // configured as well, those have precedence over this one. - void Button::onPress(ButtonHandler handler) + void Button::onPress(ButtonISR handler) { _pressHandler = handler; } // Assign an event handler for long button presses. - void Button::onLongPress(ButtonHandler handler) + void Button::onLongPress(ButtonISR handler) { _longPressHandler = handler; } // Assign an event handler for short button presses. - void Button::onShortPress(ButtonHandler handler) + void Button::onShortPress(ButtonISR handler) { _shortPressHandler = handler; } diff --git a/src/UI/Button.h b/src/UI/Button.h index 483e040..57cde4d 100644 --- a/src/UI/Button.h +++ b/src/UI/Button.h @@ -18,7 +18,7 @@ namespace Dough READY_FOR_NEXT_PRESS } ButtonState; - typedef void (*ButtonHandler)(); + typedef void (*ButtonISR)(); // This class provides a simple interface for handling button presses. // Only a few events are supported: @@ -30,21 +30,20 @@ namespace Dough class Button { public: - Button(int pin); + Button(int pin, ButtonISR isr); void setup(); void loop(); - void onInterrupt(ButtonHandler isr); - void onPress(ButtonHandler handler); - void onShortPress(ButtonHandler handler); - void onLongPress(ButtonHandler handler); + void onPress(ButtonISR handler); + void onShortPress(ButtonISR handler); + void onLongPress(ButtonISR handler); void clearEvents(); void handleButtonState(); private: int _pin; - ButtonHandler _pressHandler = nullptr; - ButtonHandler _shortPressHandler = nullptr; - ButtonHandler _longPressHandler = nullptr; + ButtonISR _pressHandler = nullptr; + ButtonISR _shortPressHandler = nullptr; + ButtonISR _longPressHandler = nullptr; bool _debounceState = false; unsigned long _debounceTimer = 0; ButtonState _state = UP; diff --git a/src/UI/UI.cpp b/src/UI/UI.cpp index 9be1dfe..5162e30 100644 --- a/src/UI/UI.cpp +++ b/src/UI/UI.cpp @@ -2,12 +2,14 @@ namespace Dough { - UI::UI() : onoffButton(ONOFF_BUTTON_PIN), - setupButton(SETUP_BUTTON_PIN), - _ledBuiltin(LED_BUILTIN), - _led1(LED1_PIN), - _led2(LED2_PIN), - _led3(LED3_PIN) {} + UI::UI( + ButtonISR onoffButtonISR, + ButtonISR setupButtonISR) : onoffButton(ONOFF_BUTTON_PIN, onoffButtonISR), + setupButton(SETUP_BUTTON_PIN, setupButtonISR), + _ledBuiltin(LED_BUILTIN), + _led1(LED1_PIN), + _led2(LED2_PIN), + _led3(LED3_PIN) {} void UI::setup() { @@ -116,7 +118,7 @@ namespace Dough _led2.off(); _led3.off(); } - + void UI::notifyConnectingToMQTT() { _led1.blink()->fast(); @@ -163,16 +165,16 @@ namespace Dough { _led3.off(); delay(50); - _led3.on(); + _led3.on(); } void UI::notifyNetworkActivity() { _led1.off(); delay(50); - _led1.on(); + _led1.on(); } - + // Flash all LEDs, one at a time in a synchroneous manner, making // this work when the UI timer interrupt is inactive. This is used // as a "Hey, I'm awake!" signal from the device after booting up. diff --git a/src/UI/UI.h b/src/UI/UI.h index 033a91a..a90dd3e 100644 --- a/src/UI/UI.h +++ b/src/UI/UI.h @@ -14,10 +14,8 @@ namespace Dough class UI { public: - UI(); + UI(ButtonISR onoffButtonISR, ButtonISR setupButtonISR); void setup(); - static void onoffButtonISR(); - static void setupButtonISR(); Button onoffButton; Button setupButton; void processButtonEvents();