diff --git a/river-monitoring-dashboard/river-monitoring-dashboard.py b/river-monitoring-dashboard/river-monitoring-dashboard.py index aa9f48d..3fb2698 100644 --- a/river-monitoring-dashboard/river-monitoring-dashboard.py +++ b/river-monitoring-dashboard/river-monitoring-dashboard.py @@ -159,4 +159,4 @@ def update_status_display(n): return f'Stato del sistema: {data_queue[-1]["status"]}' if __name__ == "__main__": - app.run_server(debug=True, port=8053) + app.run_server(debug=True, port=8055) diff --git a/water-channel-controller/platformio.ini b/water-channel-controller/platformio.ini index 04ea797..b10847a 100644 --- a/water-channel-controller/platformio.ini +++ b/water-channel-controller/platformio.ini @@ -17,3 +17,4 @@ lib_deps = marcoschwartz/LiquidCrystal_I2C@^1.1.4 arduino-libraries/Servo@^1.2.1 nabontra/ServoTimer2@0.0.0-alpha+sha.2bf7fb3c17 + paulstoffregen/TimerOne@^1.1.1 diff --git a/water-channel-controller/src/Config.h b/water-channel-controller/src/Config.h new file mode 100644 index 0000000..90c89fe --- /dev/null +++ b/water-channel-controller/src/Config.h @@ -0,0 +1,14 @@ +#ifndef __CONFIG__ +#define __CONFIG__ +#define __DEBUG__ + +#include "Arduino.h" + +constexpr int BASE_PERIOD = 100; +constexpr int POT_PIN = A0; +constexpr int BUTTON_PIN = 4; +constexpr long TIME_OUT_CONNECTION = 300000L; +constexpr int SERVO_PIN = 9; +constexpr int TIME_TO_MOVE = 20; + +#endif // __CONFIG__ \ No newline at end of file diff --git a/water-channel-controller/src/WaterChannelController.h b/water-channel-controller/src/WaterChannelController.h new file mode 100644 index 0000000..9467c78 --- /dev/null +++ b/water-channel-controller/src/WaterChannelController.h @@ -0,0 +1,37 @@ +#ifndef __WATERCHANNELCONTROLLER__ +#define __WATERCHANNELCONTROLLER__ + +#include "Arduino.h" + +/** + * @brief Enum for the modes of the water channel controller. + * + * This enum represents the possible modes of the water channel controller: Automatic and Manual. + */ +enum class Mode +{ + AUTOMATIC, + MANUAL, + ALARM_TOO_LOW, + NORMAL, + PRE_ALARM_TOO_HIGH, + ALARM_TOO_HIGH, + ALARM_TOO_HIGH_CRITIC +}; + +/** + * @brief A controller for a water channel. + * + * This class represents a controller for a water channel. The controller can be in one of two modes: Automatic or Manual. + * The controller has an active position and flags to indicate whether the communication or position has changed. + */ +class WaterChannelController +{ +public: + bool commChange; ///< Flag to indicate whether the communication has changed. + bool posChange; ///< Flag to indicate whether the position has changed. + Mode activeMode; ///< The active mode of the controller. + unsigned int activePosition; ///< The active position of the controller. +}; + +#endif // __WATERCHANNELCONTROLLER__ \ No newline at end of file diff --git a/water-channel-controller/src/WaterChannelController/State.h b/water-channel-controller/src/WaterChannelController/State.h deleted file mode 100644 index 582669a..0000000 --- a/water-channel-controller/src/WaterChannelController/State.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef __STATES_H__ -#define __STATES_H__ - -/** - * @brief Enumeration representing the different states of the water channel controller. - */ -enum class State -{ - MANUAL, /**< Manual state */ - AUTOMATIC /**< Automatic state */ -}; - -#endif // __STATES_H__ \ No newline at end of file diff --git a/water-channel-controller/src/WaterChannelController/WaterChannelController.h b/water-channel-controller/src/WaterChannelController/WaterChannelController.h deleted file mode 100644 index 70a629f..0000000 --- a/water-channel-controller/src/WaterChannelController/WaterChannelController.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef __WATERCHANNELCONTROLLER_H__ -#define __WATERCHANNELCONTROLLER_H__ - -#include "../serial_communication/Client-Arduino/ValveController.h" -#include "../serial_communication/Client-Arduino/JsonProcessor.h" -#include "LiquidCrystal_I2C.h" -#include "Servo.h" -#include "WaterChannelController/State.h" -#include "../serial_communication/Client-Arduino/MessageReceiver.h" -#include "Arduino.h" - -/** - * @class WaterChannelController - * @brief This class is responsible for controlling a water channel. - * - * The WaterChannelController class provides functionality to control a water channel using a servo motor, - * a button for manual control, and a potentiometer for adjusting the valve opening level. It also includes - * an LCD display to show the current status of the system. - */ -class WaterChannelController -{ -public: - /** - * @brief Construct a new Water Channel Controller object. - * - * @param servoPin The pin number where the servo motor is connected. - * @param buttonPin The pin number where the button for manual control is connected. - * @param potentiometerPin The pin number where the potentiometer for adjusting the valve opening level is connected. - * @param lcdAddr The I2C address of the LCD. - * @param lcdColumns The number of columns in the LCD. - * @param lcdRows The number of rows in the LCD. - */ - WaterChannelController(int servoPin, int buttonPin, int potentiometerPin, int lcdAddr, int lcdColumns, int lcdRows); - - /** - * @brief Destroy the Water Channel Controller object - * - */ - ~WaterChannelController(); - - /** - * @brief Initializes the Water Channel Controller. - * - * This method sets up the necessary hardware interfaces for the water channel controller. - * It sets the mode of the button pin to INPUT, attaches the servo to its pin, and initializes - * the LCD display with the specified dimensions. It also sets the initial position of the servo - * to represent a fully closed valve and updates the LCD display with the initial status. - */ - void initialize(); - - /** - * @brief Initializes the button used to control the water channel manually. - * This method sets the mode of the button pin to INPUT, which allows the button to be used for manual control. - */ - void initializeButton(); - - /** - * @brief Initializes the servo motor used to control the position of the water valve. - * This method attaches the servo to the specified pin, which allows the servo to be controlled by the Arduino. - */ - void initializeServo(); - - /** - * @brief Initializes the valve to a default position (fully closed). - * This method sets the initial position of the servo to represent a fully closed valve. - */ - void initializeValve(); - - /** - * @brief Initializes the LCD display. - * This method initializes the LCD display with the specified dimensions and prints the initial message. - */ - void initializeLcd(); - - void parseValveValue(); - - /** - * @brief Updates the position of the valve based on the specified opening level. - * - * This method adjusts the position of the servo motor, which in turn changes the opening level of the valve. - * The valve opening level is a percentage, where 0% represents a fully closed valve and 100% represents a fully open valve. - * The method maps the valve opening level to a servo angle (0-180 degrees) and then updates the servo position. - * If the provided valve opening level is outside the valid range (0-100), the method does nothing. - * - * @param valveOpeningLevel The desired level to which the valve should be opened, expressed as a percentage (0-100). - */ - void updateValvePosition(int valveOpeningLevel); - - /** - * @brief Checks the status of the button for manual control. - * - * This method reads the state of the button connected to the buttonPin. If the button is pressed, - * it toggles the manualMode flag, which enables or disables manual control of the water channel. - * The method also debounces the button input to prevent false triggering. - */ - void checkButtonStatus(); - - /** - * @brief Toggles the operating mode of the water channel controller between manual and automatic. - */ - void toggleState(); - - /** - * @brief Enables manual control of the water channel. - * - * This method is used to manually control the opening level of the valve. When manual control mode is active, - * it reads the value from the potentiometer connected to the potentiometerPin, maps this value to a valve opening level, - * and then updates the valve position accordingly. The current valve opening level is also displayed on the LCD. - */ - void manualControl(); - - /** - * @brief Updates the LCD display with the current status of the system. - * - * This method refreshes the LCD display to show the current status of the water channel controller. - * It displays the current valve opening level (as a percentage) and the current operating mode (manual or automatic). - * The valve opening level is displayed on the first row of the LCD, and the operating mode is displayed on the second row. - */ - void updateLCD(); - - /** - * @brief Converts the valve opening level to a servo angle. - * - * This method maps the valve opening level (0-100) to a servo angle (0-180). - * A valve opening level of 0 corresponds to a servo angle of 0 (valve fully closed), - * and a valve opening level of 100 corresponds to a servo angle of 180 (valve fully open). - * - * @param valveOpeningLevel The level to which the valve should be opened (0-100). - * @return int The corresponding servo angle (0-180). - */ - int mapValveOpeningLevelToServoAngle(int valveOpeningLevel); - - /** - * @brief Reads the status of the water channel controller. - * Checks the button status and performs manual control if the controller is in manual mode. - */ - void reading(); - -private: - int servoPin; ///< The pin number to which the servo motor is connected. - int address = 0; ///< The I2C address of the LCD display. - int columns = 16; ///< The number of columns in the LCD display. - int rows = 2; ///< The number of rows in the LCD display. - Servo valveServo; ///< The servo motor that controls the position of the water valve. - int buttonPin; ///< The pin number to which the manual control button is connected. - int potentiometerPin; ///< The pin number to which the potentiometer for adjusting the valve opening level is connected. - bool manualMode; ///< A flag indicating whether the system is in manual control mode. - LiquidCrystal_I2C lcd; ///< The object representing the LCD display. - int currentValveOpeningLevel; ///< The current level (0-100) to which the water valve is opened. - const int MIN_VALVE_OPENING_LEVEL = 0; ///< The minimum valid valve opening level. - const int MAX_VALVE_OPENING_LEVEL = 100; ///< The maximum valid valve opening level. - const int MIN_SERVO_ANGLE = 0; ///< The minimum angle to which the servo can rotate. - const int MAX_SERVO_ANGLE = 180; ///< The maximum angle to which the servo can rotate. - const int MIN_POTENTIOMETER_VALUE = 0; ///< The minimum reading from the potentiometer. - const int MAX_POTENTIOMETER_VALUE = 1023; ///< The maximum reading from the potentiometer. - ValveController valveController; ///< The valve controller object used to set the valve value based on the system state. - JsonProcessor jsonProcessor; ///< The JSON processor object used to parse the JSON data received from the server. - State state; ///< The state object used to store the current state of the system. - const unsigned long DEBOUNCE_DELAY = 200; ///< The delay (in milliseconds) used to debounce the button input. - MessageReceiver messageReceiver; ///< The message receiver object used to receive messages from the server. -}; - -#endif // __WATERCHANNELCONTROLLER_H__ \ No newline at end of file diff --git a/water-channel-controller/src/WaterChannelController/WaterChannelControllerImpl.cpp b/water-channel-controller/src/WaterChannelController/WaterChannelControllerImpl.cpp deleted file mode 100644 index 8b075f3..0000000 --- a/water-channel-controller/src/WaterChannelController/WaterChannelControllerImpl.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "WaterChannelController.h" - -WaterChannelController::WaterChannelController(int servoPin, int buttonPin, int potentiometerPin, int lcdAddr, int lcdColumns, int lcdRows) - : servoPin(servoPin), buttonPin(buttonPin), potentiometerPin(potentiometerPin), manualMode(false), lcd(lcdAddr, lcdColumns, lcdRows), currentValveOpeningLevel(0) -{ - initialize(); -} - -WaterChannelController::~WaterChannelController() -{ - valveServo.detach(); -} - -void WaterChannelController::initialize() -{ - initializeServo(); - initializeButton(); - initializeValve(); - initializeLcd(); - updateLCD(); -} - -void WaterChannelController::initializeServo() -{ - valveServo.attach(servoPin); -} - -void WaterChannelController::initializeButton() -{ - pinMode(buttonPin, INPUT); -} - -void WaterChannelController::initializeValve() -{ - valveServo.write(MIN_SERVO_ANGLE); -} - -void WaterChannelController::initializeLcd() -{ - lcd.begin(columns, rows); - lcd.init(); - lcd.backlight(); - lcd.setCursor(0, 0); - lcd.print("Water Channel"); -} - -// TODO: Implement the parseValveValue method -void WaterChannelController::updateValvePosition(int valveOpeningLevel) -{ - int servoAngle = mapValveOpeningLevelToServoAngle(valveOpeningLevel); - valveServo.write(servoAngle); - currentValveOpeningLevel = valveOpeningLevel; - updateLCD(); -} - -int WaterChannelController::mapValveOpeningLevelToServoAngle(int valveOpeningLevel) -{ - return map(valveOpeningLevel, MIN_VALVE_OPENING_LEVEL, MAX_VALVE_OPENING_LEVEL, MIN_SERVO_ANGLE, MAX_SERVO_ANGLE); -} - -void WaterChannelController::updateLCD() -{ - lcd.clear(); - lcd.setCursor(0, 0); - lcd.print("Valve: "); - lcd.print(currentValveOpeningLevel); - lcd.print("%"); - lcd.setCursor(0, 1); - lcd.print("Mode: "); - lcd.print(state == State::MANUAL ? "MANUAL" : "AUTOMATIC"); -} - -void WaterChannelController::manualControl() -{ - int potentiometerValue = analogRead(potentiometerPin); - int valveOpeningLevel = map(potentiometerValue, MIN_POTENTIOMETER_VALUE, MAX_POTENTIOMETER_VALUE, MIN_VALVE_OPENING_LEVEL, MAX_VALVE_OPENING_LEVEL); - updateValvePosition(valveOpeningLevel); -} - -void WaterChannelController::reading() -{ - checkButtonStatus(); - - switch (state) - { - case State::MANUAL: - manualControl(); - break; - - case State::AUTOMATIC: - String receivedContent = messageReceiver.getReceivedContent(); - String systemState = jsonProcessor.getSystemState(receivedContent); - int valveValue = valveController.getValveValueForStateAsInt(systemState); - updateValvePosition(valveValue); - break; - } -} - -void WaterChannelController::checkButtonStatus() -{ - static unsigned long timeOfLastButtonPress = 0; - - int currentButtonState = digitalRead(buttonPin); - if (currentButtonState == HIGH && millis() - timeOfLastButtonPress > DEBOUNCE_DELAY) - { - toggleState(); - timeOfLastButtonPress = millis(); - } -} - -void WaterChannelController::toggleState() -{ - state = (state == State::MANUAL) ? State::AUTOMATIC : State::MANUAL; -} \ No newline at end of file diff --git a/water-channel-controller/src/components/api/AbstractButton.h b/water-channel-controller/src/components/api/AbstractButton.h deleted file mode 100644 index 438a272..0000000 --- a/water-channel-controller/src/components/api/AbstractButton.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef __ABSTRACT_BUTTON_H__ -#define __ABSTRACT_BUTTON_H__ - -#include "Arduino.h" - -/** - * @class AbstractButton - * @brief This class provides an abstract interface for a button. - * - * The AbstractButton class provides a common interface for different types of buttons. - * It includes methods to check if the button is pressed and to synchronize the button state. - */ -class AbstractButton -{ - -public: - /** - * @brief Construct a new AbstractButton object. - * - * This is a default constructor for creating a new AbstractButton object. - */ - AbstractButton(); - - /** - * @brief Check if the button is pressed. - * - * This is a pure virtual method that must be implemented by any class that inherits from AbstractButton. - * It should return true if the button is currently pressed, and false otherwise. - * - * @return true if the button is pressed, false otherwise. - */ - virtual bool isPressed() = 0; - - /** - * @brief Synchronize the button state. - * - * This method is used to update the button state. It should be called regularly to ensure that the button state is up-to-date. - */ - virtual void sync(); - - /** - * @brief Get the last synchronization time. - * - * This method returns the time (in milliseconds since the program started) when the button state was last synchronized. - * - * @return The last synchronization time in milliseconds. - */ - long getLastSyncTime(); - -protected: - /** - * @brief Update the synchronization time. - * - * This method updates the last synchronization time to the specified value. - * - * @param time The new synchronization time in milliseconds. - */ - void updateSyncTime(long time); - -private: - long lastTimeSync; ///< The time (in milliseconds since the program started) when the button state was last synchronized. -}; - -#endif // __ABSTRACT_BUTTON_H__ \ No newline at end of file diff --git a/water-channel-controller/src/components/api/Button.h b/water-channel-controller/src/components/api/Button.h index db6f80b..a8b43fd 100644 --- a/water-channel-controller/src/components/api/Button.h +++ b/water-channel-controller/src/components/api/Button.h @@ -1,58 +1,24 @@ -#ifndef __BUTTON_H__ -#define __BUTTON_H__ - -#include "../api/AbstractButton.h" -#include "Arduino.h" +#ifndef __BUTTON__ +#define __BUTTON__ /** - * @class Button - * @brief This class represents a physical button connected to a digital I/O pin. + * @brief This class represents a Button. * - * The Button class extends the AbstractButton class and provides an implementation - * for a physical button connected to a digital I/O pin. It includes methods to check - * if the button is pressed and to synchronize the button state. + * The Button class is an abstract class that provides the basic functionalities of a button. + * A button is a simple input device commonly used in electronic devices. + * The class is designed to be inherited by specific implementations of buttons. */ -class Button : public AbstractButton +class Button { - public: /** - * @brief Construct a new Button object. - * - * This constructor creates a new Button object associated with a specific digital I/O pin. - * - * @param pin The digital I/O pin number to which the button is connected. - */ - Button(int pin); - - /** - * @brief Check if the button is pressed. - * - * This method returns true if the button is currently pressed, and false otherwise. + * @brief Check if the button is currently pressed. * - * @return true if the button is pressed, false otherwise. - */ - bool isPressed(); - - /** - * @brief Synchronize the button state. - * - * This method is used to update the button state. It should be called regularly to ensure that the button state is up-to-date. - */ - void sync(); - - /** - * @brief Synchronize the button state. + * This is a pure virtual function, to be implemented by derived classes. * - * This method is used to update the button state. It should be called regularly to ensure - * that the button state is up-to-date. It reads the current state of the digital I/O pin - * to which the button is connected and updates the internal state accordingly. + * @return True if the button is currently pressed, false otherwise. */ -private: - int pin; ///< The digital I/O pin number to which the button is connected. - const unsigned long DEBOUNCE_DELAY = 50; ///< The debounce delay in milliseconds. - unsigned long lastDebounceTime = 0; ///< The last time the button state was updated. - bool pressed; ///< The current state of the button (true = pressed, false = not pressed). + virtual bool isPressed() = 0; }; -#endif // __BUTTON_H__ \ No newline at end of file +#endif // __BUTTON__ \ No newline at end of file diff --git a/water-channel-controller/src/components/api/ButtonImpl.h b/water-channel-controller/src/components/api/ButtonImpl.h new file mode 100644 index 0000000..641dfd8 --- /dev/null +++ b/water-channel-controller/src/components/api/ButtonImpl.h @@ -0,0 +1,40 @@ +#ifndef __BUTTONIMPL__ +#define __BUTTONIMPL__ + +#include "Button.h" +#include "Arduino.h" + +/** + * @class ButtonImpl + * @brief This class is a concrete implementation of the Button abstract class. + * + * The ButtonImpl class provides the specific implementation for a button in an Arduino context. + * It inherits from the Button abstract class and implements the isPressed method. + */ +class ButtonImpl : public Button +{ +public: + /** + * @brief Construct a new ButtonImpl object. + * + * @param pin The Arduino pin number to which the button is connected. + * @param pullup A boolean indicating if the button has a pullup resistor or not. + * True if the button has a pullup resistor, false otherwise. + */ + ButtonImpl(int pin, bool pullup); + + /** + * @brief Check if the button is currently pressed. + * + * @return true If the button is currently pressed. + * @return false If the button is not currently pressed. + */ + bool isPressed() override; + +private: + int pin; ///< The Arduino pin number to which the button is connected. + bool pullup; ///< A boolean indicating if the button has a pullup resistor or not. + bool pressed{false}; ///< A boolean indicating if the button is currently pressed or not. Initialized to false. +}; + +#endif // __BUTTONIMPL__ \ No newline at end of file diff --git a/water-channel-controller/src/components/api/Display.h b/water-channel-controller/src/components/api/Display.h new file mode 100644 index 0000000..0d70461 --- /dev/null +++ b/water-channel-controller/src/components/api/Display.h @@ -0,0 +1,40 @@ +#ifndef __DISPLAY_CLASS__ +#define __DISPLAY_CLASS__ + +#include "Arduino.h" + +/** + * @class Display + * @brief This is an abstract base class for different types of displays. + * + * This class provides a common interface for different types of displays that can be used in Arduino projects. + * Any specific display class should inherit from this class and implement the pure virtual functions. + */ +class Display +{ +public: + /** + * @brief Clears the display. + * + * This is a pure virtual function that needs to be implemented by any class that inherits from Display. + */ + virtual void clear() = 0; + + /** + * @brief Shows a message on the display. + * + * @param msg The message to be displayed. + * + * This is a pure virtual function that needs to be implemented by any class that inherits from Display. + */ + virtual void show(String msg) = 0; + + /** + * @brief Moves the cursor to the next line on the display. + * + * This is a pure virtual function that needs to be implemented by any class that inherits from Display. + */ + virtual void ln() = 0; +}; + +#endif // __DISPLAY_CLASS__ \ No newline at end of file diff --git a/water-channel-controller/src/components/api/LCD.h b/water-channel-controller/src/components/api/LCD.h index 6e4bd9b..493e863 100644 --- a/water-channel-controller/src/components/api/LCD.h +++ b/water-channel-controller/src/components/api/LCD.h @@ -1,45 +1,22 @@ -#ifndef __LCD_H__ -#define __LCD_H__ +#ifndef __DISPLAY__ +#define __DISPLAY__ -#include "LiquidCrystal_I2C.h" -#include "Arduino.h" -#include "Wire.h" +#include "Display.h" +#include +#include -/** - * @brief This class provides an interface for controlling an LCD display - * using the I2C protocol. - */ -class LCD +class Lcd : public Display { -public: - /** - * @brief Construct a new LCD object. - * - * @param address The I2C address of the LCD display. - * @param columns The number of columns in the LCD display. - * @param rows The number of rows in the LCD display. - */ - LCD(int address, int columns, int rows); - - /** - * @brief Write a string to the LCD display at a specified location. - * - * @param string The string to write to the display. - * @param start_col The column at which to start writing the string. - * @param start_rows The row at which to start writing the string. - */ - void write(const char *string, int start_col, int start_rows); +private: + LiquidCrystal_I2C *lcd; + uint8_t active_row{0}; + uint8_t total_cols; - /** - * @brief Clear the LCD display. - */ - void clear(); - -protected: - /** - * @brief An instance of the LiquidCrystal_I2C class used to control the LCD display. - */ - LiquidCrystal_I2C lcd; +public: + Lcd(uint8_t lcd_Addr, uint8_t lcd_cols, uint8_t lcd_rows); + void clear() override; + void ln() override; + void show(String msg) override; }; -#endif // __LCD_H__ \ No newline at end of file +#endif \ No newline at end of file diff --git a/water-channel-controller/src/components/api/MyServo.h b/water-channel-controller/src/components/api/MyServo.h index 5676553..dada08a 100644 --- a/water-channel-controller/src/components/api/MyServo.h +++ b/water-channel-controller/src/components/api/MyServo.h @@ -31,7 +31,7 @@ class MyServo * * @param angle The angle to set the servo motor to, between 0 and 180 degrees. */ - void write(int angle); + void setPosition(int angle); /** * @brief Detach the servo motor, stopping it from moving. diff --git a/water-channel-controller/src/components/api/Potentiometer.h b/water-channel-controller/src/components/api/Potentiometer.h new file mode 100644 index 0000000..8514139 --- /dev/null +++ b/water-channel-controller/src/components/api/Potentiometer.h @@ -0,0 +1,33 @@ +#ifndef __POTENTIOMETER__ +#define __POTENTIOMETER__ + +/** + * @brief This class represents a Potentiometer. + * + * The Potentiometer class is an abstract class that provides the basic functionalities of a potentiometer. + * A potentiometer is a three-terminal resistor with a sliding or rotating contact that forms an adjustable voltage divider. + * The class is designed to be inherited by specific implementations of potentiometers. + */ +class Potentiometer +{ +public: + /** + * @brief Get the current position of the potentiometer. + * + * This is a pure virtual function, to be implemented by derived classes. + * + * @return The current position of the potentiometer as an unsigned integer. + */ + virtual unsigned int position() = 0; + + /** + * @brief Check if the potentiometer has been moved. + * + * This is a pure virtual function, to be implemented by derived classes. + * + * @return True if the potentiometer has been moved, false otherwise. + */ + virtual bool moved() = 0; +}; + +#endif // __POTENTIOMETER__ \ No newline at end of file diff --git a/water-channel-controller/src/components/api/PotentiometerImpl.h b/water-channel-controller/src/components/api/PotentiometerImpl.h new file mode 100644 index 0000000..a9848d2 --- /dev/null +++ b/water-channel-controller/src/components/api/PotentiometerImpl.h @@ -0,0 +1,47 @@ +#ifndef __POTENTIOMETERIMPL__ +#define __POTENTIOMETERIMPL__ + +#include "Potentiometer.h" + +/** + * @brief This class is a concrete implementation of the Potentiometer abstract class. + * + * The PotentiometerImpl class provides the specific implementation for a potentiometer. + * It inherits from the Potentiometer abstract class and implements the position and moved methods. + */ +class PotentiometerImpl : public Potentiometer +{ +public: + /** + * @brief Construct a new PotentiometerImpl object. + * + * @param pin The pin number to which the potentiometer is connected. + * @param tolerance The tolerance value for detecting movement in the potentiometer. + */ + PotentiometerImpl(int pin, int tolerance); + + /** + * @brief Get the current position of the potentiometer. + * + * @return The current position of the potentiometer as an unsigned integer. + */ + unsigned int position() override; + + /** + * @brief Check if the potentiometer has been moved. + * + * @return True if the potentiometer has been moved, false otherwise. + */ + bool moved() override; + +private: + int pin; ///< The pin number to which the potentiometer is connected. + int tolerance; ///< The tolerance value for detecting movement in the potentiometer. + unsigned int oldValue; ///< The old position value of the potentiometer. + const int MIN_ANALOG_READ = 0; ///< The minimum value that can be read from the analog pin. + const int MAX_ANALOG_READ = 1000; ///< The maximum value that can be read from the analog pin. + const int MIN_POSITION = 0; ///< The minimum position value of the potentiometer. + const int MAX_POSITION = 100; ///< The maximum position value of the potentiometer. +}; + +#endif // __POTENTIOMETERIMPL__ \ No newline at end of file diff --git a/water-channel-controller/src/components/api/ServoMotor.h b/water-channel-controller/src/components/api/ServoMotor.h new file mode 100644 index 0000000..611ee93 --- /dev/null +++ b/water-channel-controller/src/components/api/ServoMotor.h @@ -0,0 +1,38 @@ +#ifndef __SERVO_MOTOR__ +#define __SERVO_MOTOR__ + +/** + * @brief This class represents a Servo Motor. + * + * The ServoMotor class is an abstract class that provides the basic functionalities of a servo motor. + * A servo motor is a rotary actuator or linear actuator that allows for precise control of angular or linear position. + * The class is designed to be inherited by specific implementations of servo motors. + */ +class ServoMotor +{ +public: + /** + * @brief Turns the servo motor on. + * + * This is a pure virtual function, to be implemented by derived classes. + */ + virtual void on() = 0; + + /** + * @brief Turns the servo motor off. + * + * This is a pure virtual function, to be implemented by derived classes. + */ + virtual void off() = 0; + + /** + * @brief Set the position of the servo motor in degrees. + * + * This is a pure virtual function, to be implemented by derived classes. + * + * @param angle The desired position of the servo motor in degrees. The valid range is typically from 0 to 180 degrees, but can vary depending on the specific servo motor used. + */ + virtual void setPosition(int angle) = 0; +}; + +#endif // __SERVO_MOTOR__ \ No newline at end of file diff --git a/water-channel-controller/src/components/api/ServoMotorImpl.h b/water-channel-controller/src/components/api/ServoMotorImpl.h new file mode 100644 index 0000000..3f9b4ad --- /dev/null +++ b/water-channel-controller/src/components/api/ServoMotorImpl.h @@ -0,0 +1,57 @@ +#ifndef __SERVO_MOTOR_IMPL__ +#define __SERVO_MOTOR_IMPL__ + +#include "ServoMotor.h" +#include "Arduino.h" +#include "ServoTimer2.h" + +/** + * @brief This class is a concrete implementation of the ServoMotor abstract class. + * + * The ServoMotorImpl class provides the specific implementation for a servo motor in an Arduino context. + * It inherits from the ServoMotor abstract class and implements the on, off, and setPosition methods. + */ +class ServoMotorImpl : public ServoMotor +{ +public: + /** + * @brief Construct a new ServoMotorImpl object. + * + * @param pin The Arduino pin number to which the servo motor is connected. + */ + ServoMotorImpl(int pin); + + /** + * @brief Turns the servo motor on. + * + * This method sends a signal to the servo motor to start moving. + */ + void on() override; + + /** + * @brief Turns the servo motor off. + * + * This method sends a signal to the servo motor to stop moving. + */ + void off() override; + + /** + * @brief Set the position of the servo motor in degrees. + * + * This method sends a signal to the servo motor to rotate to the specified position. + * + * @param angle The desired position of the servo motor in degrees. The valid range is from 0 to 180 degrees. + */ + void setPosition(int angle) override; + +private: + int pin; ///< The Arduino pin number to which the servo motor is connected. + ServoTimer2 motor; ///< An instance of the ServoTimer2 class used to control the servo motor. + const int MAX_ANGLE = 180; ///< The maximum angle that the servo motor can reach. + const int MIN_ANGLE = 0; ///< The minimum angle that the servo motor can reach. + const double SERVO_MAX_PULSE_WIDTH = 2400.0; ///< The maximum pulse width that the servo motor can reach. + const double SERVO_MIN_PULSE_WIDTH = 544.0; ///< The minimum pulse width that the servo motor can reach. + const double SERVO_MAX_ANGLE = 180.0; ///< The maximum angle that the servo motor can reach. +}; + +#endif // __SERVO_MOTOR_IMPL__ \ No newline at end of file diff --git a/water-channel-controller/src/components/api/ServoTimer2.cpp b/water-channel-controller/src/components/api/ServoTimer2.cpp new file mode 100644 index 0000000..4ac95c1 --- /dev/null +++ b/water-channel-controller/src/components/api/ServoTimer2.cpp @@ -0,0 +1,136 @@ +extern "C" +{ +// AVR LibC Includes +#include +#include + // #include "WConstants.h" +} +#include +#include "ServoTimer2.h" +static void initISR(); +static void writeChan(uint8_t chan, int pulsewidth); + +#define FRAME_SYNC_INDEX 0 // frame sync delay is the first entry in the channel array +#define FRAME_SYNC_PERIOD 20000 // total frame duration in microseconds +#define FRAME_SYNC_DELAY ((FRAME_SYNC_PERIOD - (NBR_CHANNELS * DEFAULT_PULSE_WIDTH)) / 128) // number of iterations of the ISR to get the desired frame rate +#define DELAY_ADJUST 8 // number of microseconds of calculation overhead to be subtracted from pulse timings + +static servo_t servos[NBR_CHANNELS + 1]; // static array holding servo data for all channels + +static volatile uint8_t Channel; // counter holding the channel being pulsed +static volatile uint8_t ISRCount; // iteration counter used in the interrupt routines; +uint8_t ChannelCount = 0; // counter holding the number of attached channels +static boolean isStarted = false; // flag to indicate if the ISR has been initialised + +ISR(TIMER2_OVF_vect) +{ + ++ISRCount; // increment the overlflow counter + if (ISRCount == servos[Channel].counter) // are we on the final iteration for this channel + { + TCNT2 = servos[Channel].remainder; // yes, set count for overflow after remainder ticks + } + else if (ISRCount > servos[Channel].counter) + { + // we have finished timing the channel so pulse it low and move on + if (servos[Channel].Pin.isActive == true) // check if activated + digitalWrite(servos[Channel].Pin.nbr, LOW); // pulse this channel low if active + + Channel++; // increment to the next channel + ISRCount = 0; // reset the isr iteration counter + TCNT2 = 0; // reset the clock counter register + if ((Channel != FRAME_SYNC_INDEX) && (Channel <= NBR_CHANNELS)) + { // check if we need to pulse this channel + if (servos[Channel].Pin.isActive == true) // check if activated + digitalWrite(servos[Channel].Pin.nbr, HIGH); // its an active channel so pulse it high + } + else if (Channel > NBR_CHANNELS) + { + Channel = 0; // all done so start over + } + } +} + +ServoTimer2::ServoTimer2() +{ + if (ChannelCount < NBR_CHANNELS) + this->chanIndex = ++ChannelCount; // assign a channel number to this instance + else + this->chanIndex = 0; // todo // too many channels, assigning 0 inhibits this instance from functioning +} + +uint8_t ServoTimer2::attach(int pin) +{ + if (isStarted == false) + initISR(); + if (this->chanIndex > 0) + { + // debug("attaching chan = ", chanIndex); + pinMode(pin, OUTPUT); // set servo pin to output + servos[this->chanIndex].Pin.nbr = pin; + servos[this->chanIndex].Pin.isActive = true; + } + return this->chanIndex; +} + +void ServoTimer2::detach() +{ + servos[this->chanIndex].Pin.isActive = false; +} + +void ServoTimer2::write(int pulsewidth) +{ + writeChan(this->chanIndex, pulsewidth); // call the static function to store the data for this servo +} + +int ServoTimer2::read() +{ + unsigned int pulsewidth; + if (this->chanIndex > 0) + pulsewidth = servos[this->chanIndex].counter * 128 + ((255 - servos[this->chanIndex].remainder) / 2) + DELAY_ADJUST; + else + pulsewidth = 0; + return pulsewidth; +} + +boolean ServoTimer2::attached() +{ + return servos[this->chanIndex].Pin.isActive; +} + +static void writeChan(uint8_t chan, int pulsewidth) +{ + // calculate and store the values for the given channel + if ((chan > 0) && (chan <= NBR_CHANNELS)) // ensure channel is valid + { + if (pulsewidth < MIN_PULSE_WIDTH) // ensure pulse width is valid + pulsewidth = MIN_PULSE_WIDTH; + else if (pulsewidth > MAX_PULSE_WIDTH) + pulsewidth = MAX_PULSE_WIDTH; + + pulsewidth -= DELAY_ADJUST; // subtract the time it takes to process the start and end pulses (mostly from digitalWrite) + servos[chan].counter = pulsewidth / 128; + servos[chan].remainder = 255 - (2 * (pulsewidth - (servos[chan].counter * 128))); // the number of 0.5us ticks for timer overflow + } +} + +static void initISR() +{ + for (uint8_t i = 1; i <= NBR_CHANNELS; i++) + { // channels start from 1 + writeChan(i, DEFAULT_PULSE_WIDTH); // store default values + } + servos[FRAME_SYNC_INDEX].counter = FRAME_SYNC_DELAY; // store the frame sync period + + Channel = 0; // clear the channel index + ISRCount = 0; // clear the value of the ISR counter; + + /* setup for timer 2 */ + TIMSK2 = 0; // disable interrupts + TCCR2A = 0; // normal counting mode + TCCR2B = _BV(CS21); // set prescaler of 8 + TCNT2 = 0; // clear the timer2 count + TIFR2 = _BV(TOV2); // clear pending interrupts; + TIMSK2 = _BV(TOIE2); // enable the overflow interrupt + + isStarted = true; // flag to indicate this initialisation code has been executed +} diff --git a/water-channel-controller/src/components/api/ServoTimer2.h b/water-channel-controller/src/components/api/ServoTimer2.h new file mode 100644 index 0000000..aeb2d2f --- /dev/null +++ b/water-channel-controller/src/components/api/ServoTimer2.h @@ -0,0 +1,144 @@ +/* + ServoTimer2.h - Interrupt driven Servo library for Arduino using Timer2- Version 0.1 + Copyright (c) 2008 Michael Margolis. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +// Updated for Arduino 1.x by Nick Bontrager 2013 + +/* + This library uses Timer2 to drive up to 8 servos using interrupts so no refresh activity is required from within the sketch. + The usage and method naming is similar to the Arduino software servo library http://www.arduino.cc/playground/ComponentLib/Servo + except that pulse widths can be in microseconds or degrees. + write() treats parameters of 180 or less as degrees, otherwise values are milliseconds. + + + A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method. + The servo is pulsed in the background to the value most recently written using the write() method + + Note that analogWrite of PWM on pins 3 and 11 is disabled when the first servo is attached + + The methods are: + + ServoTimer2 - Class for manipulating servo motors connected to Arduino pins. + + + + attach(pin ) - Attaches a servo motor to an i/o pin. + + attach(pin, min, max ) - Attaches to a pin setting min and max values in microseconds + + default min is 544, max is 2400 + + + + write() - Sets the servo pulse width in microseconds. + + + + read() - Gets the last written servo pulse width in microseconds. + + + + attached() - Returns true if there is a servo attached. + + + + detach() - Stops an attached servos from pulsing its i/o pin. + + + +The library takes about 824 bytes of program memory and 32+(1*servos) bytes of SRAM. + +The pulse width timing is accurate to within 1% + + + */ + +// ensure this library description is only included once +#ifndef ServoTimer2_h +#define ServoTimer2_h + +#include +// typedef uint8_t boolean; +// typedef uint8_t byte; + +#define MIN_PULSE_WIDTH 750 // the shortest pulse sent to a servo + +#define MAX_PULSE_WIDTH 2250 // the longest pulse sent to a servo + +#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached + +#define FRAME_SYNC_PERIOD 20000 // total frame duration in microseconds + +#define NBR_CHANNELS 8 // the maximum number of channels, don't change this + +typedef struct +{ + + uint8_t nbr : 5; // a pin number from 0 to 31 + + uint8_t isActive : 1; // false if this channel not enabled, pin only pulsed if true + +} ServoPin_t; + +typedef struct +{ + + ServoPin_t Pin; + + byte counter; + + byte remainder; + +} servo_t; + +class ServoTimer2 +{ +public: + // constructor: + ServoTimer2(); + + uint8_t attach(int); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure + // the attached servo is pulsed with the current pulse width value, (see the write method) + uint8_t attach(int, int, int); // as above but also sets min and max values for writes. + void detach(); + void write(int); // store the pulse width in microseconds (between MIN_PULSE_WIDTH and MAX_PULSE_WIDTH)for this channel + int read(); // returns current pulse width in microseconds for this servo + boolean attached(); // return true if this servo is attached +private: + uint8_t chanIndex; // index into the channel data for this servo +}; + +// the following ServoArrayT2 class is not implemented in the first version of this library +class ServoArrayT2 +{ +public: + // constructor: + ServoArrayT2(); + + uint8_t attach(int); // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure + // channels are assigned consecutively starting from 1 + // the attached servo is pulsed with the current pulse width value, (see the write method) + void detach(int); // detach the servo on the given channel + void write(int, int); // store the pulse width in microseconds (between MIN_PULSE_WIDTH and MAX_PULSE_WIDTH)for the given channel + int read(int); // returns current pulse width in microseconds for the given channel + boolean attached(int); // return true if the servo on the given channel is attached +private: + uint8_t chanIndex; // index into the channel data for this servo +}; + +#endif \ No newline at end of file diff --git a/water-channel-controller/src/components/impl/AbstractButtonImpl.cpp b/water-channel-controller/src/components/impl/AbstractButtonImpl.cpp deleted file mode 100644 index d6bbb0e..0000000 --- a/water-channel-controller/src/components/impl/AbstractButtonImpl.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "../api/AbstractButton.h" - -AbstractButton::AbstractButton() : lastTimeSync(0) {} - -void AbstractButton::updateSyncTime(long time) -{ - lastTimeSync = time; -} - -long AbstractButton::getLastSyncTime() -{ - return lastTimeSync; -} \ No newline at end of file diff --git a/water-channel-controller/src/components/impl/Button.cpp b/water-channel-controller/src/components/impl/Button.cpp new file mode 100644 index 0000000..704ca93 --- /dev/null +++ b/water-channel-controller/src/components/impl/Button.cpp @@ -0,0 +1,19 @@ +#include "../api/ButtonImpl.h" + +ButtonImpl::ButtonImpl(int pin, bool pullup) : pin(pin), pullup(pullup) +{ + if (pullup) + { + pinMode(pin, INPUT_PULLUP); + } + else + { + pinMode(pin, INPUT); + } +} + +bool ButtonImpl::isPressed() +{ + // if pull up enabled, output reversed + return digitalRead(pin) ^ this->pullup; +} \ No newline at end of file diff --git a/water-channel-controller/src/components/impl/ButtonImpl.cpp b/water-channel-controller/src/components/impl/ButtonImpl.cpp deleted file mode 100644 index c2a4b76..0000000 --- a/water-channel-controller/src/components/impl/ButtonImpl.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "../api/Button.h" - -Button::Button(int pin) : pin(pin), lastDebounceTime(0), pressed(false) -{ - pinMode(pin, INPUT); - sync(); -} - -bool Button::isPressed() -{ - return pressed; -} - -void Button::sync() -{ - // Read the state of the button - - bool reading = digitalRead(pin) == HIGH; - - // Check if the button's state has changed - if (reading != pressed) - { - // Reset the debounce timer - lastDebounceTime = millis(); - } - - // If the button's state has been stable for longer than the debounce delay, update the button state - if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) - { - pressed = reading; - } - - updateSyncTime(millis()); -} \ No newline at end of file diff --git a/water-channel-controller/src/components/impl/LCD.cpp b/water-channel-controller/src/components/impl/LCD.cpp new file mode 100644 index 0000000..46da715 --- /dev/null +++ b/water-channel-controller/src/components/impl/LCD.cpp @@ -0,0 +1,36 @@ +#include "../api/LCD.h" + +#include + +Lcd::Lcd(uint8_t lcd_Addr, uint8_t lcd_cols, uint8_t lcd_rows) + : lcd(new LiquidCrystal_I2C(lcd_Addr, lcd_cols, lcd_rows)), + total_cols(lcd_cols) +{ + lcd->init(); + lcd->backlight(); +} +void Lcd::clear() +{ + active_row = 0; + lcd->clear(); + lcd->setCursor(0, active_row); +} +void Lcd::show(String msg) +{ + String padding = ""; + for (size_t i = msg.length(); i < total_cols * 2; i++) + { + padding = padding + " "; + } + String paddedMessage = msg + padding; + lcd->print(paddedMessage.substring(0, total_cols)); + this->ln(); + lcd->print(paddedMessage.substring(total_cols)); + lcd->setCursor(0, 0); +} + +void Lcd::ln() +{ + active_row++; + lcd->setCursor(0, active_row); +} \ No newline at end of file diff --git a/water-channel-controller/src/components/impl/LCDImpl.cpp b/water-channel-controller/src/components/impl/LCDImpl.cpp deleted file mode 100644 index d0f16af..0000000 --- a/water-channel-controller/src/components/impl/LCDImpl.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "../api/LCD.h" - -LCD::LCD(int address, int columns, int rows) : lcd(address, columns, rows) -{ - lcd.init(); - lcd.backlight(); - lcd.clear(); -} - -void LCD::clear() -{ - lcd.clear(); -} - -void LCD::write(const char *string, int start_col, int start_rows) -{ - lcd.setCursor(start_col, start_rows); - lcd.print(string); -} \ No newline at end of file diff --git a/water-channel-controller/src/components/impl/MyServoImpl.cpp b/water-channel-controller/src/components/impl/MyServo.cpp similarity index 94% rename from water-channel-controller/src/components/impl/MyServoImpl.cpp rename to water-channel-controller/src/components/impl/MyServo.cpp index ff9796d..99f4746 100644 --- a/water-channel-controller/src/components/impl/MyServoImpl.cpp +++ b/water-channel-controller/src/components/impl/MyServo.cpp @@ -5,7 +5,7 @@ MyServo::MyServo(int pin) : pin(pin) servo.attach(pin); } -void MyServo::write(int angle) +void MyServo::setPosition(int angle) { servo.write(angle); } diff --git a/water-channel-controller/src/components/impl/Potentiometer.cpp b/water-channel-controller/src/components/impl/Potentiometer.cpp new file mode 100644 index 0000000..a71f520 --- /dev/null +++ b/water-channel-controller/src/components/impl/Potentiometer.cpp @@ -0,0 +1,26 @@ +#include "../api/PotentiometerImpl.h" + +#include "Arduino.h" + +PotentiometerImpl::PotentiometerImpl(int pin, int tolerance) : pin(pin), tolerance(tolerance), oldValue(analogRead(pin)) +{ + // pinMode(pin, INPUT); +} + +unsigned int PotentiometerImpl::position() +{ + int val = map(analogRead(pin), MIN_ANALOG_READ, MAX_ANALOG_READ, MIN_POSITION, MAX_POSITION); + if (val < MIN_POSITION) + val = MIN_POSITION; + if (val > MAX_POSITION) + val = MAX_POSITION; + return val; +} + +bool PotentiometerImpl::moved() +{ + unsigned int val = analogRead(pin); + bool hasMoved = !(oldValue - tolerance < val && oldValue + tolerance > val); + oldValue = val; + return hasMoved; +} \ No newline at end of file diff --git a/water-channel-controller/src/components/impl/ServoMotor.cpp b/water-channel-controller/src/components/impl/ServoMotor.cpp new file mode 100644 index 0000000..eea27c8 --- /dev/null +++ b/water-channel-controller/src/components/impl/ServoMotor.cpp @@ -0,0 +1,28 @@ +#include "../api/ServoMotorImpl.h" + +ServoMotorImpl::ServoMotorImpl(int pin) : pin(pin) {} + +void ServoMotorImpl::on() +{ + motor.attach(pin); +} + +void ServoMotorImpl::off() +{ + motor.detach(); +} + +void ServoMotorImpl::setPosition(int angle) +{ + if (angle > MAX_ANGLE) + { + angle = MAX_ANGLE; + } + else if (angle < MIN_ANGLE) + { + angle = MIN_ANGLE; + } + + double coeff = (SERVO_MAX_PULSE_WIDTH - SERVO_MIN_PULSE_WIDTH) / SERVO_MAX_ANGLE; + motor.write((int)(SERVO_MIN_PULSE_WIDTH + angle * coeff)); +} \ No newline at end of file diff --git a/water-channel-controller/src/main.cpp b/water-channel-controller/src/main.cpp index dc2a997..8667fc9 100644 --- a/water-channel-controller/src/main.cpp +++ b/water-channel-controller/src/main.cpp @@ -1,19 +1,28 @@ -#include "serial_communication/Client-Arduino/SerialCommunication.h" -#include "WaterChannelController/WaterChannelController.h" #include "serial_communication/Client-Arduino/ValveController.h" +#include "system/Scheduler.h" +#include "task/InputTask.h" +#include "task/ValveTask.h" +#include "task/SerialCommunicationTask.h" +#include "Config.h" +#include "task/LcdTask.h" #include "Arduino.h" +Scheduler *scheduler; +WaterChannelController waterChannelController; + void setup() { Serial.begin(9600); - delay(10); + scheduler = new Scheduler(BASE_PERIOD); + + // Initialize the task and add them to the scheduler + scheduler->addTask(new LcdTask(150, &waterChannelController, 0x27, 16, 2)); + scheduler->addTask(new InputTask(100, &waterChannelController, BUTTON_PIN, POT_PIN)); + scheduler->addTask(new ValveTask(105, &waterChannelController, SERVO_PIN)); + scheduler->addTask(new SerialCommunicationTask(200, JsonProcessor(), &waterChannelController)); } void loop() { - JsonProcessor jsonProcessor; - SerialCommunicationChannel commChannel(jsonProcessor); - commChannel.initializeSerialCommunication(); - // WaterChannelController waterChannelController(9, 4, A0, 0x27, 16, 2); - // waterChannelController.reading(); + scheduler->schedule(); } diff --git a/water-channel-controller/src/serial_communication/Client-Arduino/MessageSenderImpl.cpp b/water-channel-controller/src/serial_communication/Client-Arduino/MessageSenderImpl.cpp index c3d036f..19e4aec 100644 --- a/water-channel-controller/src/serial_communication/Client-Arduino/MessageSenderImpl.cpp +++ b/water-channel-controller/src/serial_communication/Client-Arduino/MessageSenderImpl.cpp @@ -1,4 +1,6 @@ +#define DEBUG #include "MessageSender.h" +#include "system/Logger.h" MessageSender::MessageSender() {} @@ -8,7 +10,7 @@ bool MessageSender::sendMessage(String message) { // Send the message to the server size_t bytesSent = Serial.println(message); - + logger("Sent message: " + message); // Check if the message is sent return checkMessageSent(bytesSent, message); } diff --git a/water-channel-controller/src/serial_communication/Client-Arduino/SerialCommunication.h b/water-channel-controller/src/serial_communication/Client-Arduino/SerialCommunication.h index 00a0b07..7912ff5 100644 --- a/water-channel-controller/src/serial_communication/Client-Arduino/SerialCommunication.h +++ b/water-channel-controller/src/serial_communication/Client-Arduino/SerialCommunication.h @@ -1,120 +1,128 @@ -#ifndef __COMMUNICATION_H__ -#define __COMMUNICATION_H__ - -#include "Arduino.h" -#include "ArduinoJson.h" -#include "JsonProcessor.h" -#include "MessageSender.h" -#include "MessageReceiver.h" - -/** - * @brief Manages a serial communication channel between a client and a server. - * - * The SerialCommunicationChannel class provides a high-level interface - * for managing serial communication between a client (an Arduino board) - * and a server (a computer). - * It encapsulates the details of sending and receiving messages over a serial port, - * and provides methods for processing received messages, checking message availability, - * and closing the communication channel. - * - * Key responsibilities of this class include: - * - Processing incoming messages and formatting outgoing messages. - * - Managing the state of the communication channel (open, closed). - * - Checking the availability of incoming messages. - * - * @note This class is specifically designed for communication where - * the client is an Arduino board and the server is a computer. - */ -class SerialCommunicationChannel -{ - -public: - /** - * @brief Constructs a new SerialCommunicationChannel object. - */ - SerialCommunicationChannel(JsonProcessor jsonProcessor); - - /** - * @brief Uses RAII to automatically close the serial port when the object is destroyed. - */ - ~SerialCommunicationChannel(); - - /** - * @brief Initializes the serial communication channel. - * - * This function sets up the serial communication with the Arduino board. - * It should be called before any other serial communication functions are used. - */ - void initializeSerialCommunication(); - -private: - /** - * @brief An instance of the JsonProcessor class. - * - * This instance, jsonProcessor, is used to handle JSON-formatted messages within the SerialCommunication class. It provides methods for processing these messages, including parsing the JSON content, extracting relevant information, and formatting it for use by other parts of the application. - */ - JsonProcessor jsonProcessor; - - /** - * @brief The status of the valve received from the server. - * - * The valve's status depends on the received message. - */ - String status; - - /** - * @brief The valve value received from the server. - * - * This value is used to control the valve's opening and closing percentage. - */ - String valveValue; - - /** - * @brief The content of the message received from the server. - */ - String content; - - /** - * @brief The baud rate used by the serial communication channel. - */ - const int BAUD_RATE = 9600; - - /** - * @brief The maximum amount of time to wait for data to be available on the serial port. - * - * This constant defines the maximum amount of time, in milliseconds, that the system - * will wait for data to become available on the serial port before timing out. - * It is used in the getReceivedContent method to control how long the method waits for incoming data. - */ - const unsigned long WAIT_TIME = 500; - - /** - * @brief A flag indicating whether a message is available - * on the serial port to be read by the client. - */ - bool messageAvailable; - - /** - * @brief A flag indicating whether my message has been - * delivered to the server. - */ - bool messageDelivered; - - /** - * @brief Processes a specific end message received from the server - * to close the communication channel. - */ - void receivedEndMessage(); - - /** - * @brief an instance of the MessageSender class. - */ - MessageSender messageSender; - - /** - * @brief an instance of the MessageReceiver class. - */ - MessageReceiver messageReceiver; -}; - -#endif // __COMMUNICATION_H__ \ No newline at end of file +// #ifndef __COMMUNICATION_H__ +// #define __COMMUNICATION_H__ + +// #include "Arduino.h" +// #include "ArduinoJson.h" +// #include "JsonProcessor.h" +// #include "MessageSender.h" +// #include "MessageReceiver.h" +// #include "system/Logger.h" +// #include "Config.h" +// #include "system/Task.h" +// #include "WaterChannelController.h" + +// /** +// * @brief Manages a serial communication channel between a client and a server. +// * +// * The SerialCommunicationChannel class provides a high-level interface +// * for managing serial communication between a client (an Arduino board) +// * and a server (a computer). +// * It encapsulates the details of sending and receiving messages over a serial port, +// * and provides methods for processing received messages, checking message availability, +// * and closing the communication channel. +// * +// * Key responsibilities of this class include: +// * - Processing incoming messages and formatting outgoing messages. +// * - Managing the state of the communication channel (open, closed). +// * - Checking the availability of incoming messages. +// * +// * @note This class is specifically designed for communication where +// * the client is an Arduino board and the server is a computer. +// */ +// class SerialCommunicationChannel : public Task +// { + +// public: +// /** +// * @brief Constructs a new SerialCommunicationChannel object. +// */ +// SerialCommunicationChannel(JsonProcessor jsonProcessor, int period, WaterChannelController *waterChannelController); + +// void tick() override; + +// /** +// * @brief Uses RAII to automatically close the serial port when the object is destroyed. +// */ +// ~SerialCommunicationChannel(); + +// /** +// * @brief Initializes the serial communication channel. +// * +// * This function sets up the serial communication with the Arduino board. +// * It should be called before any other serial communication functions are used. +// */ +// void initializeSerialCommunication(); + +// private: +// /** +// * @brief An instance of the JsonProcessor class. +// * +// * This instance, jsonProcessor, is used to handle JSON-formatted messages within the SerialCommunication class. It provides methods for processing these messages, including parsing the JSON content, extracting relevant information, and formatting it for use by other parts of the application. +// */ +// JsonProcessor jsonProcessor; + +// /** +// * @brief The status of the valve received from the server. +// * +// * The valve's status depends on the received message. +// */ +// String status; + +// /** +// * @brief The valve value received from the server. +// * +// * This value is used to control the valve's opening and closing percentage. +// */ +// String valveValue; + +// /** +// * @brief The content of the message received from the server. +// */ +// String content; + +// /** +// * @brief The baud rate used by the serial communication channel. +// */ +// const int BAUD_RATE = 9600; + +// /** +// * @brief The maximum amount of time to wait for data to be available on the serial port. +// * +// * This constant defines the maximum amount of time, in milliseconds, that the system +// * will wait for data to become available on the serial port before timing out. +// * It is used in the getReceivedContent method to control how long the method waits for incoming data. +// */ +// const unsigned long WAIT_TIME = 500; + +// /** +// * @brief A flag indicating whether a message is available +// * on the serial port to be read by the client. +// */ +// bool messageAvailable; + +// /** +// * @brief A flag indicating whether my message has been +// * delivered to the server. +// */ +// bool messageDelivered; + +// /** +// * @brief Processes a specific end message received from the server +// * to close the communication channel. +// */ +// void receivedEndMessage(); + +// /** +// * @brief an instance of the MessageSender class. +// */ +// MessageSender messageSender; + +// /** +// * @brief an instance of the MessageReceiver class. +// */ +// MessageReceiver messageReceiver; + +// WaterChannelController *waterChannelController; +// }; + +// #endif // __COMMUNICATION_H__ \ No newline at end of file diff --git a/water-channel-controller/src/serial_communication/Client-Arduino/SerialCommunicationImpl.cpp b/water-channel-controller/src/serial_communication/Client-Arduino/SerialCommunicationImpl.cpp index 40d3631..f2ce7d8 100644 --- a/water-channel-controller/src/serial_communication/Client-Arduino/SerialCommunicationImpl.cpp +++ b/water-channel-controller/src/serial_communication/Client-Arduino/SerialCommunicationImpl.cpp @@ -1,22 +1,22 @@ -#include "SerialCommunication.h" +// #include "SerialCommunication.h" -SerialCommunicationChannel::SerialCommunicationChannel(JsonProcessor jsonProcessor) - : jsonProcessor(jsonProcessor), messageAvailable(false), messageDelivered(false) {} +// SerialCommunicationChannel::SerialCommunicationChannel(JsonProcessor jsonProcessor) +// : jsonProcessor(jsonProcessor), messageAvailable(false), messageDelivered(false) {} -SerialCommunicationChannel::~SerialCommunicationChannel() -{ - Serial.end(); -} +// SerialCommunicationChannel::~SerialCommunicationChannel() +// { +// Serial.end(); +// } -void SerialCommunicationChannel::initializeSerialCommunication() -{ - Serial.begin(BAUD_RATE); - messageReceiver.checkMessageAvailability(); - messageReceiver.getReceivedContent(); -} +// void SerialCommunicationChannel::initializeSerialCommunication() +// { +// Serial.begin(BAUD_RATE); +// messageReceiver.checkMessageAvailability(); +// messageReceiver.getReceivedContent(); +// } -void SerialCommunicationChannel::receivedEndMessage() -{ - String message = jsonProcessor.formatMessage(status, valveValue); - messageSender.sendMessage(message); -} \ No newline at end of file +// void SerialCommunicationChannel::receivedEndMessage() +// { +// String message = jsonProcessor.formatMessage(status, valveValue); +// messageSender.sendMessage(message); +// } \ No newline at end of file diff --git a/water-channel-controller/src/serial_communication/Client-Java/SerialCommunicationChannel.java b/water-channel-controller/src/serial_communication/Client-Java/SerialCommunicationChannel.java deleted file mode 100644 index dfe4656..0000000 --- a/water-channel-controller/src/serial_communication/Client-Java/SerialCommunicationChannel.java +++ /dev/null @@ -1,50 +0,0 @@ -package serial_communication.Client; -import javax.naming.CommunicationException; - -/** - * This is an interface that represents a communication channel between a client - * and a server over a serial port. - * It defines methods for processing received messages, sending messages, - * checking if a message is available, and closing the communication channel. - */ -public interface SerialCommunicationChannel { - - /** - * Processes a message received from the server. - * This method should be called by the client when a message is received from - * the server. - * - * @param messageToProcess the message received from the server. - * @return the processed message received from the server. - * @throws InterruptedException if the thread is interrupted while processing - * the message. - */ - public String processReceivedMessage(String messageToProcess) throws InterruptedException; - - /** - * Sends a message to the server. - * This method should be called by the client to send a message to the server. - * - * @param messageToSend the message to send to the server. - * @throws CommunicationException if there is a problem sending the message to - * the server. - */ - public void sendMessage(String messageToSend) throws CommunicationException; - - /** - * Checks if a message is available on the serial port to be read by the client. - * This method should be called by the client to check if a message is available - * on the serial port. - * - * @return true if a message is available, false otherwise. - */ - boolean isMessageAvailable(); - - /** - * Closes the communication channel. - * This method should be called by the client to close the communication channel - * with the server. - */ - public void close(); - -} \ No newline at end of file diff --git a/water-channel-controller/src/serial_communication/Client-Java/SerialCommunicationChannelImpl.java b/water-channel-controller/src/serial_communication/Client-Java/SerialCommunicationChannelImpl.java deleted file mode 100644 index a108ed6..0000000 --- a/water-channel-controller/src/serial_communication/Client-Java/SerialCommunicationChannelImpl.java +++ /dev/null @@ -1,157 +0,0 @@ -package serial_communication.Client; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; - -import javax.naming.CommunicationException; - -import jssc.SerialPort; -import jssc.SerialPortEvent; -import jssc.SerialPortEventListener; -import jssc.SerialPortException; - -/** - * This class implementation represents a communication channel between a client - * and a server over a serial port. - * It uses the jSSC (Java Simple Serial Connector) library to handle the serial - * communication. - */ -public class SerialCommunicationChannelImpl implements SerialCommunicationChannel, SerialPortEventListener { - - /** - * The serial port used for the communication. - */ - private SerialPort serialPort; - /** - * The queue of messages received from the server. - */ - private BlockingQueue messageQueue; - /** - * The current message being received from the server. - */ - private StringBuffer currentMessage = new StringBuffer(""); - - /** - * Constructs a new with the specified serial port and rate. - * - * @param serialPort The serial port to use for the communication. - * @param rate The baud rate of the serial port. - * @throws CommunicationException If the communication channel cannot be opened. - * @throws SerialPortException If the serial port cannot be opened. - */ - public SerialCommunicationChannelImpl(SerialPort serialPort, int rate) - throws CommunicationException, SerialPortException { - this.serialPort = new SerialPort(serialPort.getPortName()); - this.messageQueue = new LinkedBlockingQueue(); - this.serialPort.openPort(); - this.serialPort.setParams(rate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); - this.serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT); - this.serialPort.addEventListener(this); - } - - /** - * Processes a message received from the server. - * This method should be called by the client when a message is received from - * the server. - * - * @param messageToProcess the message received from the server. - * @return the processed message received from the server. - * @throws InterruptedException if the thread is interrupted while processing - * the message. - */ - @Override - public String processReceivedMessage(String messageToProcess) throws InterruptedException { - return messageQueue.take(); - } - - /** - * Sends a message to the server. - * This method should be called by the client to send a message to the server. - * - * @param messageToSend the message to send to the server. - * @throws CommunicationException if there is a problem sending the message to - * the server. - */ - @Override - public void sendMessage(String messageToSend) throws CommunicationException { - char[] message = (messageToSend + "\n").toCharArray(); - byte[] messageBytes = new byte[message.length]; - for (int i = 0; i < messageBytes.length; i++) { - messageBytes[i] = (byte) message[i]; - } - try { - synchronized (this.serialPort) { - this.serialPort.writeBytes(messageBytes); - } - } catch (SerialPortException exception) { - exception.printStackTrace(); - throw new CommunicationException("Error while sending message from client to server"); - } - } - - /** - * Checks if a message is available on the serial port to be read by the client. - * This method should be called by the client to check if a message is available - * on the serial port. - * - * @return true if a message is available, {@code false} otherwise. - */ - @Override - public boolean isMessageAvailable() { - return !messageQueue.isEmpty(); - } - - /** - * Closes the communication channel. - * This method should be called by the client to close the communication channel - * with the server. - */ - @Override - public void close() { - try { - if (this.serialPort.isOpened()) { - this.serialPort.removeEventListener(); - this.serialPort.closePort(); - } - } catch (Exception exception) { - exception.printStackTrace(); - } - } - - /** - * Handles a serial port event. - * This method is called by the jSSC library when a serial port event occurs, - * such as receiving data. - * - * @param event the serial port event. - */ - @Override - public void serialEvent(SerialPortEvent event) { - if (event.isRXCHAR()) { - try { - String message = serialPort.readString(event.getEventValue()); - message = message.replaceAll("\r", ""); - currentMessage.append(message); - boolean goAhead = true; - while (goAhead) { - String currentMessageString = currentMessage.toString(); - int index = currentMessageString.indexOf("\n"); - if (index >= 0) { - messageQueue.put(currentMessageString.substring(0, index)); - currentMessage = new StringBuffer(""); - if (index + 1 < currentMessageString.length()) { - currentMessage.append(currentMessageString.substring(index + 1)); - } - } else { - goAhead = false; - } - } - } catch (SerialPortException exception) { - exception.printStackTrace(); - System.out.println("Error in receiving string from COM-port: " + exception); - } catch (InterruptedException exception) { - exception.printStackTrace(); - System.out.println("Error in receiving string from COM-port: " + exception); - } - } - } -} \ No newline at end of file diff --git a/water-channel-controller/src/system/Logger.cpp b/water-channel-controller/src/system/Logger.cpp new file mode 100644 index 0000000..6412722 --- /dev/null +++ b/water-channel-controller/src/system/Logger.cpp @@ -0,0 +1,20 @@ +#include "config.h" + +void logger(String log) +{ +#ifdef __DEBUG__ + Serial.println(log); +#endif +} +void logger(double log) +{ +#ifdef __DEBUG__ + Serial.println(log); +#endif +} +void logger(int log) +{ +#ifdef __DEBUG__ + Serial.println(log); +#endif +} \ No newline at end of file diff --git a/water-channel-controller/src/system/Logger.h b/water-channel-controller/src/system/Logger.h new file mode 100644 index 0000000..5e76b37 --- /dev/null +++ b/water-channel-controller/src/system/Logger.h @@ -0,0 +1,33 @@ +#ifndef __LOGGER__ +#define __LOGGER__ + +#include "Arduino.h" + +/** + * @brief Logs a string message. + * + * This function logs a string message to the console or other output device. + * + * @param log The string message to log. + */ +void logger(String log); + +/** + * @brief Logs a double value. + * + * This function logs a double value to the console or other output device. The value is converted to a string before logging. + * + * @param log The double value to log. + */ +void logger(double log); + +/** + * @brief Logs an integer value. + * + * This function logs an integer value to the console or other output device. The value is converted to a string before logging. + * + * @param log The integer value to log. + */ +void logger(int log); + +#endif // __LOGGER__ \ No newline at end of file diff --git a/water-channel-controller/src/system/Scheduler.cpp b/water-channel-controller/src/system/Scheduler.cpp new file mode 100644 index 0000000..33c11dd --- /dev/null +++ b/water-channel-controller/src/system/Scheduler.cpp @@ -0,0 +1,51 @@ +#include "scheduler.h" +#include "TimerOne.h" +#include "logger.h" + +volatile bool timerFlag; + +void timerHandler(void) +{ + if (timerFlag == true) + { + // logger("Scheduler: Timer overrun\n"); + } + timerFlag = true; +} + +Scheduler::Scheduler(int basePeriod) : basePeriod(basePeriod) +{ + timerFlag = false; + long period = 1000l * basePeriod; + Timer1.initialize(period); + Timer1.attachInterrupt(timerHandler); +} + +bool Scheduler::addTask(Task *task) +{ + if (nTasks < MAX_TASKS - 1) + { + taskList[nTasks] = task; + nTasks++; + return true; + } + else + { + return false; + } +} + +void Scheduler::schedule() +{ + while (!timerFlag) + { + } + for (int i = 0; i < nTasks; i++) + { + if (taskList[i]->updateAndCheckTime(basePeriod)) + { + taskList[i]->tick(); + } + } + timerFlag = false; +} \ No newline at end of file diff --git a/water-channel-controller/src/system/Scheduler.h b/water-channel-controller/src/system/Scheduler.h new file mode 100644 index 0000000..5df952d --- /dev/null +++ b/water-channel-controller/src/system/Scheduler.h @@ -0,0 +1,49 @@ +#ifndef __SCHEDULER__ +#define __SCHEDULER__ + +#include "task.h" + +constexpr int MAX_TASKS = 50; + +/** + * @brief A scheduler for tasks. + * + * This class represents a scheduler that can schedule tasks to run at certain periods. + * The scheduler can hold up to MAX_TASKS tasks. + */ +class Scheduler +{ +public: + /** + * @brief Constructor for a Scheduler. + * + * This constructor initializes a new Scheduler with the specified base period. + * + * @param basePeriod The base period for the scheduler. + */ + Scheduler(int basePeriod); + + /** + * @brief Adds a task to the scheduler. + * + * This method adds a task to the scheduler. If the scheduler is already full, the method returns false. + * + * @param task The task to add. + * @return true if the task was added successfully, false otherwise. + */ + virtual bool addTask(Task *task); + + /** + * @brief Schedules the tasks in the scheduler. + * + * This method schedules the tasks in the scheduler to run at their specified periods. + */ + virtual void schedule(); + +private: + int basePeriod; ///< The base period for the scheduler. + int nTasks{0}; ///< The number of tasks currently in the scheduler. + Task *taskList[MAX_TASKS]{}; ///< The list of tasks in the scheduler. +}; + +#endif // __SCHEDULER__ \ No newline at end of file diff --git a/water-channel-controller/src/system/Task.h b/water-channel-controller/src/system/Task.h new file mode 100644 index 0000000..63b386d --- /dev/null +++ b/water-channel-controller/src/system/Task.h @@ -0,0 +1,87 @@ +#ifndef __TASK__ +#define __TASK__ + +/** + * @brief Abstract base class for a task. + * + * This class represents a task that can be scheduled to run at a certain period. + * The actual task is implemented by subclasses in the `tick` method. + */ +class Task +{ + +public: + /** + * @brief Constructor for a Task. + * + * This constructor initializes a new Task with the specified period. + * + * @param period The period of the task. + */ + Task(int period) : myPeriod(period) {} + + /** + * @brief Virtual method for the task to be performed. + * + * This method is implemented by subclasses to perform the actual task. + */ + virtual void tick() = 0; + + /** + * @brief Get the period of the task. + * + * @return The period of the task. + */ + int getPeriod() const { return myPeriod; } + + /** + * @brief Set the period of the task. + * + * @param period The new period of the task. + */ + void setPeriod(int period) { myPeriod = period; } + + /** + * @brief Get the elapsed time since the last time the task was performed. + * + * @return The elapsed time. + */ + int getTimeElapsed() const { return timeElapsed; } + + /** + * @brief Set the elapsed time since the last time the task was performed. + * + * @param time The new elapsed time. + */ + void setTimeElapsed(int time) { timeElapsed = time; } + + /** + * @brief Updates the elapsed time and checks if it's time to perform the task. + * + * This method increments the elapsed time by the base period and checks if it's time to perform the task. + * If the elapsed time is greater than or equal to the task's period, the elapsed time is reset to 0 and the method returns true. + * Otherwise, the method returns false. + * + * @param basePeriod The base period to increment the elapsed time by. + * @return true if it's time to perform the task, false otherwise. + */ + bool updateAndCheckTime(int basePeriod) + { + timeElapsed += basePeriod; + if (timeElapsed >= myPeriod) + { + timeElapsed = 0; + return true; + } + else + { + return false; + } + } + +private: + int myPeriod; ///< The period of the task. + int timeElapsed{0}; ///< The elapsed time since the last time the task was performed. +}; + +#endif // __TASK__ \ No newline at end of file diff --git a/water-channel-controller/src/system/TaskWithState.h b/water-channel-controller/src/system/TaskWithState.h new file mode 100644 index 0000000..401de9e --- /dev/null +++ b/water-channel-controller/src/system/TaskWithState.h @@ -0,0 +1,75 @@ +#ifndef __TASK_WITH_STATE__ +#define __TASK_WITH_STATE__ + +#include "Arduino.h" +#include "system/task.h" + +/** + * @brief A task with a state. + * + * This class represents a task that has a state. The state can be of any type. + * The class provides methods to set and get the state, and to get the elapsed time since the state was last set. + * + * @param T The type of the state. + */ +template +class TaskWithState : public Task +{ +public: + /** + * @brief Constructor for a TaskWithState. + * + * This constructor initializes a new TaskWithState with the specified period and initializes the state. + * + * @param period The period of the task. + * @param initialState The initial state of the task. + */ + TaskWithState(int period, T initialState = T()) + : Task(period), state(initialState) + { + } + +protected: + /** + * @brief Sets the state of the task. + * + * This method sets the state of the task and updates the state timestamp to the current time. + * + * @param s The new state. + */ + void setState(T s) + { + state = s; + stateTimestamp = millis(); + } + + /** + * @brief Gets the state of the task. + * + * This method returns the current state of the task. + * + * @return The current state of the task. + */ + T getState() + { + return this->state; + } + + /** + * @brief Gets the elapsed time in the current state. + * + * This method returns the elapsed time since the state was last set. + * + * @return The elapsed time in the current state. + */ + unsigned long elapsedTimeInState() + { + return millis() - stateTimestamp; + } + +private: + T state; ///< The current state of the task. + unsigned long stateTimestamp{0}; ///< The timestamp when the state was last set. +}; + +#endif // __TASK_WITH_STATE__ \ No newline at end of file diff --git a/water-channel-controller/src/task/InputTask.cpp b/water-channel-controller/src/task/InputTask.cpp new file mode 100644 index 0000000..99ff8b7 --- /dev/null +++ b/water-channel-controller/src/task/InputTask.cpp @@ -0,0 +1,78 @@ +#include "InputTask.h" + +#include "Arduino.h" + +#include "WaterChannelController.h" +#include "components/api/ButtonImpl.h" +#include "components/api/PotentiometerImpl.h" +#include "system/Logger.h" + +InputTask::InputTask(int period, WaterChannelController *WaterChannelController, int buttonPin, int potPin) + : Task(period), + waterChannelController(WaterChannelController), + pressed(false), + button(new ButtonImpl(buttonPin, true)), + potentiometer(new PotentiometerImpl(potPin, 20)), + oldPotPos(0), + lastButtonPress(0) +{ + waterChannelController->posChange = true; + oldPotPos = potentiometer->position(); +} + +void InputTask::tick() +{ + handleButtonPress(); + handleModeChange(); + updatePressedState(); +}; + +void InputTask::handleButtonPress() +{ + unsigned long currentMillis = millis(); + if (button->isPressed() && !this->pressed && (currentMillis - lastButtonPress > DEBOUNCE_DELAY)) + { + lastButtonPress = currentMillis; + toggleMode(); + waterChannelController->commChange = true; + } +} + +void InputTask::toggleMode() +{ + waterChannelController->activeMode = waterChannelController->activeMode == Mode::MANUAL ? Mode::AUTOMATIC : Mode::MANUAL; +} + +void InputTask::handleModeChange() +{ + if (waterChannelController->activeMode == Mode::MANUAL) + { + handleManualMode(); + } + else if (waterChannelController->activeMode == Mode::AUTOMATIC) + { + handleAutomaticMode(); + } +} + +void InputTask::handleManualMode() +{ + if (potentiometer->moved()) + { + waterChannelController->activePosition = potentiometer->position(); + waterChannelController->posChange = true; + } +} + +void InputTask::handleAutomaticMode() +{ + String receivedContent = messageReceiver.getReceivedContent(); + String systemState = jsonProcessor.getSystemState(receivedContent); + int valveValue = valveController.getValveValueForStateAsInt(systemState); + waterChannelController->activePosition = valveValue; +} + +void InputTask::updatePressedState() +{ + this->pressed = button->isPressed(); +} \ No newline at end of file diff --git a/water-channel-controller/src/task/InputTask.h b/water-channel-controller/src/task/InputTask.h new file mode 100644 index 0000000..7faa21d --- /dev/null +++ b/water-channel-controller/src/task/InputTask.h @@ -0,0 +1,39 @@ +#ifndef __INPUTTASK__ +#define __INPUTTASK__ + +#include "WaterChannelController.h" +#include "system/taskWithState.h" +#include "system/task.h" +#include "components/api/ServoMotor.h" +#include "components/api/Button.h" +#include "components/api/Potentiometer.h" +#include "serial_communication/Client-Arduino/MessageReceiver.h" +#include "serial_communication/Client-Arduino/JsonProcessor.h" +#include "serial_communication/Client-Arduino/ValveController.h" + +class InputTask : public Task +{ +public: + InputTask(int period, WaterChannelController *WaterChannelController, int buttonPin, int potPin); + void tick() override; + void handleButtonPress(); + void handleModeChange(); + void toggleMode(); + void updatePressedState(); + void handleManualMode(); + void handleAutomaticMode(); + +private: + WaterChannelController *waterChannelController; + MessageReceiver messageReceiver; + JsonProcessor jsonProcessor; + ValveController valveController; + bool pressed; + Button *button; + Potentiometer *potentiometer; + unsigned int oldPotPos; + const unsigned long DEBOUNCE_DELAY = 50; + unsigned long lastButtonPress; +}; + +#endif // __INPUTTASK__ \ No newline at end of file diff --git a/water-channel-controller/src/task/LcdTask.cpp b/water-channel-controller/src/task/LcdTask.cpp new file mode 100644 index 0000000..6280ff6 --- /dev/null +++ b/water-channel-controller/src/task/LcdTask.cpp @@ -0,0 +1,18 @@ +#include "LcdTask.h" + +#include "components/api/LCD.h" + +LcdTask::LcdTask(int period, WaterChannelController *waterChannelController, uint8_t lcd_Addr, uint8_t lcd_cols, uint8_t lcd_rows) + : Task(period), waterChannelController(waterChannelController), lcd(new Lcd(lcd_Addr, lcd_cols, lcd_rows)), colDisplay(lcd_cols) +{ + this->lcd->clear(); + this->lcd->show("Water Channel Controller"); +} + +void LcdTask::tick() +{ + const String mode = waterChannelController->activeMode == Mode::AUTOMATIC ? "Automatic: " : "Manual: "; + const String position = String(waterChannelController->activePosition); + String message = mode + position; + lcd->show(message); +} \ No newline at end of file diff --git a/water-channel-controller/src/task/LcdTask.h b/water-channel-controller/src/task/LcdTask.h new file mode 100644 index 0000000..68375d8 --- /dev/null +++ b/water-channel-controller/src/task/LcdTask.h @@ -0,0 +1,23 @@ +#ifndef __LCD_TASK__ +#define __LCD_TASK__ + +#include "WaterChannelController.h" +#include "components/api/Display.h" +#include "system/task.h" + +class LcdTask : public Task +{ +public: + LcdTask(int period, + WaterChannelController *WaterChannelController, + uint8_t lcd_Addr, + uint8_t lcd_cols, + uint8_t lcd_rows); + void tick() override; + +private: + WaterChannelController *waterChannelController; + Display *lcd; + unsigned short colDisplay; +}; +#endif // __LCD_TASK__ \ No newline at end of file diff --git a/water-channel-controller/src/task/SerialCommunicationTask.cpp b/water-channel-controller/src/task/SerialCommunicationTask.cpp new file mode 100644 index 0000000..9ca8914 --- /dev/null +++ b/water-channel-controller/src/task/SerialCommunicationTask.cpp @@ -0,0 +1,85 @@ +#include "task/SerialCommunicationTask.h" +#include "system/Logger.h" +#define DEBUG + +SerialCommunicationTask::SerialCommunicationTask(int period, JsonProcessor jsonProcessor, WaterChannelController *waterChannelController) + : Task(period), + waterChannelController(waterChannelController), + lastMessage(0), + messageReceiver(), + messageSender(), + jsonProcessor(jsonProcessor), + messageAvailable(false), + messageDelivered(0) +{ + Serial.begin(BAUD_RATE); +} + +void SerialCommunicationTask::tick() +{ + unsigned long startTime = millis(); + String receivedContent = ""; + while (millis() - startTime < 600) + { + logger("Checking message availability"); + if (Serial.available() > 0) + { + logger("Message available"); + messageAvailable = true; + String message = Serial.readStringUntil('\n'); + JsonDocument doc; + deserializeJson(doc, message); + String status = doc["status"]; // This will hold the status value from the server + String valveValue = doc["valveValue"]; // Get valveValue as a string + // int valveValueInt = valveValue.toInt(); // Convert valveValue to an integer + + // Log the status and valveValue: + // logger("Status: " + status); + // logger("Valve Value: " + valveValue); + // logger("Valve Value Int: " + String(valveValueInt)); + + // Create a confirmation message and send it back: + String confirmationMessage = createConfirmationMessage(message); + + break; + } + } +} + +String SerialCommunicationTask::formatMessage(String status, String valveValue) +{ + JsonDocument doc; + // Set the values in the JSON document + doc["status"] = status; + doc["valveValue"] = valveValue; + + // Serialize JSON document to string + String formattedMessage; + serializeJson(doc, formattedMessage); + + return formattedMessage; +} + +String SerialCommunicationTask::createConfirmationMessage(String originalMessage) +{ + JsonDocument doc; + deserializeJson(doc, originalMessage); + + String status = doc["status"]; + String valveValue = doc["valveValue"]; + // this->motor->setPosition(valveValue.toInt()); // Set the valve position to the value received from the server + + String confirmationMessage; + serializeJson(doc, confirmationMessage); + + return confirmationMessage; +} + +String SerialCommunicationTask::getSystemStatus(String receivedMessage) +{ + JsonDocument doc; + deserializeJson(doc, receivedMessage); + + String status = doc["status"]; + return status; +} diff --git a/water-channel-controller/src/task/SerialCommunicationTask.h b/water-channel-controller/src/task/SerialCommunicationTask.h new file mode 100644 index 0000000..b849b82 --- /dev/null +++ b/water-channel-controller/src/task/SerialCommunicationTask.h @@ -0,0 +1,39 @@ +#ifndef __SERIAL_COMMUNICATION_H__ +#define __SERIAL_COMMUNICATION_H__ + +#include "WaterChannelController.h" +#include "system/Task.h" +#include "serial_communication/Client-Arduino/JsonProcessor.h" +#include "serial_communication/Client-Arduino/MessageReceiver.h" +#include "serial_communication/Client-Arduino/MessageSender.h" +#include "system/Logger.h" +#include "../components/api/ServoMotor.h" +#include "Arduino.h" + +class SerialCommunicationTask : public Task +{ +public: + SerialCommunicationTask(int period, JsonProcessor jsonProcessor, WaterChannelController *waterChannelController); + void initializeSerialCommunication(); + void receivedEndMessage(); + String formatMessage(String status, String valveValue); + String createConfirmationMessage(String originalMessage); + String getSystemStatus(String receivedMessage); + void tick() override; + +private: + unsigned long BAUD_RATE = 9600; + WaterChannelController *waterChannelController; + unsigned long int lastMessage{0}; + MessageReceiver messageReceiver; + MessageSender messageSender; + JsonProcessor jsonProcessor; + String status; + String valveValue; + String content; + bool messageAvailable; + bool messageDelivered; + ServoMotor *motor; ///< The ServoMotor used to control the valve. +}; + +#endif // __SERIAL_COMMUNICATION_H__ diff --git a/water-channel-controller/src/task/ValveTask.cpp b/water-channel-controller/src/task/ValveTask.cpp new file mode 100644 index 0000000..65344f5 --- /dev/null +++ b/water-channel-controller/src/task/ValveTask.cpp @@ -0,0 +1,32 @@ +#include "ValveTask.h" +#include "components/api/ServoMotorImpl.h" + +ValveTask::ValveTask(int period, WaterChannelController *waterChannelController, int servoPin) + : TaskWithState(period), waterChannelController(waterChannelController), motor(new ServoMotorImpl(servoPin)) +{ + this->motor->on(); + this->motor->setPosition(0); + setState(ValveStates::Idle); +} + +void ValveTask::tick() +{ + switch (this->getState()) + { + case ValveStates::Idle: + if (this->waterChannelController->posChange) + { + this->motor->setPosition(map((int)this->waterChannelController->activePosition, 0, 100, 0, 180)); + this->waterChannelController->posChange = false; + } + break; + case ValveStates::Opening: + if (this->elapsedTimeInState() > this->timeToMove) + { + this->setState(ValveStates::Idle); + } + break; + default: + break; + } +} \ No newline at end of file diff --git a/water-channel-controller/src/task/ValveTask.h b/water-channel-controller/src/task/ValveTask.h new file mode 100644 index 0000000..6c84c13 --- /dev/null +++ b/water-channel-controller/src/task/ValveTask.h @@ -0,0 +1,55 @@ +#ifndef __VALVE_TASK__ +#define __VALVE_TASK__ + +#include "WaterChannelController.h" +#include "../components/api/ServoMotor.h" +#include "system/Task.h" +#include "system/TaskWithState.h" +#include "config.h" + +/** + * @brief Enum for the states of the valve. + * + * This enum represents the possible states of the valve: Idle and Opening. + */ +enum class ValveStates +{ + Idle, + Opening, +}; + +/** + * @brief A task for controlling a valve. + * + * This class represents a task for controlling a valve using a servo motor. + * The task uses a WaterChannelController to control the water channel and a ServoMotor to control the valve. + * The state of the task is one of the ValveStates. + */ +class ValveTask : public TaskWithState +{ +public: + /** + * @brief Constructor for a ValveTask. + * + * This constructor initializes a new ValveTask with the specified period, WaterChannelController, and servo pin. + * + * @param period The period of the task. + * @param WaterChannelController The WaterChannelController used to control the water channel. + * @param servoPin The pin connected to the servo motor. + */ + ValveTask(int period, WaterChannelController *WaterChannelController, int servoPin); + + /** + * @brief Performs the task. + * + * This method is called when it's time to perform the task. The actual implementation of the task is provided by subclasses. + */ + void tick() override; + +private: + WaterChannelController *waterChannelController; ///< The WaterChannelController used to control the water channel. + ServoMotor *motor; ///< The ServoMotor used to control the valve. + unsigned int timeToMove{TIME_TO_MOVE}; ///< The time it takes for the valve to move. +}; + +#endif // __VALVE_TASK__ \ No newline at end of file