Skip to content

Commit

Permalink
Basic impl. with a bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
GreenWizard2015 committed Dec 28, 2023
1 parent 090fd4e commit aff9e62
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 52 deletions.
8 changes: 8 additions & 0 deletions controller/tea_poor/lib/RemoteControl/RemoteControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,12 @@ void RemoteControl::process() {
_app.process(&client);
client.stop();
}
}

String RemoteControl::asJSONString() const {
String result = "{";
result += "\"SSID\": \"" + _SSID + "\",";
result += "\"signal strength\": " + String(WiFi.RSSI());
result += "}";
return result;
}
1 change: 1 addition & 0 deletions controller/tea_poor/lib/RemoteControl/RemoteControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class RemoteControl {
~RemoteControl();
void setup(RemoteControlRoutesCallback routes);
void process();
String asJSONString() const;
private:
const String _SSID;
const String _SSIDPassword;
Expand Down
15 changes: 15 additions & 0 deletions controller/tea_poor/lib/WaterPump/IWaterPump.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef IWATERPUMP_H
#define IWATERPUMP_H

class IWaterPump {
public:
virtual ~IWaterPump() {}

virtual void setup() = 0;
virtual void start() = 0;
virtual void stop() = 0;

virtual bool isRunning() const = 0;
};

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,17 @@ void WaterPumpController::setup() {
pinMode(_directionPin, OUTPUT);
pinMode(_brakePin, OUTPUT);
pinMode(_powerPin, OUTPUT);
// TODO: check that its okay to do during setup
stopPump();
stop();
}

void WaterPumpController::pour(int milliseconds) {
startPump();
delay(milliseconds);
stopPump();
}

void WaterPumpController::startPump() {
_state = PUMP_ON;
void WaterPumpController::start() {
_isRunning = true;
digitalWrite(_brakePin, LOW); // release breaks
analogWrite(_powerPin, 255);
}

void WaterPumpController::stopPump() {
void WaterPumpController::stop() {
digitalWrite(_brakePin, HIGH); // activate breaks
analogWrite(_powerPin, 0);
_state = PUMP_OFF;
}
_isRunning = false;
}
23 changes: 23 additions & 0 deletions controller/tea_poor/lib/WaterPump/WaterPumpController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef WATERPUMPCONTROLLER_H
#define WATERPUMPCONTROLLER_H
#include "IWaterPump.h"

class WaterPumpController: public IWaterPump {
private:
const int _directionPin;
const int _brakePin;
const int _powerPin;
const int _maxPower = 255;
bool _isRunning = false;
public:
WaterPumpController(int directionPin, int brakePin, int powerPin);
virtual ~WaterPumpController() override;

virtual void setup() override;
virtual void start() override;
virtual void stop() override;

virtual bool isRunning() const override { return _isRunning; }
};

#endif
36 changes: 36 additions & 0 deletions controller/tea_poor/lib/WaterPump/WaterPumpScheduler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#include "WaterPumpScheduler.h"

WaterPumpScheduler::WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs) {
}

WaterPumpScheduler::~WaterPumpScheduler() {
delete _waterPump;
}

void WaterPumpScheduler::setup() {
_waterPump->setup();
}

void WaterPumpScheduler::start(unsigned long runTimeMs, unsigned long currentTimeMs) {
_stopTime = currentTimeMs + runTimeMs;
_waterPump->start();
}

void WaterPumpScheduler::stop() {
_waterPump->stop();
_stopTime = 0; // a bit of paranoia :)
}

void WaterPumpScheduler::tick(unsigned long currentTimeMs) {
if (_stopTime <= currentTimeMs) {
stop();
_stopTime = currentTimeMs + _forceStopIntervalMs; // force stop after X milliseconds
}
}

WaterPumpScheduler::WaterPumpStatus WaterPumpScheduler::status() {
return {
_waterPump->isRunning(),
_stopTime
};
}
33 changes: 33 additions & 0 deletions controller/tea_poor/lib/WaterPump/WaterPumpScheduler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef WATERPUMPSCHEDULER_H
#define WATERPUMPSCHEDULER_H

#include "IWaterPump.h"

// This class is responsible for scheduling water pump
// It is used to make sure that water pump is running for a limited time
// It is also ensuring that water pump is stopped if not needed
class WaterPumpScheduler {
private:
IWaterPump* _waterPump;
unsigned long _stopTime = 0;
// each X milliseconds will force stop water pump
unsigned long _forceStopIntervalMs;
public:
WaterPumpScheduler(IWaterPump* waterPump, unsigned long forceStopIntervalMs);
WaterPumpScheduler(IWaterPump* waterPump) : WaterPumpScheduler(waterPump, 1000) {}
~WaterPumpScheduler();

void setup();
void stop();
// for simplicity and testability we are passing current time as parameter
void start(unsigned long runTimeMs, unsigned long currentTimeMs);
void tick(unsigned long currentTimeMs);

// pump status
struct WaterPumpStatus {
bool isRunning;
unsigned long stopTime;
};
WaterPumpStatus status();
};
#endif
28 changes: 0 additions & 28 deletions controller/tea_poor/lib/WaterPumpController/WaterPumpController.h

This file was deleted.

5 changes: 5 additions & 0 deletions controller/tea_poor/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ board = uno_r4_wifi
framework = arduino
lib_deps =
lasselukkari/aWOT@^3.5.0
test_ignore = local

[env:local]
platform = native
test_framework = googletest
60 changes: 49 additions & 11 deletions controller/tea_poor/src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
#include <Arduino.h>
#include <WaterPumpController.h>
#include <WaterPumpScheduler.h>
#include <RemoteControl.h>

// Setting up water pump
WaterPumpController waterPumpController(12, 9, 3);
WaterPumpScheduler waterPump(
new 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;
Expand All @@ -14,8 +17,38 @@ RemoteControl remoteControl(
"VerySecurePassword" // network password
);

void _sendSystemStatus(Response &res) {
// send system status as JSON
res.println("{");
// send water threshold
res.print("\"water threshold\": ");
res.print(WATER_PUMP_SAFE_THRESHOLD);
res.println(",");

// send water pump status
const auto waterPumpStatus = waterPump.status();
res.println("\"pump\": {");
res.print("\"running\": ");
res.print(waterPumpStatus.isRunning ? "true, " : "false, ");
const unsigned long timeLeft =
waterPumpStatus.isRunning ?
waterPumpStatus.stopTime - millis() :
0;
res.print("\"time left\": ");
res.print(timeLeft);
res.println("},");
// end of water pump status
///////////////////////////////////
// send remote control status
res.print("\"remote control\": ");
res.print(remoteControl.asJSONString());
res.println();
// end of JSON
res.println("}");
}

bool isValidIntNumber(const char *str, const int maxValue, const int minValue=0) {
if (strlen(str) <= 0) return false;
if (strlen(str) < 1) return false;
const int value = atoi(str);
if (value < minValue) return false;
if (maxValue <= value) return false;
Expand All @@ -31,24 +64,29 @@ void pour_tea(Request &req, Response &res) {
res.println(WATER_PUMP_SAFE_THRESHOLD);
return;
}
const int pouringDelayMs = atoi(milliseconds);
// actually pour tea
waterPumpController.pour(pouringDelayMs);

// Serial.println(req.JSON());
res.print("Poured Tea in: ");
res.print(pouringDelayMs);
res.print(" milliseconds!");
// start pouring tea
waterPump.start( atoi(milliseconds), millis() );
_sendSystemStatus(res);
}

void setup() {
Serial.begin(9600);
waterPumpController.setup();
waterPump.setup();
remoteControl.setup([](Application &app) {
app.get("/pour_tea", pour_tea);
// stop water pump
app.get("/stop", [](Request &req, Response &res) {
waterPump.stop();
_sendSystemStatus(res);
});
// get system status
app.get("/status", [](Request &req, Response &res) {
_sendSystemStatus(res);
});
});
}

void loop() {
waterPump.tick(millis());
remoteControl.process();
};
69 changes: 69 additions & 0 deletions controller/tea_poor/test/test_local/WaterPumpScheduler_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// I wasn't able to run tests at all. Run them locally and confirm that they are working.
// Its either a local problem or a problem with the configuration of the project.
// Further goes a sketch of the tests, but I wasn't able to run them.
#include <gtest/gtest.h>
#include <WaterPumpScheduler.h>

// Fake water pump
class FakeWaterPump : public IWaterPump {
private:
bool _isRunning = false;
public:
void setup() override { _isRunning = false; }
void start() override { _isRunning = true; }
void stop() override { _isRunning = false; }

bool isRunning() override { return _isRunning; }
};
// End of fake water pump

// test that pump is stopping after given time
TEST(WaterPumpScheduler, test_pump_stops_after_given_time) {
// random time between 1 and 10 seconds
const unsigned long runTimeMs = 1000 + (rand() % 10) * 1000;
FakeWaterPump fakeWaterPump;
WaterPumpScheduler waterPumpScheduler(&fakeWaterPump);
waterPumpScheduler.setup();
// start water pump
unsigned long currentTimeMs = 0;
waterPumpScheduler.start(runTimeMs, currentTimeMs);
// check status
auto status = waterPumpScheduler.status();
ASSERT_TRUE(status.isRunning);
ASSERT_EQ(status.stopTime, runTimeMs);

while (currentTimeMs < runTimeMs) {
waterPumpScheduler.tick(currentTimeMs);
ASSERT_TRUE(fakeWaterPump->isRunning());
currentTimeMs += 100;
}
// pump should be stopped after given time
waterPumpScheduler.tick(runTimeMs + 1);
ASSERT_FALSE(fakeWaterPump->isRunning());
}

// test that pump is periodically forced to stop after given time
TEST(WaterPumpScheduler, test_pump_is_periodically_forced_to_stop_after_given_time) {
FakeWaterPump fakeWaterPump;
WaterPumpScheduler waterPumpScheduler(&fakeWaterPump, 1000); // force stop each 1 second
waterPumpScheduler.setup();
// start water pump
unsigned long currentTimeMs = 0;
waterPumpScheduler.start(1, currentTimeMs);
currentTimeMs += 1;
waterPumpScheduler.tick(currentTimeMs);
ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped after given time

for(int i = 0; i < 10; i++) {
// emulate that pump was started again
fakeWaterPump.start();
currentTimeMs += 1000;
waterPumpScheduler.tick(currentTimeMs);
ASSERT_FALSE(fakeWaterPump->isRunning()); // pump should be stopped
}
}

int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

0 comments on commit aff9e62

Please sign in to comment.