Skip to content

Commit

Permalink
Merge pull request #3 from psmgeelen/feature/async_control
Browse files Browse the repository at this point in the history
Async control & api update
  • Loading branch information
GreenWizard2015 authored Jan 7, 2024
2 parents 743c64f + eade444 commit 86b2ede
Show file tree
Hide file tree
Showing 25 changed files with 591 additions and 78 deletions.
29 changes: 29 additions & 0 deletions .github/workflows/platformio_build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: PlatformIO CI - Build for Arduino

on: [push]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: "3.9"
- name: Install PlatformIO Core
run: pip install --upgrade platformio

# rename src/secrets.h.example to src/secrets.h, to use a dummy values during build
- name: Copy secrets.h
run: cp ./controller/tea_poor/src/secrets.h.example ./controller/tea_poor/src/secrets.h

- name: Build PlatformIO Project for Arduino
working-directory: ./controller/tea_poor
run: pio run -e uno_r4_wifi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: PlatformIO CI
name: PlatformIO CI - Unit Tests

on: [push]

Expand All @@ -20,6 +20,6 @@ jobs:
- name: Install PlatformIO Core
run: pip install --upgrade platformio

- name: Build PlatformIO Project
- name: Run tests on the native platform
working-directory: ./controller/tea_poor
run: pio run
run: pio test -e native
3 changes: 3 additions & 0 deletions controller/tea_poor/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

# hide secrets
src/secrets.h
15 changes: 15 additions & 0 deletions controller/tea_poor/lib/Arduino/ArduinoEnvironment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Arduino environment
#ifndef ARDUINO_ENVIRONMENT_H
#define ARDUINO_ENVIRONMENT_H

#include <IEnvironment.h>
#include <Arduino.h>

class ArduinoEnvironment : public IEnvironment {
public:
unsigned long time() const override {
return millis();
}
};

#endif // ARDUINO_ENVIRONMENT_H
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;
}
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
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/Arduino/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
58 changes: 58 additions & 0 deletions controller/tea_poor/lib/CommandProcessor/CommandProcessor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include "CommandProcessor.h"
#include <cstring>
#include <sstream>

bool isValidIntNumber(const char *str, const int maxValue, const int minValue=0) {
const int len = strlen(str);
if (len < 1) return false;
// check that string contains only digits
// first character can be '-' for negative numbers
if ((str[0] != '-') && !isdigit(str[0])) return false;
for (int i = 1; i < len; i++) {
if (!isdigit(str[i])) return false;
}
// check that number is in range
const int value = atoi(str);
if (value < minValue) return false;
if (maxValue <= value) return false;
return true;
}

std::string CommandProcessor::status() {
std::stringstream response;
response << "{";
// send water threshold
response << "\"water threshold\": " << _waterPumpSafeThreshold << ", ";
// send water pump status
const auto waterPumpStatus = _waterPump->status();
const auto now = _env->time();
const auto timeLeft = waterPumpStatus.isRunning ? waterPumpStatus.stopTime - now : 0;
response
<< "\"pump\": {"
<< " \"running\": " << (waterPumpStatus.isRunning ? "true, " : "false, ")
<< " \"time left\": " << timeLeft
<< "}";
// end of water pump status
///////////////////////////////////
// send remote control status
// response << "\"remote control\": " << remoteControl.asJSONString();
// end of JSON
response << "}";

return response.str();
}

std::string CommandProcessor::pour_tea(const char *milliseconds) {
if (!isValidIntNumber(milliseconds, _waterPumpSafeThreshold)) {
// send error message as JSON
return std::string("{ \"error\": \"invalid milliseconds value\" }");
}
// start pouring tea
_waterPump->start( atoi(milliseconds), _env->time() );
return status();
}

std::string CommandProcessor::stop() {
_waterPump->stop();
return status();
}
30 changes: 30 additions & 0 deletions controller/tea_poor/lib/CommandProcessor/CommandProcessor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// CommandProcessor class definition
#ifndef COMMANDPROCESSOR_H
#define COMMANDPROCESSOR_H

#include <string>
#include <IWaterPumpSchedulerAPI.h>
#include <IEnvironment.h>

// This class is used to process incoming commands
class CommandProcessor {
public:
CommandProcessor(
int waterPumpSafeThreshold,
const IEnvironmentPtr env,
const IWaterPumpSchedulerAPIPtr waterPump
) :
_waterPumpSafeThreshold(waterPumpSafeThreshold),
_env(env),
_waterPump(waterPump)
{}

std::string status();
std::string pour_tea(const char *milliseconds);
std::string stop();
private:
const int _waterPumpSafeThreshold;
const IEnvironmentPtr _env;
const IWaterPumpSchedulerAPIPtr _waterPump;
};
#endif // COMMANDPROCESSOR_H
28 changes: 0 additions & 28 deletions controller/tea_poor/lib/WaterPumpController/WaterPumpController.h

This file was deleted.

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

WaterPumpScheduler::WaterPumpScheduler(IWaterPumpPtr waterPump, unsigned long forceStopIntervalMs) :
_waterPump(waterPump),
_forceStopIntervalMs(forceStopIntervalMs)
{
}

WaterPumpScheduler::~WaterPumpScheduler() {}

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
}
}

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

#include <IWaterPump.h>
#include <IWaterPumpSchedulerAPI.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 : public IWaterPumpSchedulerAPI {
private:
IWaterPumpPtr _waterPump;
unsigned long _stopTime = 0;
// each X milliseconds will force stop water pump
unsigned long _forceStopIntervalMs;
public:
WaterPumpScheduler(IWaterPumpPtr waterPump, unsigned long forceStopIntervalMs);
WaterPumpScheduler(IWaterPumpPtr waterPump) : WaterPumpScheduler(waterPump, 1000) {}
~WaterPumpScheduler();

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

// Public API
void start(unsigned long runTimeMs, unsigned long currentTimeMs) override;
void stop() override;
WaterPumpStatus status() override;
};
#endif
13 changes: 13 additions & 0 deletions controller/tea_poor/lib/interfaces/IEnvironment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef IENVIRONMENT_H
#define IENVIRONMENT_H

#include <memory>

class IEnvironment {
public:
virtual unsigned long time() const = 0;
virtual ~IEnvironment() {}
};

typedef std::shared_ptr<IEnvironment> IEnvironmentPtr;
#endif // IENVIRONMENT_H
19 changes: 19 additions & 0 deletions controller/tea_poor/lib/interfaces/IWaterPump.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef IWATERPUMP_H
#define IWATERPUMP_H

#include <memory>

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

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

virtual bool isRunning() const = 0;
};

// define shared pointer alias
using IWaterPumpPtr = std::shared_ptr<IWaterPump>;
#endif
Loading

0 comments on commit 86b2ede

Please sign in to comment.