Skip to content

Commit

Permalink
various: mass refactoring
Browse files Browse the repository at this point in the history
- DS18B20/DS3231/SHT31: Added sensor lock out with a default of 5 updates
  before the sensor trips an exception. An exception because both the
  temperature and clock are critical to safe operation.
- various: Where applicable, changed assertions to expectations with returns
  or exceptions
- various: Where applicable, changed DBG to LOG/WARN statements and viceversa
- subsys/climatecontrol/API: Added setpoint as argument to `heaterAutotune`
  and `ventilationAutotune`
- analogue output: Added `creep_stop()` which stops the hardware stack backend
  from trying to ‘creep’ towards the target.
- events: changed names of lights events to match names in other parts of code
  • Loading branch information
s-t-a-n committed Oct 12, 2024
1 parent 8c0e57b commit f3443d2
Show file tree
Hide file tree
Showing 22 changed files with 234 additions and 163 deletions.
12 changes: 6 additions & 6 deletions src/kaskas/events.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ enum class Events {
WaterInjectStart,
WaterInjectFollowUp,
WaterInjectStop,
LightRedBlueSpectrumCycleCheck,
LightRedBlueSpectrumTurnOn,
LightRedBlueSpectrumTurnOff,
LightFullSpectrumCycleCheck,
LightFullSpectrumTurnOn,
LightFullSpectrumTurnOff,
LightVioletSpectrumCycleCheck,
LightVioletSpectrumTurnOn,
LightVioletSpectrumTurnOff,
LightBroadSpectrumCycleCheck,
LightBroadSpectrumTurnOn,
LightBroadSpectrumTurnOff,
DAQWarmedUp,
DAQTainted,
Size
Expand Down
4 changes: 2 additions & 2 deletions src/kaskas/io/controllers/heater.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@ class Heater {
const auto adjusted_setpoint = std::clamp(excess > 0.0 ? setpoint - feedback : setpoint, 0.0, 1.0);

if (adjusted_setpoint != setpoint) {
DBG("Heater: T %.2f C is above limit of T %.2f C, clamping response from %.2f to %.2f", surface_temperature,
_cfg.max_heater_setpoint, setpoint, adjusted_setpoint);
WARN("Heater: T %.2f C is above limit of T %.2f C, clamping response from %.2f to %.2f",
surface_temperature, _cfg.max_heater_setpoint, setpoint, adjusted_setpoint);
}
return adjusted_setpoint;
}
Expand Down
1 change: 1 addition & 0 deletions src/kaskas/io/hardware_stack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class HardwareStack : public VirtualStack {

/// Safely shutdown all peripherals
void safe_shutdown(bool critical = false) {
DBG("Hardware stack: shutting down all components");
for (auto& p : _peripherals) {
if (p) {
HAL::delay(time_ms(100));
Expand Down
6 changes: 3 additions & 3 deletions src/kaskas/io/peripheral.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ class Peripheral {
bool needs_update() { return is_updateable() && _timer->expired(); }
bool is_updateable() const { return _timer != std::nullopt; }
time_ms update_interval() const {
spn_assert(is_updateable());
spn_expect(is_updateable());
return is_updateable() ? _timer->interval() : time_ms(0);
}
time_ms time_until_next_update() const {
spn_assert(is_updateable());
spn_expect(is_updateable());
return is_updateable() ? _timer->time_until_next() : time_ms(0);
}

Expand All @@ -46,7 +46,7 @@ class FilteredPeripheral : public Peripheral {
: Peripheral(sampling_interval), _fs(number_of_filters) {}

void attach_filter(std::unique_ptr<Filter> filter) {
spn_assert(_fs.filter_slots() > 0);
spn_expect(_fs.filter_slots_occupied() < _fs.filter_slots());
_fs.attach_filter(std::move(filter));
}

Expand Down
12 changes: 9 additions & 3 deletions src/kaskas/io/peripherals/DS18B20_Temp_Probe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class DS18B20TempProbe : public FilteredPeripheral {
struct Config {
uint8_t pin;
time_ms sampling_interval = time_s(1);
int sensor_lockout_threshold = 5;
};

public:
Expand All @@ -40,15 +41,19 @@ class DS18B20TempProbe : public FilteredPeripheral {
update();
_ds18b20.setWaitForConversion(false);

DBG("DS18B20TempProbe initialized. Temperature: %.2f °C", temperature());
LOG("DS18B20TempProbe initialized. Temperature: %.2f °C", temperature());
}

void update() override {
if (_ds18b20.isConversionComplete()) {
_fs.new_sample(_ds18b20.getTempC(_ds18b20_address));
_ds18b20.requestTemperatures();
} else
DBG("DS18B20: update called before conversion is complete");
_sensor_lockout = 0;
} else {
WARN("DS18B20: update called before conversion completed");
if (++_sensor_lockout > _cfg.sensor_lockout_threshold)
spn::throw_exception(spn::runtime_exception("DS18B20: Maximum number of failed updates reached"));
}
}

void safe_shutdown(bool critical) override {}
Expand All @@ -65,5 +70,6 @@ class DS18B20TempProbe : public FilteredPeripheral {
OneWire _onewire;
DallasTemperature _ds18b20;
DeviceAddress _ds18b20_address{};
int _sensor_lockout = 0;
};
} // namespace kaskas::io
18 changes: 14 additions & 4 deletions src/kaskas/io/peripherals/DS3231_RTC_EEPROM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class DS3231Clock final : public Peripheral {
public:
struct Config {
time_ms update_interval = time_s(1);
int sensor_lockout_threshold = 5;
};

explicit DS3231Clock(const Config&& cfg) : Peripheral(cfg.update_interval), _cfg(cfg) {}
Expand All @@ -33,15 +34,23 @@ class DS3231Clock final : public Peripheral {

if (is_ready()) {
const auto n = now();
DBG("DS3231Clock initialized. The reported time is %u:%u @ %u:%u:%u", n.getHour(), n.getMinute(),
LOG("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_exception("DS3231Clock failed to initialize. Maybe set the time?"));
spn::throw_exception(::spn::assertion_exception("DS3231Clock failed to initialize. Maybe set the time?"));
}
}

void update() override {
spn_assert(is_ready());
if (!is_ready()) {
WARN("DS3231: failed to update.");
if (++_sensor_lockout > _cfg.sensor_lockout_threshold)
spn::throw_exception(spn::runtime_exception("DS3231: Maximum number of failed updates reached"));
return;
} else {
_sensor_lockout = 0;
}

_now = DateTime(DS3231::RTClib::now().getUnixTime());
}
void safe_shutdown(bool critical) override {}
Expand Down Expand Up @@ -69,8 +78,9 @@ class DS3231Clock final : public Peripheral {
const Config _cfg;

DS3231::DS3231 _ds3231;

DateTime _now;

int _sensor_lockout = 0;
};

} // namespace kaskas::io::clock
29 changes: 22 additions & 7 deletions src/kaskas/io/peripherals/SHT31_TempHumidityProbe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class SHT31TempHumidityProbe : public Peripheral {
struct Config {
uint8_t i2c_address = SHT_DEFAULT_ADDRESS;
time_ms sampling_interval = time_s(1);
int sensor_lockout_threshold = 5;
};

public:
Expand All @@ -37,25 +38,37 @@ class SHT31TempHumidityProbe : public Peripheral {
Wire.begin();
Wire.setClock(100000); // per example

_sht31.begin();
const bool initial_contact = _sht31.begin();

if (!_sht31.read(SHT31_USE_CRC) || !is_ready()) {
WARN("SHT31: Probe reports errorcode: %04X with status: %04X", _sht31.getError(), _sht31.readStatus())
HAL::delay(time_ms(20)); // give the probe some time to initialize

if (!initial_contact || !_sht31.read(SHT31_USE_CRC) || !is_ready()) {
ERR("SHT31: Probe failed to initialize with errorcode: %04X and status: %04X", _sht31.getError(),
_sht31.readStatus())
spn::throw_exception(spn::assertion_exception("SHT31TempHumidityProbe could not be initialized"));
}
DBG("SHT31TempHumidityProbe initialized. Humidity: %.2f %%, temperature: %.2f °C", read_humidity(),
read_temperature());

_sht31.heatOff(); // make sure that probe's internal heating element is turned off
spn_assert(!_sht31.isHeaterOn());
if (_sht31.isHeaterOn())
spn::throw_exception(spn::runtime_exception("SHT31: internal heater failed to turn off"));

LOG("SHT31TempHumidityProbe initialized. Humidity: %.2f %%, temperature: %.2f °C", _sht31.getHumidity(),
_sht31.getTemperature());

_sht31.requestData();
}

void update() override {
bool has_data = _sht31.dataReady() && _sht31.readData(SHT31_USE_CRC);
if (!has_data) {
WARN("SHT31: request was not ready in time.");
if (!is_ready())
if (!is_ready()) {
WARN("SHT31: Probe reports errorcode: %04X with status: %04X", _sht31.getError(), _sht31.readStatus());
if (++_sensor_lockout > _cfg.sensor_lockout_threshold)
spn::throw_exception(spn::runtime_exception("SHT31: Maximum number of failed updates reached"));
} else {
_sensor_lockout = 0;
}
}
if (!_sht31.requestData()) {
WARN("SHT31: couldnt request data. Probe reports errorcode: %04X with status: %04X", _sht31.getError(),
Expand All @@ -70,6 +83,7 @@ class SHT31TempHumidityProbe : public Peripheral {

void safe_shutdown(bool critical) override {
_sht31.heatOff(); // make sure that heater is off
HAL::delay(time_ms(15)); // give I2C time to write data
}

double read_temperature() {
Expand Down Expand Up @@ -98,5 +112,6 @@ class SHT31TempHumidityProbe : public Peripheral {

spn::filter::Stack<double> _temperature_fs;
spn::filter::Stack<double> _humidity_fs;
int _sensor_lockout = 0;
};
} // namespace kaskas::io
3 changes: 2 additions & 1 deletion src/kaskas/io/peripherals/analogue_input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class AnalogueInputPeripheral : public FilteredPeripheral {
AnalogueInput::Config input_cfg;
time_ms sampling_interval = time_s(1);
size_t number_of_filters = 0;
const char* id = nullptr;
};

public:
Expand All @@ -27,7 +28,7 @@ class AnalogueInputPeripheral : public FilteredPeripheral {
void initialize() override {
_input.initialize();
update();
DBG("Analog Sensor initialized. Raw value: %.2f, Filtered value: %.2f", raw_value(), value());
LOG("Analog sensor {%s} initialized. Raw value: %.2f, Filtered value: %.2f", _cfg.id, raw_value(), value());
}

void update() override { _fs.new_sample(raw_value()); }
Expand Down
12 changes: 8 additions & 4 deletions src/kaskas/io/peripherals/analogue_output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class AnalogueOutputPeripheral : public HAL::AnalogueOutput, public Peripheral {
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;
creep_stop();
AnalogueOutput::set_value(_creep_target_value);
}
return;
Expand All @@ -34,7 +34,7 @@ class AnalogueOutputPeripheral : public HAL::AnalogueOutput, public Peripheral {

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

Expand All @@ -46,10 +46,11 @@ class AnalogueOutputPeripheral : public HAL::AnalogueOutput, public Peripheral {
[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); }}};
.creep_to_f = [this](double setpoint, time_ms travel_time) { this->creep_to(setpoint, travel_time); },
.creep_stop_f = [this]() { this->creep_stop(); }}};
}

/// set a target value and let the updater approach the value with a single increment per sample
/// Set a creeping 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;

Expand All @@ -59,6 +60,9 @@ class AnalogueOutputPeripheral : public HAL::AnalogueOutput, public Peripheral {
_creep_target_increment = (setpoint - value()) / (travel_time / update_interval()).raw<double>();
}

/// Stop creeping to target.
void creep_stop() { _is_creeping = false; }

private:
bool _is_creeping = false;
double _creep_target_value = 0;
Expand Down
3 changes: 2 additions & 1 deletion src/kaskas/io/peripherals/digital_input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class DigitalInputPeripheral : public Peripheral {
struct Config {
DigitalInput::Config input_cfg;
time_ms sampling_interval = time_s(1);
const char* id = nullptr;
};

public:
Expand All @@ -27,7 +28,7 @@ class DigitalInputPeripheral : public Peripheral {
void initialize() override {
_input.initialize();
update();
DBG("Analog Sensor initialized. Value: %.2f V", value());
DBG("Digital sensor {%s} initialized. Value: %.2f V", _cfg.id, value());
}

void update() override { _value = _input.state() ? LogicalState::ON : LogicalState::OFF; }
Expand Down
8 changes: 4 additions & 4 deletions src/kaskas/io/peripherals/relay.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ class Relay : public Peripheral {
if (_cfg.throw_on_early_flip) {
spn::throw_exception(spn::runtime_exception("Tried to flip relay within backoff threshold"));
} else {
DBG("--------------------------------------------------------------------------");
DBG("Tried to flip relay within backoff threshold: %ims since last flip",
time_ms(time_since_last_flip).printable());
DBG("--------------------------------------------------------------------------");
WARN("--------------------------------------------------------------------------");
WARN("Tried to flip relay within backoff threshold: %ims since last flip",
time_ms(time_since_last_flip).printable());
WARN("--------------------------------------------------------------------------");
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/kaskas/io/providers/analogue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class AnalogueActuator : public Provider {
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;
const std::function<void()> creep_stop_f;
};

AnalogueActuator(const FunctionMap& map) : _map(map){};
Expand All @@ -50,7 +51,7 @@ class AnalogueActuator : public Provider {
}

void creep_to(double setpoint, time_ms travel_time) { _map.creep_to_f(setpoint, travel_time); }

void creep_stop() { _map.creep_stop_f(); }
std::unique_ptr<prompt::RPCRecipe> rpc_recipe(const std::string_view& recipe_name, const std::string_view& root) {
return {};
}
Expand Down
1 change: 1 addition & 0 deletions src/kaskas/io/providers/pump.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct Pump {
void initialize() { _interrupt.initialize(); }

void safe_shutdown() {
DBG("Pump: Shutting down");
HAL::delay(time_ms(100));
stop_injection();
}
Expand Down
6 changes: 3 additions & 3 deletions src/kaskas/kaskas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ class KasKas {
}

_prompt->initialize();
LOG("Initialized prompt");
}

_evsys.trigger(_evsys.event(Events::WakeUp, time_s(0), Event::Data()));
LOG("Kaskas: Startup complete");
return 0;
}

Expand Down Expand Up @@ -119,7 +119,7 @@ class KasKas {
private:
void platform_sanity_checks() {
if (HAL::free_memory() < 1024) { // it is good to have no leaks, it is better to be safe
spn::throw_exception(spn::runtime_exception("Memory is below a kilobyte. Halting."));
spn::throw_exception(spn::runtime_exception("Less than a kilobyte of free memory. Halting."));
}
}

Expand All @@ -133,7 +133,7 @@ class KasKas {
for (auto& sf : _kk._components) {
sf->safe_shutdown(Component::State::CRITICAL);
}
HAL::halt("KasKas shutdown after exception");
HAL::halt("KasKas: halting after exception");
}

private:
Expand Down
1 change: 1 addition & 0 deletions src/kaskas/prompt/prompt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Prompt {
public:
/// Initialize the prompt
void initialize() {
LOG("Prompt initialized")
spn_assert(_dl);
_dl->initialize();
}
Expand Down
Loading

0 comments on commit f3443d2

Please sign in to comment.