Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
- various clean up
- analogcreeping: analog creep towards setpoint asynchronously
- relay: add exception option for flipping on to early
- growlights rewrite
- moisture sensor provides percentage through MappedRange (q: should all metrics instead be normalized when applicable)
  • Loading branch information
s-t-a-n committed Jul 8, 2024
1 parent 7ddf32c commit 8edb0cb
Show file tree
Hide file tree
Showing 23 changed files with 504 additions and 369 deletions.
4 changes: 2 additions & 2 deletions src/kaskas/data_providers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ enum class DataProviders {
HEATING_SURFACE_TEMP,
HEATING_SURFACE_FAN,
HEATING_ELEMENT,
VIOLET_SPECTRUM,
BROAD_SPECTRUM,
REDBLUE_SPECTRUM,
FULL_SPECTRUM,
PUMP,
SIZE
};
24 changes: 13 additions & 11 deletions src/kaskas/events.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ namespace kaskas {

enum class Events {
WakeUp, //
ShutDown,
UIButtonCheck,
UIWatchDog,
UIPromptFollowUp,
SensorFollowUp,
OutOfWater,
VentilationFollowUp,
VentilationStart,
VentilationStop,
VentilationCycleCheck,
VentilationCycleStart,
VentilationCycleStop,
VentilationAutoTune,
HeatingAutoTune,
HeatingFollowUp,
HeatingCycleCheck,
Expand All @@ -24,15 +27,14 @@ enum class Events {
WaterInjectStart,
WaterInjectFollowUp,
WaterInjectStop,
LightCycleStart,
LightCycleEnd,
VioletSpectrumTurnOn,
VioletSpectrumTurnOff,
BroadSpectrumTurnOn,
BroadSpectrumTurnOff,
MetricsStartDatadump,
MetricsFollowUp,
MetricsStopDatadump,
LightRedBlueSpectrumCycleCheck,
LightRedBlueSpectrumCycleStart,
LightRedBlueSpectrumCycleEnd,
LightFullSpectrumCycleCheck,
LightFullSpectrumCycleStart,
LightFullSpectrumCycleEnd,
MetricsReady,
MetricsTainted,
Size
};
}; // namespace kaskas
10 changes: 5 additions & 5 deletions src/kaskas/io/controllers/heater.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ class Heater {

void autotune(PID::TuneConfig&& cfg, std::function<void()> process_loop = {}) {
block_until_setpoint(cfg.startpoint);
set_setpoint(cfg.setpoint);
set_target_setpoint(cfg.setpoint);
const auto process_setter = [&](double pwm_value) {
const auto normalized_response = (pwm_value - _cfg.pid_cfg.output_lower_limit)
/ (_cfg.pid_cfg.output_upper_limit - _cfg.pid_cfg.output_lower_limit);
Expand All @@ -255,7 +255,7 @@ class Heater {
update_state();
}

void set_setpoint(const Value setpoint) {
void set_target_setpoint(const Value setpoint) {
_pid.set_target_setpoint(std::min(_cfg.max_heater_setpoint, setpoint));
_climate_trp.adjust_setpoint(setpoint);
}
Expand Down Expand Up @@ -292,18 +292,18 @@ class Heater {
//
if (_surface_temperature.value() < MIN_SURFACE_TEMPERATURE
|| _surface_temperature.value() > MAX_SURFACE_TEMPERATURE) {
spn::throw_exception(spn::assertion_error("Heater: Heater element temperature out of limits"));
spn::throw_exception(spn::assertion_exception("Heater: Heater element temperature out of limits"));
}
if (_climate_temperature.value() < MIN_INSIDE_TEMPERATURE
|| _climate_temperature.value() > MAX_INSIDE_TEMPERATURE) {
spn::throw_exception(spn::assertion_error("Heater: Climate temperature out of limits"));
spn::throw_exception(spn::assertion_exception("Heater: Climate temperature out of limits"));
}

if (_climate_trp.is_runaway()) {
HAL::printf("Runaway detected: surfaceT %.2f, climateT %.2f, throttle: %i/255, state: %s",
_surface_temperature.value(), _climate_temperature.value(), int(throttle() * 255),
std::string(as_stringview(state())).c_str());
spn::throw_exception(spn::assertion_error("Heater: Run away detected"));
spn::throw_exception(spn::assertion_exception("Heater: Run away detected"));
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/kaskas/io/hardware_stack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ class HardwareStack : public VirtualStack {
/// Safely shutdown all peripherals
void safe_shutdown(bool critical = false) {
for (auto& p : _peripherals) {
if (p)
if (p) {
HAL::delay(time_ms(100));
p->safe_shutdown(critical);
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/kaskas/io/peripherals/DS18B20_Temp_Probe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ class DS18B20TempProbe : public FilteredPeripheral {

public:
DS18B20TempProbe(const Config& cfg) : FilteredPeripheral(cfg.sampling_interval, 1), _cfg(cfg), _ds18b20(_cfg.pin) {
_fs.attach_filter(BandPass::Middle());
_fs.attach_filter(BandPass::Broad());
}
~DS18B20TempProbe() override = default;

void initialize() override {
_ds18b20_probe_selector = _ds18b20.selectNext();
if (_ds18b20_probe_selector == 0) {
spn::throw_exception(spn::assertion_error("DS18B20TempProbe could not be initialized"));
spn::throw_exception(spn::assertion_exception("DS18B20TempProbe could not be initialized"));
}

update();
Expand Down
2 changes: 1 addition & 1 deletion src/kaskas/io/peripherals/DS3231_RTC_EEPROM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class DS3231Clock final : public Peripheral {
DBGF("DS3231Clock initialized. The reported time is %u:%u @ %u:%u:%u", n.getHour(), n.getMinute(),
n.getDay(), n.getMonth(), n.getYear());
} else {
::spn::throw_exception(::spn::assertion_error("DS3231Clock failed to initialize. Maybe set the time?"));
::spn::throw_exception(::spn::assertion_exception("DS3231Clock failed to initialize. Maybe set the time?"));
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/kaskas/io/peripherals/SHT31_TempHumidityProbe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class SHT31TempHumidityProbe : public Peripheral {
SHT31TempHumidityProbe(const Config& cfg)
: Peripheral(cfg.sampling_interval), _cfg(cfg), _sht31(SHT31(_cfg.i2c_address)), _temperature_fs(1),
_humidity_fs(1) {
_temperature_fs.attach_filter(BandPass::Middle());
_humidity_fs.attach_filter(BandPass::Middle());
_temperature_fs.attach_filter(BandPass::Broad());
_humidity_fs.attach_filter(BandPass::Broad());
}

void initialize() override {
Expand All @@ -44,7 +44,7 @@ class SHT31TempHumidityProbe : public Peripheral {
update();

if (!is_ready()) {
spn::throw_exception(spn::assertion_error("SHT31TempHumidityProbe could not be initialized"));
spn::throw_exception(spn::assertion_exception("SHT31TempHumidityProbe could not be initialized"));
}
DBGF("SHT31TempHumidityProbe initialized. Humidity: %.2f %%, temperature: %.2f °C", read_humidity(),
read_temperature());
Expand Down
45 changes: 42 additions & 3 deletions src/kaskas/io/peripherals/analogue_output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "kaskas/io/provider.hpp"
#include "kaskas/io/providers/analogue.hpp"

#include <float.h>
#include <spine/core/debugging.hpp>
#include <spine/core/exception.hpp>
#include <spine/core/time.hpp>
Expand All @@ -11,24 +12,62 @@ namespace kaskas::io {

class AnalogueOutputPeripheral : public HAL::AnalogueOutput, public Peripheral {
public:
explicit AnalogueOutputPeripheral(const Config&& cfg) : HAL::AnalogueOutput(std::move(cfg)) {}
explicit AnalogueOutputPeripheral(const Config&& cfg, time_ms sampling_speed = time_ms(100))
: HAL::AnalogueOutput(std::move(cfg)), Peripheral(sampling_speed), _creep_time_on_target({}) {}
~AnalogueOutputPeripheral() override = default;

void initialize() override { HAL::AnalogueOutput::initialize(); }

void safe_shutdown(bool critical) override { this->fade_to(0.0); }

void update() override {
if (!_is_creeping)
return;
const auto creep_delta = _creep_target_value - value();
if (std::fabs(creep_delta) < std::fabs(_creep_target_increment)) { // on target
if (value() != _creep_target_value) { // finalize
_is_creeping = false;
AnalogueOutput::set_value(_creep_target_value);
}
return;
}
if (_creep_target_increment < 0 == creep_delta < 0) { // creep without overshoot
AnalogueOutput::set_value(value() + _creep_target_increment);
}

if (_creep_time_on_target.expired()) { // move directly if out of time
fade_to(_creep_target_value);
_is_creeping = false;
}
}

AnalogueActuator analogue_output_provider() {
return {AnalogueActuator::FunctionMap{
.value_f = [this]() { return this->value(); },
.set_value_f = [this](double value) { AnalogueOutput::set_value(value); },
.fade_to_f =
[this](double setpoint, double increment = 0.1, time_ms increment_interval = time_ms(100)) {
HAL::AnalogueOutput::fade_to(setpoint, increment, increment_interval);
}
},
.creep_to_f = [this](double setpoint, time_ms travel_time) { this->creep_to(setpoint, travel_time); }}};
}

/// set a target value and let the updater approach the value with a single increment per sample
void creep_to(const double setpoint, const time_ms travel_time = time_s(1000)) {
if (value() == setpoint)
return;

}};
_is_creeping = true;
_creep_target_value = setpoint;
_creep_time_on_target = spn::core::time::AlarmTimer(travel_time);
_creep_target_increment = (setpoint - value()) / (travel_time / update_interval()).raw<double>();
}

private:
bool _is_creeping = false;
double _creep_target_value = 0;
double _creep_target_increment = 0;
spn::core::time::AlarmTimer _creep_time_on_target;
};

} // namespace kaskas::io
15 changes: 12 additions & 3 deletions src/kaskas/io/peripherals/relay.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Relay : public Peripheral {
struct Config {
HAL::DigitalOutput::Config pin_cfg;
time_ms backoff_time;
bool throw_on_early_flip = true;
};

public:
Expand All @@ -30,9 +31,17 @@ class Relay : public Peripheral {

void set_state(LogicalState state) {
// hard protect against flipping relay back on within backoff threshold
if (_backoff_timer && _cfg.backoff_time > time_ms(0) && _backoff_timer->timeSinceLast() < _cfg.backoff_time
&& state == ON && _pin.state() == OFF) {
spn::throw_exception(spn::runtime_error("Tried to flip relay within backoff threshold"));
const auto time_since_last_flip = _backoff_timer->timeSinceLast();
if (_backoff_timer && _cfg.backoff_time > time_ms(0) && time_since_last_flip < _cfg.backoff_time && state == ON
&& _pin.state() == OFF) {
if (_cfg.throw_on_early_flip) {
spn::throw_exception(spn::runtime_exception("Tried to flip relay within backoff threshold"));
} else {
DBG("--------------------------------------------------------------------------");
DBGF("Tried to flip relay within backoff threshold: %ims since last flip",
time_ms(time_since_last_flip).printable());
DBG("--------------------------------------------------------------------------");
}
}

if (!_backoff_timer && _cfg.backoff_time > time_s(0))
Expand Down
2 changes: 2 additions & 0 deletions src/kaskas/io/providers/analogue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class AnalogueActuator : public Provider {
const std::function<double()> value_f;
const std::function<void(double)> set_value_f;
const std::function<void(double, double, time_ms)> fade_to_f;
const std::function<void(double, time_ms)> creep_to_f;
};
AnalogueActuator(const FunctionMap& map) : _map(map){};

Expand All @@ -47,6 +48,7 @@ class AnalogueActuator : public Provider {
void fade_to(double setpoint, double increment = 0.1, time_ms increment_interval = time_ms(150)) {
_map.fade_to_f(setpoint, increment, increment_interval);
}
void creep_to(double setpoint, time_ms travel_time) { _map.creep_to_f(setpoint, travel_time); }

std::unique_ptr<prompt::RPCRecipe> rpc_recipe(const std::string_view& recipe_name, const std::string_view& root) {
return {};
Expand Down
6 changes: 3 additions & 3 deletions src/kaskas/kaskas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ using spn::structure::Vector;
class KasKas {
public:
struct Config {
EventSystem::Config esc_cfg;
EventSystem::Config es_cfg;
uint16_t component_cap = 1;

std::optional<Prompt::Config> prompt_cfg;
};

public:
explicit KasKas(std::shared_ptr<io::HardwareStack> hws, Config& cfg)
: _cfg(cfg), _evsys({cfg.esc_cfg}), _hws(std::move(hws)),
: _cfg(cfg), _evsys({cfg.es_cfg}), _hws(std::move(hws)),
_components(std::vector<std::unique_ptr<Component>>()) {
if (_cfg.prompt_cfg) {
using prompt::SerialDatalink;
Expand Down Expand Up @@ -107,7 +107,7 @@ class KasKas {
if (_cfg.prompt_cfg) {
if (auto recipe = component->rpc_recipe()) {
assert(recipe != nullptr);
DBGF("Hotloading component rpc recipes!");
// DBGF("Hotloading component rpc recipes!");
hotload_rpc_recipe(std::move(recipe));
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/kaskas/prompt/cookbook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ class RPCCookbook {
}
// get rid off old recipe
// r.reset();
} else {
DBGF("wtf: cmd %s, name :%s", std::string(r->command()).c_str(), std::string(name).c_str())
}
}
new_cookbook.emplace_back(std::move(rf.extract_recipe()));
Expand Down
8 changes: 4 additions & 4 deletions src/kaskas/prompt/datalink.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class Datalink {
}
const auto s1 = _unfinished->length;

DBGF("unfinished length : %i", _unfinished->length);
// DBGF("unfinished length : %i", _unfinished->length);

assert(_unfinished->capacity > 0);
_unfinished->length +=
Expand Down Expand Up @@ -108,7 +108,7 @@ class Datalink {
newbuffer->reset();

// copy in the line
DBGF("prompt: copying");
// DBGF("prompt: copying");
newbuffer->length = nl + 1;
assert(_unfinished->length >= newbuffer->length);
strlcpy(newbuffer->raw, _unfinished->raw, newbuffer->length);
Expand All @@ -118,7 +118,7 @@ class Datalink {
// delay(100);

// move forward the remaining of the unfinished line
DBGF("prompt: memmoving");
// DBGF("prompt: memmoving");
// Serial.print("unfinished length before MEMMOVE: ");
// Serial.println(_unfinished->length);
// Serial.print("newbuffer length before MEMMOVE: ");
Expand All @@ -127,7 +127,7 @@ class Datalink {
_unfinished->length -= newbuffer->length;
memmove(_unfinished->raw, _unfinished->raw + newbuffer->length, _unfinished->length);
_unfinished->raw[_unfinished->length] = '\0';
DBGF("Remaining in buffer: {%s} (removed: {%s})", _unfinished->raw, newbuffer->raw);
// DBGF("Remaining in buffer: {%s} (removed: {%s})", _unfinished->raw, newbuffer->raw);
// Serial.println("memmoved line");
// Serial.print("unfinished length when leaving: ");
// Serial.println(_unfinished->length);
Expand Down
2 changes: 1 addition & 1 deletion src/kaskas/prompt/message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class Message {
// m._value = std::string_view(buffer->raw + operant_idx + 1, buffer->length - operant_idx - 1 -
// newline_at_end);
m._buffer = std::move(buffer);
DBGF("from buffer: {%s}", m.as_string().c_str());
// DBGF("from buffer: {%s}", m.as_string().c_str());
return std::move(m);
}

Expand Down
8 changes: 3 additions & 5 deletions src/kaskas/prompt/prompt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ class Prompt {
void update() {
assert(_dl);

if (const auto message = _dl->receive_message()) {
DBGF("update: {%s}", message->as_string().c_str());
while (const auto message = _dl->receive_message()) {
// DBGF("update: {%s}", message->as_string().c_str());
// Serial.println("------------------------");
// Serial.print("Received back: {");
// const auto s = message->as_string();
Expand All @@ -77,10 +77,8 @@ class Prompt {
_dl->send_message(*reply);
} else {
// Serial.print("Invalid message");
DBGF("Couldnt build rpc from message");
DBGF("Couldnt build rpc from message: {%s}", message->as_string().c_str());
}
} else {
// DBGF("No message at this time");
}
}

Expand Down
Loading

0 comments on commit 8edb0cb

Please sign in to comment.