diff --git a/controller/tea_poor/.gitignore b/controller/tea_poor/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/controller/tea_poor/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/controller/tea_poor/lib/RemoteControl/RemoteControl.cpp b/controller/tea_poor/lib/RemoteControl/RemoteControl.cpp new file mode 100644 index 0000000..c4ebe9d --- /dev/null +++ b/controller/tea_poor/lib/RemoteControl/RemoteControl.cpp @@ -0,0 +1,98 @@ +#include "RemoteControl.h" + +void printMacAddress(byte mac[]) { + for (int i = 0; i < 6; i++) { + if (i > 0) { + Serial.print(":"); + } + if (mac[i] < 16) { + Serial.print("0"); + } + Serial.print(mac[i], HEX); + } + Serial.println(); +} + +void debugNetworkInfo() { + Serial.print("Connected. IP: "); + Serial.println(WiFi.localIP()); + + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + // print the MAC address of the router you're attached to: + byte bssid[6]; + WiFi.BSSID(bssid); + Serial.print("BSSID: "); + printMacAddress(bssid); + + // print the received signal strength: + Serial.print("signal strength (RSSI): "); + Serial.println(WiFi.RSSI()); + + // print the encryption type: + Serial.print("Encryption Type: "); + Serial.println(WiFi.encryptionType(), HEX); + + Serial.println("----------------------------------------------"); + Serial.println(); +} + +RemoteControl::RemoteControl(const char* SSID, const char* SSIDPassword) : + _SSID(SSID), _SSIDPassword(SSIDPassword), + _server(80), _app() +{ +} + +RemoteControl::~RemoteControl() { +} + +void RemoteControl::_setupNetwork() { + if (WiFi.status() == WL_NO_MODULE) { + Serial.println("Communication with WiFi module failed!"); + while(true) delay(500); + } + + String firmware_version = WiFi.firmwareVersion(); + if ( firmware_version < WIFI_FIRMWARE_LATEST_VERSION) { + Serial.print("Latest available version: "); + Serial.println(WIFI_FIRMWARE_LATEST_VERSION); + Serial.println("Please upgrade your firmware."); + } + + Serial.print("Connecting to "); + Serial.println(_SSID); + + int attempts = 0; + while (WL_CONNECTED != WiFi.status()) { // try to connect to the network + attempts++; + Serial.println("Atempt to connect: " + String(attempts)); + WiFi.begin(_SSID.c_str(), _SSIDPassword.c_str()); + for (int i = 0; i < 50; i++) { // wait for connection + Serial.print("."); + delay(500); + if (WL_CONNECTED == WiFi.status()) break; + } + Serial.println(); + Serial.println("Connection status: " + String(WiFi.status())); + } + Serial.println(); + + debugNetworkInfo(); +} + +void RemoteControl::setup(RemoteControlRoutesCallback routes) { + _setupNetwork(); + routes(_app); // setup routes + _server.begin(); +} + +void RemoteControl::process() { + // TODO: check if we still have a connection. If not, reconnect. + WiFiClient client = _server.available(); + + if (client.connected()) { + _app.process(&client); + client.stop(); + } +} \ No newline at end of file diff --git a/controller/tea_poor/lib/RemoteControl/RemoteControl.h b/controller/tea_poor/lib/RemoteControl/RemoteControl.h new file mode 100644 index 0000000..78fb5e5 --- /dev/null +++ b/controller/tea_poor/lib/RemoteControl/RemoteControl.h @@ -0,0 +1,26 @@ +#ifndef REMOTECONTROL_H +#define REMOTECONTROL_H + +#include +#include +#include + +// define routes callback function signature +typedef void (*RemoteControlRoutesCallback)(Application &app); + +class RemoteControl { +public: + RemoteControl(const char* SSID, const char* SSIDPassword); + ~RemoteControl(); + void setup(RemoteControlRoutesCallback routes); + void process(); +private: + const String _SSID; + const String _SSIDPassword; + WiFiServer _server; + Application _app; + + void _setupNetwork(); +}; + +#endif \ No newline at end of file diff --git a/controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp b/controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp new file mode 100644 index 0000000..b8ce60f --- /dev/null +++ b/controller/tea_poor/lib/WaterPumpController/WaterPumpController.cpp @@ -0,0 +1,38 @@ +#include +#include "WaterPumpController.h" + +WaterPumpController::WaterPumpController(int directionPin, int brakePin, int powerPin) : + _directionPin(directionPin), + _brakePin(brakePin), + _powerPin(powerPin) +{ + +} + +WaterPumpController::~WaterPumpController() {} + +void WaterPumpController::setup() { + pinMode(_directionPin, OUTPUT); + pinMode(_brakePin, OUTPUT); + pinMode(_powerPin, OUTPUT); + // TODO: check that its okay to do during setup + stopPump(); +} + +void WaterPumpController::pour(int milliseconds) { + startPump(); + delay(milliseconds); + stopPump(); +} + +void WaterPumpController::startPump() { + _state = PUMP_ON; + digitalWrite(_brakePin, LOW); // release breaks + analogWrite(_powerPin, 255); +} + +void WaterPumpController::stopPump() { + digitalWrite(_brakePin, HIGH); // activate breaks + analogWrite(_powerPin, 0); + _state = PUMP_OFF; +} diff --git a/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h b/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h new file mode 100644 index 0000000..8561876 --- /dev/null +++ b/controller/tea_poor/lib/WaterPumpController/WaterPumpController.h @@ -0,0 +1,28 @@ +#ifndef WATERPUMPCONTROLLER_H +#define WATERPUMPCONTROLLER_H + +class WaterPumpController { +public: + enum EPumpState { + PUMP_OFF, + PUMP_ON + }; +private: + const int _directionPin; + const int _brakePin; + const int _powerPin; + const int _maxPower = 255; + EPumpState _state = PUMP_OFF; +public: + WaterPumpController(int directionPin, int brakePin, int powerPin); + ~WaterPumpController(); + + void setup(); + void pour(int miliseconds); + void startPump(); + void stopPump(); + + EPumpState state() const { return _state; } +}; + +#endif // WATERPUMPCONTROLLER_H diff --git a/controller/tea_poor/platformio.ini b/controller/tea_poor/platformio.ini index c98bfb6..1515112 100644 --- a/controller/tea_poor/platformio.ini +++ b/controller/tea_poor/platformio.ini @@ -13,5 +13,4 @@ platform = renesas-ra board = uno_r4_wifi framework = arduino lib_deps = - bblanchon/ArduinoJson@^6.21.4 lasselukkari/aWOT@^3.5.0 diff --git a/controller/tea_poor/src/main.cpp b/controller/tea_poor/src/main.cpp index aa8247b..87b8358 100644 --- a/controller/tea_poor/src/main.cpp +++ b/controller/tea_poor/src/main.cpp @@ -1,205 +1,54 @@ #include -#include -#include -#include +#include +#include -// setting up WiFi -const char *SSID = "MyWiFiNetwork"; -const char *PWD = "VerySecurePassword"; +// Setting up water pump +WaterPumpController waterPumpController(12, 9, 3); +// Just for safety reasons, we don't want to pour tea for too long +// Their is no reason to make it configurable and add unnecessary complexity +const int WATER_PUMP_SAFE_THRESHOLD = 10 * 1000; -// Setting up Motorcontroller -int directionPin = 12; -int pwmPin = 3; -int brakePin = 9; +// setting up remote control +RemoteControl remoteControl( + "MyWiFiNetwork", // network name/SSID + "VerySecurePassword" // network password +); -// minimalistic webserver -WiFiServer server(80); -Application app; - -// Start value Threshold for pouring -int threshold_pour = 10; - -void printMacAddress(byte mac[]) { - for (int i = 0; i < 6; i++) { - if (i > 0) { - Serial.print(":"); - } - if (mac[i] < 16) { - Serial.print("0"); - } - Serial.print(mac[i], HEX); - } - Serial.println(); -} - - -void printCurrentNet() { - // print the SSID of the network you're attached to: - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); - - // print the MAC address of the router you're attached to: - byte bssid[6]; - WiFi.BSSID(bssid); - Serial.print("BSSID: "); - printMacAddress(bssid); - - // print the received signal strength: - long rssi = WiFi.RSSI(); - Serial.print("signal strength (RSSI):"); - Serial.println(rssi); - - // print the encryption type: - byte encryption = WiFi.encryptionType(); - Serial.print("Encryption Type:"); - Serial.println(encryption, HEX); - Serial.println(); -} - - -void connectToWiFi() { - - if (WiFi.status() == WL_NO_MODULE) { - Serial.println("Communication with WiFi module failed!"); - // block further activity - while(true); - } - - // info about your adapter - String firmware_version = WiFi.firmwareVersion(); - - Serial.print("WiFi Firmware Version: "); - Serial.println(firmware_version); - if ( firmware_version < WIFI_FIRMWARE_LATEST_VERSION) { - Serial.print("Latest available version: "); - Serial.println(WIFI_FIRMWARE_LATEST_VERSION); - Serial.println("Please upgrade your firmware."); - } - - - Serial.print("Connecting to "); - Serial.println(SSID); - - WiFi.begin(SSID, PWD); - - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(500); - // we can even make the ESP32 to sleep - } - - Serial.print("Connected. IP: "); - Serial.println(WiFi.localIP()); - printCurrentNet(); -} - - - -void set_threshold(Request &req, Response &res) { - char seconds_char[64]; - req.query("seconds", seconds_char, 64); - - if (strlen(seconds_char) != NULL){ - threshold_pour = atoi(seconds_char); - res.print("You have reset the threshold! Please make sure that this is safe, as it aims to prevent overflow due to mistyping the query parameter. The threshold is now set to: "); - res.print(threshold_pour); - } else { - res.print("Please specify amount of seconds in query parameter; threshold?seconds=10 e.g.."); - } -} - -void get_threshold(Request &req, Response &res) { - res.print("The threshold is: "); - res.print(threshold_pour); - res.print(" seconds."); +bool isValidIntNumber(const char *str, const int maxValue, const int minValue=0) { + if (strlen(str) <= 0) return false; + const int value = atoi(str); + if (value < minValue) return false; + if (maxValue <= value) return false; + return true; } void pour_tea(Request &req, Response &res) { - char seconds_char[64]; - req.query("seconds", seconds_char, 64); - int pouring_delay = atoi(seconds_char); - - if (strlen(seconds_char) != NULL && pouring_delay <= threshold_pour ){ - //release breaks - digitalWrite(brakePin, LOW); - - //set work duty for the motor, Duty is the amount of power it is getting from 0 to 256. - analogWrite(pwmPin, 256); - - delay(pouring_delay*1000); // convert miliseconds to seconds - - //activate breaks - digitalWrite(brakePin, HIGH); - - //set work duty for the motor to 0 (off) - analogWrite(pwmPin, 0); - // Serial.println(req.JSON()); - res.print("Poured Tea in: "); - res.print(pouring_delay); - res.print(" seconds!"); - } else if (pouring_delay > threshold_pour) { - res.print("Exceeded safety threshold for pouring. Change threshold in firmware or use endpoint /flush and set minutes"); - } - else { - res.print("Please specify amount of seconds in query parameter; pour_tea?seconds=10 e.g.."); + char milliseconds[64]; + req.query("milliseconds", milliseconds, 64); + if (!isValidIntNumber(milliseconds, WATER_PUMP_SAFE_THRESHOLD)) { + res.println("Please specify amount of milliseconds in query parameter; pour_tea?milliseconds=10 e.g."); + res.print("Maximal allowed time is: "); + res.println(WATER_PUMP_SAFE_THRESHOLD); + return; } -} + const int pouringDelayMs = atoi(milliseconds); + // actually pour tea + waterPumpController.pour(pouringDelayMs); -void flush(Request &req, Response &res) { - char minutes_char[64]; - req.query("minutes", minutes_char, 64); - - if (strlen(minutes_char) != NULL){ - //release breaks - digitalWrite(brakePin, LOW); - - //set work duty for the motor, Duty is the amount of power it is getting from 0 to 256. - analogWrite(pwmPin, 256); - - int pouring_delay = atoi(minutes_char); - delay(pouring_delay*1000*60); // convert miliseconds to seconds - - //activate breaks - digitalWrite(brakePin, HIGH); - - //set work duty for the motor to 0 (off) - analogWrite(pwmPin, 0); - // Serial.println(req.JSON()); - res.print("Poured Tea in: "); - res.print(pouring_delay); - res.print(" minutes!"); - } else { - res.print("Please specify amount of seconds in query parameter; flush?minutes=1 e.g.."); - } + // Serial.println(req.JSON()); + res.print("Poured Tea in: "); + res.print(pouringDelayMs); + res.print(" milliseconds!"); } void setup() { - - // define print serial port Serial.begin(9600); - //define pins - pinMode(directionPin, OUTPUT); - pinMode(pwmPin, OUTPUT); - pinMode(brakePin, OUTPUT); - - // connect to WiFi - connectToWiFi(); - - // Set endpoints - app.post("/pour_tea", &pour_tea); - app.post("/flush", &flush); - app.get("/threshold", &get_threshold); - app.post("/threshold", &set_threshold); - - // setup Server - server.begin(); + waterPumpController.setup(); + remoteControl.setup([](Application &app) { + app.get("/pour_tea", pour_tea); + }); } void loop() { - WiFiClient client = server.available(); - - if (client.connected()) { - app.process(&client); - client.stop(); - } + remoteControl.process(); }; \ No newline at end of file