From d83b38ee904020fecb959dd7366cdb23cfaec891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 12:12:32 +0100 Subject: [PATCH 01/10] Separate factory and peripheral types --- src/devices/Peripheral.hpp | 40 +++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/devices/Peripheral.hpp b/src/devices/Peripheral.hpp index 9b03fbfb..c9ea79cf 100644 --- a/src/devices/Peripheral.hpp +++ b/src/devices/Peripheral.hpp @@ -93,21 +93,28 @@ class PeripheralCreationException class PeripheralFactoryBase { public: - PeripheralFactoryBase(const String& type) - : type(type) { + PeripheralFactoryBase(const String& factoryType, const String& peripheralType) + : factoryType(factoryType) + , peripheralType(peripheralType) { } virtual unique_ptr createPeripheral(const String& name, const String& jsonConfig, shared_ptr mqttRoot) = 0; - const String type; + const String factoryType; + const String peripheralType; }; template class PeripheralFactory : public PeripheralFactoryBase { public: + // By default use the factory type as the peripheral type // TODO Use TDeviceConfigArgs&& instead PeripheralFactory(const String& type, TDeviceConfigArgs... deviceConfigArgs) - : PeripheralFactoryBase(type) + : PeripheralFactory(type, type, deviceConfigArgs...) { + } + + PeripheralFactory(const String& type, const String& peripheralType, TDeviceConfigArgs... deviceConfigArgs) + : PeripheralFactoryBase(type, peripheralType) , deviceConfigArgs(std::forward(deviceConfigArgs)...) { } @@ -153,8 +160,8 @@ class PeripheralManager void registerFactory(PeripheralFactoryBase& factory) { Log.traceln("Registering peripheral factory: %s", - factory.type.c_str()); - factories.insert(std::make_pair(factory.type, std::reference_wrapper(factory))); + factory.factoryType.c_str()); + factories.insert(std::make_pair(factory.factoryType, std::reference_wrapper(factory))); } void createPeripherals() { @@ -165,13 +172,13 @@ class PeripheralManager PeripheralDeviceConfiguration deviceConfig; deviceConfig.loadFromString(perpheralConfigJsonAsString.get()); const String& name = deviceConfig.name.get(); - const String& type = deviceConfig.type.get(); + const String& factory = deviceConfig.type.get(); try { - unique_ptr peripheral = createPeripheral(name, type, deviceConfig.params.get().get()); + unique_ptr peripheral = createPeripheral(name, factory, deviceConfig.params.get().get()); peripherals.push_back(move(peripheral)); } catch (const PeripheralCreationException& e) { - Log.errorln("Failed to create peripheral: %s of type %s because %s", - name.c_str(), type.c_str(), e.reason.c_str()); + Log.errorln("Failed to create peripheral '%s' with factory '%s' because %s", + name.c_str(), factory.c_str(), e.reason.c_str()); } } } @@ -190,14 +197,15 @@ class PeripheralManager Property params { this, "params" }; }; - unique_ptr createPeripheral(const String& name, const String& type, const String& configJson) { - Log.traceln("Creating peripheral: %s of type %s", - name.c_str(), type.c_str()); - auto it = factories.find(type); + unique_ptr createPeripheral(const String& name, const String& factoryType, const String& configJson) { + Log.traceln("Creating peripheral '%s' with factory '%s'", + name.c_str(), factoryType.c_str()); + auto it = factories.find(factoryType); if (it == factories.end()) { - throw PeripheralCreationException(name, "No factory found for peripheral type '" + type + "'"); + throw PeripheralCreationException(name, "Factory not found: '" + factoryType + "'"); } - shared_ptr mqttRoot = mqttDeviceRoot->forSuffix("peripherals/" + type + "/" + name); + const String& peripheralType = it->second.get().peripheralType; + shared_ptr mqttRoot = mqttDeviceRoot->forSuffix("peripherals/" + peripheralType + "/" + name); PeripheralFactoryBase& factory = it->second.get(); return factory.createPeripheral(name, configJson, mqttRoot); } From 2fbdde5c40e7703a10bb3036b701684442453dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 13:43:03 +0100 Subject: [PATCH 02/10] Initialize built-in peripherals of devices --- src/devices/Device.hpp | 16 +++++++++-- src/devices/DeviceDefinition.hpp | 9 +++++++ src/devices/Peripheral.hpp | 46 +++++++++++++++++--------------- 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/devices/Device.hpp b/src/devices/Device.hpp index da7fc9de..79281717 100644 --- a/src/devices/Device.hpp +++ b/src/devices/Device.hpp @@ -241,7 +241,19 @@ class Device : ConsoleProvider { // We want RTC to be in sync before we start setting up peripherals kernel.getRtcInSyncState().awaitSet(); - peripheralManager.createPeripherals(); + auto builtInPeripheralsCofig = deviceDefinition.getBuiltInPeripherals(); + Log.traceln("Loading configuration for %d built-in peripherals", + builtInPeripheralsCofig.size()); + for (auto& perpheralConfig : builtInPeripheralsCofig) { + peripheralManager.createPeripheral(perpheralConfig); + } + + auto& peripheralsConfig = deviceConfig.peripherals.get(); + Log.infoln("Loading configuration for %d peripherals", + peripheralsConfig.size()); + for (auto& perpheralConfig : peripheralsConfig) { + peripheralManager.createPeripheral(perpheralConfig.get()); + } kernel.getKernelReadyState().awaitSet(); @@ -277,7 +289,7 @@ class Device : ConsoleProvider { TDeviceConfiguration& deviceConfig = deviceDefinition.config; Kernel kernel { deviceConfig, deviceDefinition.statusLed }; shared_ptr mqttDeviceRoot = kernel.mqtt.forRoot("devices/ugly-duckling/" + deviceConfig.instance.get()); - PeripheralManager peripheralManager { mqttDeviceRoot, deviceConfig.peripherals }; + PeripheralManager peripheralManager { mqttDeviceRoot }; TelemetryCollector deviceTelemetryCollector; MqttTelemetryPublisher deviceTelemetryPublisher { mqttDeviceRoot, deviceTelemetryCollector }; diff --git a/src/devices/DeviceDefinition.hpp b/src/devices/DeviceDefinition.hpp index bfcdc64a..edb5662a 100644 --- a/src/devices/DeviceDefinition.hpp +++ b/src/devices/DeviceDefinition.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -50,6 +52,13 @@ class DeviceDefinition { virtual void registerPeripheralFactories(PeripheralManager& peripheralManager) { } + /** + * @brief Returns zero or more JSON configurations for any built-in peripheral of the device. + */ + virtual std::list getBuiltInPeripherals() { + return {}; + } + public: LedDriver statusLed; PwmManager pwm; diff --git a/src/devices/Peripheral.hpp b/src/devices/Peripheral.hpp index c9ea79cf..74ebeb3a 100644 --- a/src/devices/Peripheral.hpp +++ b/src/devices/Peripheral.hpp @@ -152,10 +152,8 @@ class PeripheralManager : public TelemetryPublisher { public: PeripheralManager( - const shared_ptr mqttDeviceRoot, - ArrayProperty& peripheralsConfig) - : mqttDeviceRoot(mqttDeviceRoot) - , peripheralsConfig(peripheralsConfig) { + const shared_ptr mqttDeviceRoot) + : mqttDeviceRoot(mqttDeviceRoot) { } void registerFactory(PeripheralFactoryBase& factory) { @@ -164,22 +162,29 @@ class PeripheralManager factories.insert(std::make_pair(factory.factoryType, std::reference_wrapper(factory))); } - void createPeripherals() { - Log.infoln("Loading configuration for %d peripherals", - peripheralsConfig.get().size()); - - for (auto& perpheralConfigJsonAsString : peripheralsConfig.get()) { - PeripheralDeviceConfiguration deviceConfig; - deviceConfig.loadFromString(perpheralConfigJsonAsString.get()); - const String& name = deviceConfig.name.get(); - const String& factory = deviceConfig.type.get(); - try { - unique_ptr peripheral = createPeripheral(name, factory, deviceConfig.params.get().get()); - peripherals.push_back(move(peripheral)); - } catch (const PeripheralCreationException& e) { - Log.errorln("Failed to create peripheral '%s' with factory '%s' because %s", - name.c_str(), factory.c_str(), e.reason.c_str()); - } + void createPeripheral(const String& peripheralConfig) { + Log.info("Creating peripheral with config: %s", + peripheralConfig.c_str()); + PeripheralDeviceConfiguration deviceConfig; + try { + deviceConfig.loadFromString(peripheralConfig); + } catch (const std::exception& e) { + Log.errorln("Failed to parse peripheral config because %s:\n%s", + e.what(), peripheralConfig.c_str()); + return; + } + + const String& name = deviceConfig.name.get(); + const String& factory = deviceConfig.type.get(); + try { + unique_ptr peripheral = createPeripheral(name, factory, deviceConfig.params.get().get()); + peripherals.push_back(move(peripheral)); + } catch (const PeripheralCreationException& e) { + Log.errorln("Failed to create peripheral '%s' with factory '%s' because %s", + name.c_str(), factory.c_str(), e.reason.c_str()); + } catch (const std::exception& e) { + Log.errorln("Failed to create peripheral '%s' with factory '%s' because %s", + name.c_str(), factory.c_str(), e.what()); } } @@ -211,7 +216,6 @@ class PeripheralManager } const shared_ptr mqttDeviceRoot; - ArrayProperty& peripheralsConfig; // TODO Use an unordered_map? std::map> factories; From 6b7076481a16bca02713894df8469e3016f560d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 18:13:49 +0100 Subject: [PATCH 03/10] Remove unnecessary commented out code --- src/devices/Device.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/devices/Device.hpp b/src/devices/Device.hpp index 79281717..8aab4919 100644 --- a/src/devices/Device.hpp +++ b/src/devices/Device.hpp @@ -216,8 +216,6 @@ class Device : ConsoleProvider { deviceDefinition.registerPeripheralFactories(peripheralManager); - // deviceTelemetryCollector.registerProvider("peripherals", peripheralManager); - mqttDeviceRoot->registerCommand(echoCommand); mqttDeviceRoot->registerCommand(pingCommand); // TODO Add reset-wifi command From ba0ee61c23143933068bd7ef1beb64d1862bc684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 18:14:42 +0100 Subject: [PATCH 04/10] Polish exceptions --- src/peripherals/flow_control/FlowControl.hpp | 4 ++-- src/peripherals/valve/Valve.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/peripherals/flow_control/FlowControl.hpp b/src/peripherals/flow_control/FlowControl.hpp index 0410986a..581bd5c5 100644 --- a/src/peripherals/flow_control/FlowControl.hpp +++ b/src/peripherals/flow_control/FlowControl.hpp @@ -79,7 +79,7 @@ class FlowControlFactory valveConfig.switchDuration.get(), valveConfig.duty.get() / 100.0); } catch (const std::exception& e) { - throw PeripheralCreationException(name, "Failed to create strategy: " + String(e.what())); + throw PeripheralCreationException(name, "failed to create strategy: " + String(e.what())); } return make_unique( name, @@ -99,7 +99,7 @@ class FlowControlFactory return motor.get(); } } - throw PeripheralCreationException(name, "Failed to find motor: " + motorName); + throw PeripheralCreationException(name, "failed to find motor: " + motorName); } private: diff --git a/src/peripherals/valve/Valve.hpp b/src/peripherals/valve/Valve.hpp index cb9e4443..5d8ffc24 100644 --- a/src/peripherals/valve/Valve.hpp +++ b/src/peripherals/valve/Valve.hpp @@ -66,7 +66,7 @@ class ValveFactory deviceConfig.switchDuration.get(), deviceConfig.duty.get() / 100.0); } catch (const std::exception& e) { - throw PeripheralCreationException(name, "Failed to create strategy: " + String(e.what())); + throw PeripheralCreationException(name, "failed to create strategy: " + String(e.what())); } return make_unique(name, targetMotor, *strategy, mqttRoot); } @@ -77,7 +77,7 @@ class ValveFactory return motor.get(); } } - throw PeripheralCreationException(name, "Failed to find motor: " + motorName); + throw PeripheralCreationException(name, "failed to find motor: " + motorName); } private: From a523eb5e2d3e9d2439d754c1c492ac4448ea25e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 18:15:32 +0100 Subject: [PATCH 05/10] Add some missing includes --- src/devices/DeviceDefinition.hpp | 3 +++ src/devices/Peripheral.hpp | 1 + src/devices/UglyDucklingMk4.hpp | 2 ++ src/peripherals/valve/ValveConfig.hpp | 1 + 4 files changed, 7 insertions(+) diff --git a/src/devices/DeviceDefinition.hpp b/src/devices/DeviceDefinition.hpp index edb5662a..4784323f 100644 --- a/src/devices/DeviceDefinition.hpp +++ b/src/devices/DeviceDefinition.hpp @@ -8,10 +8,13 @@ #include #include +#include + #include using namespace farmhub::kernel; using namespace farmhub::kernel::drivers; +using namespace farmhub::peripherals::environment; namespace farmhub::devices { diff --git a/src/devices/Peripheral.hpp b/src/devices/Peripheral.hpp index 74ebeb3a..71724146 100644 --- a/src/devices/Peripheral.hpp +++ b/src/devices/Peripheral.hpp @@ -15,6 +15,7 @@ using std::shared_ptr; using std::unique_ptr; using namespace farmhub::kernel; +using namespace farmhub::kernel::drivers; namespace farmhub::devices { diff --git a/src/devices/UglyDucklingMk4.hpp b/src/devices/UglyDucklingMk4.hpp index 6ab5f3d5..84cefd45 100644 --- a/src/devices/UglyDucklingMk4.hpp +++ b/src/devices/UglyDucklingMk4.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include diff --git a/src/peripherals/valve/ValveConfig.hpp b/src/peripherals/valve/ValveConfig.hpp index 363e6e77..b0c3e01a 100644 --- a/src/peripherals/valve/ValveConfig.hpp +++ b/src/peripherals/valve/ValveConfig.hpp @@ -4,6 +4,7 @@ #include #include +#include #include using namespace farmhub::kernel; From a751ed29504c3be0bdbc3294bfa4006d4140526d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 18:17:50 +0100 Subject: [PATCH 06/10] Allow registering global peripheral factories --- src/devices/DeviceDefinition.hpp | 4 ++++ src/devices/UglyDucklingMk4.hpp | 2 +- src/devices/UglyDucklingMk5.hpp | 2 +- src/devices/UglyDucklingMk6.hpp | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/devices/DeviceDefinition.hpp b/src/devices/DeviceDefinition.hpp index 4784323f..baff4712 100644 --- a/src/devices/DeviceDefinition.hpp +++ b/src/devices/DeviceDefinition.hpp @@ -53,6 +53,10 @@ class DeviceDefinition { } virtual void registerPeripheralFactories(PeripheralManager& peripheralManager) { + registerDeviceSpecificPeripheralFactories(peripheralManager); + } + + virtual void registerDeviceSpecificPeripheralFactories(PeripheralManager& peripheralManager) { } /** diff --git a/src/devices/UglyDucklingMk4.hpp b/src/devices/UglyDucklingMk4.hpp index 84cefd45..322e7fd1 100644 --- a/src/devices/UglyDucklingMk4.hpp +++ b/src/devices/UglyDucklingMk4.hpp @@ -38,7 +38,7 @@ class UglyDucklingMk4 : public DeviceDefinition { GPIO_NUM_26) { } - void registerPeripheralFactories(PeripheralManager& peripheralManager) override { + void registerDeviceSpecificPeripheralFactories(PeripheralManager& peripheralManager) override { peripheralManager.registerFactory(valveFactory); peripheralManager.registerFactory(flowMeterFactory); peripheralManager.registerFactory(flowControlFactory); diff --git a/src/devices/UglyDucklingMk5.hpp b/src/devices/UglyDucklingMk5.hpp index 675a5628..693a6700 100644 --- a/src/devices/UglyDucklingMk5.hpp +++ b/src/devices/UglyDucklingMk5.hpp @@ -40,7 +40,7 @@ class UglyDucklingMk5 : public BatteryPoweredDeviceDefinition { GPIO_NUM_1, 2.4848) { } - void registerPeripheralFactories(PeripheralManager& peripheralManager) override { + void registerDeviceSpecificPeripheralFactories(PeripheralManager& peripheralManager) override { peripheralManager.registerFactory(valveFactory); peripheralManager.registerFactory(flowMeterFactory); peripheralManager.registerFactory(flowControlFactory); diff --git a/src/devices/UglyDucklingMk6.hpp b/src/devices/UglyDucklingMk6.hpp index 6ce0d31f..bffb1f0c 100644 --- a/src/devices/UglyDucklingMk6.hpp +++ b/src/devices/UglyDucklingMk6.hpp @@ -39,7 +39,7 @@ class UglyDucklingMk6 : public BatteryPoweredDeviceDefinition { GPIO_NUM_1, 1.2424) { } - void registerPeripheralFactories(PeripheralManager& peripheralManager) override { + void registerDeviceSpecificPeripheralFactories(PeripheralManager& peripheralManager) override { peripheralManager.registerFactory(valveFactory); peripheralManager.registerFactory(flowMeterFactory); peripheralManager.registerFactory(flowControlFactory); From 20d1dcc56a703547a91ca8df414c19fda37b56c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 18:18:40 +0100 Subject: [PATCH 07/10] Polish exception handling during peripheral creation --- src/devices/Peripheral.hpp | 20 +++++++++----------- src/kernel/Configuration.hpp | 25 +++++++++++++++++++++---- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/devices/Peripheral.hpp b/src/devices/Peripheral.hpp index 71724146..d26bb658 100644 --- a/src/devices/Peripheral.hpp +++ b/src/devices/Peripheral.hpp @@ -80,16 +80,14 @@ class PeripheralCreationException : public std::exception { public: PeripheralCreationException(const String& name, const String& reason) - : name(name) - , reason(reason) { + : message(String("PeripheralCreationException: Failed to create peripheral '" + name + "' because " + reason)) { } const char* what() const noexcept override { - return String("Failed to create peripheral '" + name + "' because " + reason).c_str(); + return message.c_str(); } - const String name; - const String reason; + const String message; }; class PeripheralFactoryBase { @@ -111,11 +109,11 @@ class PeripheralFactory : public PeripheralFactoryBase { // By default use the factory type as the peripheral type // TODO Use TDeviceConfigArgs&& instead PeripheralFactory(const String& type, TDeviceConfigArgs... deviceConfigArgs) - : PeripheralFactory(type, type, deviceConfigArgs...) { + : PeripheralFactory(type, type, std::forward(deviceConfigArgs)...) { } - PeripheralFactory(const String& type, const String& peripheralType, TDeviceConfigArgs... deviceConfigArgs) - : PeripheralFactoryBase(type, peripheralType) + PeripheralFactory(const String& factoryType, const String& peripheralType, TDeviceConfigArgs... deviceConfigArgs) + : PeripheralFactoryBase(factoryType, peripheralType) , deviceConfigArgs(std::forward(deviceConfigArgs)...) { } @@ -180,12 +178,12 @@ class PeripheralManager try { unique_ptr peripheral = createPeripheral(name, factory, deviceConfig.params.get().get()); peripherals.push_back(move(peripheral)); - } catch (const PeripheralCreationException& e) { - Log.errorln("Failed to create peripheral '%s' with factory '%s' because %s", - name.c_str(), factory.c_str(), e.reason.c_str()); } catch (const std::exception& e) { Log.errorln("Failed to create peripheral '%s' with factory '%s' because %s", name.c_str(), factory.c_str(), e.what()); + } catch (...) { + Log.errorln("Failed to create peripheral '%s' with factory '%s' because of an unknown exception", + name.c_str(), factory.c_str()); } } diff --git a/src/kernel/Configuration.hpp b/src/kernel/Configuration.hpp index 7c9555e2..3ded8088 100644 --- a/src/kernel/Configuration.hpp +++ b/src/kernel/Configuration.hpp @@ -13,6 +13,20 @@ using std::reference_wrapper; namespace farmhub::kernel { +class ConfigurationException + : public std::exception { +public: + ConfigurationException(const String& message) + : message("ConfigurationException: " + message) { + } + + const char* what() const noexcept override { + return message.c_str(); + } + + const String message; +}; + class JsonAsString { public: JsonAsString() { @@ -63,8 +77,11 @@ class ConfigurationEntry { void loadFromString(const String& json) { DynamicJsonDocument jsonDocument(docSizeFor(json)); DeserializationError error = deserializeJson(jsonDocument, json); + if (error == DeserializationError::EmptyInput) { + return; + } if (error) { - throw "Cannot parse JSON configuration: " + String(error.c_str()); + throw ConfigurationException("Cannot parse JSON configuration: " + String(error.c_str()) + json); } load(jsonDocument.as()); } @@ -273,21 +290,21 @@ class ConfigurationFile { } else { File file = fs.open(path, FILE_READ); if (!file) { - throw "Cannot open config file " + path; + throw ConfigurationException("Cannot open config file " + path); } DynamicJsonDocument json(docSizeFor(file)); DeserializationError error = deserializeJson(json, file); file.close(); if (error) { - throw "Cannot open config file " + path + " (" + String(error.c_str()) + ")"; + throw ConfigurationException("Cannot open config file " + path + " (" + String(error.c_str()) + ")"); } update(json.as()); } onUpdate([&fs, path](const JsonObject& json) { File file = fs.open(path, FILE_WRITE); if (!file) { - throw "Cannot open config file " + path; + throw ConfigurationException("Cannot open config file " + path); } serializeJson(json, file); From bd1e8c88eaf733127405de583afe097149c9f02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 18:19:03 +0100 Subject: [PATCH 08/10] Add SHT3x support --- platformio.ini | 2 + src/devices/DeviceDefinition.hpp | 4 ++ src/peripherals/I2CConfig.hpp | 54 ++++++++++++++ src/peripherals/environment/Environment.hpp | 49 +++++++++++++ .../environment/EnvironmentComponent.hpp | 72 +++++++++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 src/peripherals/I2CConfig.hpp create mode 100644 src/peripherals/environment/Environment.hpp create mode 100644 src/peripherals/environment/EnvironmentComponent.hpp diff --git a/platformio.ini b/platformio.ini index aa0ba9aa..ed8ff164 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,6 +49,8 @@ lib_deps = https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2 arduino-libraries/NTPClient@^3.2.1 thijse/ArduinoLog@^1.1.1 + # SHT31 + robtillaart/SHT31@~0.5.0 [debug] build_type = debug diff --git a/src/devices/DeviceDefinition.hpp b/src/devices/DeviceDefinition.hpp index baff4712..03999985 100644 --- a/src/devices/DeviceDefinition.hpp +++ b/src/devices/DeviceDefinition.hpp @@ -53,6 +53,7 @@ class DeviceDefinition { } virtual void registerPeripheralFactories(PeripheralManager& peripheralManager) { + peripheralManager.registerFactory(sht31Factory); registerDeviceSpecificPeripheralFactories(peripheralManager); } @@ -75,6 +76,9 @@ class DeviceDefinition { public: TDeviceConfiguration& config = configFile.config; + +private: + EnvironmentSht31Factory sht31Factory; }; template diff --git a/src/peripherals/I2CConfig.hpp b/src/peripherals/I2CConfig.hpp new file mode 100644 index 00000000..991043de --- /dev/null +++ b/src/peripherals/I2CConfig.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include + +#include + +using namespace std::chrono; +using namespace farmhub::kernel; + +namespace farmhub::peripherals::environment { + +struct I2CConfig { +public: + uint8_t address; + gpio_num_t sda; + gpio_num_t scl; + + String toString() { + return String("I2C address: 0x") + String(address, HEX) + ", SDA: " + String(sda) + ", SCL: " + String(scl); + } +}; + +class I2CDeviceConfig + : public ConfigurationSection { +public: + // I2C address is typically a hexadecimal number, + // but JSON doesn't support 0x notation, so we + // take it as a string instead + Property address { this, "address" }; + Property sda { this, "sda", GPIO_NUM_NC }; + Property scl { this, "scl", GPIO_NUM_NC }; + + I2CConfig parse() const { + return parse(-1, GPIO_NUM_NC, GPIO_NUM_NC); + } + + I2CConfig parse(uint8_t defaultAddress, gpio_num_t defaultSda, gpio_num_t defaultScl) const { + return { + address.get().isEmpty() + ? defaultAddress + : (uint8_t) strtol(address.get().c_str(), nullptr, 0), + sda.get() == GPIO_NUM_NC + ? defaultSda + : sda.get(), + scl.get() == GPIO_NUM_NC + ? defaultScl + : scl.get() + }; + } +}; + +} // namespace farmhub::peripherals::environment diff --git a/src/peripherals/environment/Environment.hpp b/src/peripherals/environment/Environment.hpp new file mode 100644 index 00000000..bd90abbc --- /dev/null +++ b/src/peripherals/environment/Environment.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include "EnvironmentComponent.hpp" + +using namespace farmhub::devices; +using namespace farmhub::kernel; +using namespace farmhub::kernel::drivers; +using std::make_unique; +using std::unique_ptr; +namespace farmhub::peripherals::environment { + +class EnvironmentSht31 + : public Peripheral { +public: + EnvironmentSht31(const String& name, shared_ptr mqttRoot, I2CConfig config) + : Peripheral(name, mqttRoot) + , sht31(name, mqttRoot, config) { + } + + void populateTelemetry(JsonObject& telemetryJson) override { + sht31.populateTelemetry(telemetryJson); + } + +private: + Sht31Component sht31; +}; + +class EnvironmentSht31Factory + : public PeripheralFactory { +public: + EnvironmentSht31Factory() + : PeripheralFactory("environment:sht31", "environment") { + } + + unique_ptr> createPeripheral(const String& name, const I2CDeviceConfig& deviceConfig, shared_ptr mqttRoot) override { + auto i2cConfig = deviceConfig.parse(SHT_DEFAULT_ADDRESS, GPIO_NUM_NC, GPIO_NUM_NC); + Log.infoln("Creating SHT31 environment sensor %s with %s", name.c_str(), i2cConfig.toString().c_str()); + return make_unique(name, mqttRoot, i2cConfig); + } +}; + +} // namespace farmhub::peripherals::environment diff --git a/src/peripherals/environment/EnvironmentComponent.hpp b/src/peripherals/environment/EnvironmentComponent.hpp new file mode 100644 index 00000000..e823fd92 --- /dev/null +++ b/src/peripherals/environment/EnvironmentComponent.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include +#include + +#include +#include + +#include + +using namespace farmhub::kernel; + +namespace farmhub::peripherals::environment { + +class Sht31Component + : public Component, + public TelemetryProvider { +public: + Sht31Component( + const String& name, + shared_ptr mqttRoot, + I2CConfig config) + : Component(name, mqttRoot) + // TODO Add I2C manager to hand out wires + , wire(1) + , sht31(config.address, &wire) { + + // TODO Add commands to soft/hard reset the sensor + // TODO Add configuration for fast / slow measurement + // TODO Add a separate task to do measurements to unblock telemetry collection? + + Log.infoln("Initializing SHT31 environment sensor with %s", config.toString().c_str()); + if (!wire.begin(config.sda, config.scl, 100000L)) { + Log.errorln("Failed to initialize I2C bus for SHT31 environment sensor"); + return; + } + if (!sht31.begin()) { + Log.errorln("Failed to initialize SHT31 environment sensor: %d", + sht31.getError()); + return; + } + if (!sht31.isConnected()) { + Log.errorln("SHT31 environment sensor is not connected: %d", + sht31.getError()); + return; + } + initialized = true; + } + + void populateTelemetry(JsonObject& json) override { + if (!initialized) { + return; + } + if (!sht31.read()) { + Log.errorln("Failed to read SHT31 environment sensor: %d", + sht31.getError()); + return; + } + json["temperature"] = sht31.getTemperature(); + json["humidity"] = sht31.getHumidity(); + } + +private: + TwoWire wire; + SHT31 sht31; + bool initialized = false; +}; + +} // namespace farmhub::peripherals::environment From bf0eb1637354499f79f648b50e25c3ba60712e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 18:19:12 +0100 Subject: [PATCH 09/10] Add MK4 built-in SHT31 --- src/devices/UglyDucklingMk4.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/devices/UglyDucklingMk4.hpp b/src/devices/UglyDucklingMk4.hpp index 322e7fd1..e405bd89 100644 --- a/src/devices/UglyDucklingMk4.hpp +++ b/src/devices/UglyDucklingMk4.hpp @@ -44,6 +44,21 @@ class UglyDucklingMk4 : public DeviceDefinition { peripheralManager.registerFactory(flowControlFactory); } + std::list getBuiltInPeripherals() override { + // Device address is 0x44 = 68 + return { + R"({ + "type": "environment:sht31", + "name": "environment", + "params": { + "address": "0x44", + "sda": 8, + "scl": 9 + } + })" + }; + } + Drv8801Driver motorDriver { pwm, GPIO_NUM_10, // Enable From 23d6b5c49f77d2f06a03bdfa4acb87ba4c62e1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=B3r=C3=A1nt=20Pint=C3=A9r?= Date: Wed, 31 Jan 2024 18:28:32 +0100 Subject: [PATCH 10/10] Rename files related to SHT3x to EnvironmentSht3x --- platformio.ini | 2 +- src/devices/DeviceDefinition.hpp | 6 ++-- src/devices/UglyDucklingMk4.hpp | 2 +- .../{Environment.hpp => EnvironmentSht3x.hpp} | 22 ++++++------ ...nent.hpp => EnvironmentSht3xComponent.hpp} | 34 +++++++++---------- 5 files changed, 33 insertions(+), 33 deletions(-) rename src/peripherals/environment/{Environment.hpp => EnvironmentSht3x.hpp} (68%) rename src/peripherals/environment/{EnvironmentComponent.hpp => EnvironmentSht3xComponent.hpp} (61%) diff --git a/platformio.ini b/platformio.ini index ed8ff164..e60b820c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -49,7 +49,7 @@ lib_deps = https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2 arduino-libraries/NTPClient@^3.2.1 thijse/ArduinoLog@^1.1.1 - # SHT31 + # SHT3x support robtillaart/SHT31@~0.5.0 [debug] diff --git a/src/devices/DeviceDefinition.hpp b/src/devices/DeviceDefinition.hpp index 03999985..6ca808cf 100644 --- a/src/devices/DeviceDefinition.hpp +++ b/src/devices/DeviceDefinition.hpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include @@ -53,7 +53,7 @@ class DeviceDefinition { } virtual void registerPeripheralFactories(PeripheralManager& peripheralManager) { - peripheralManager.registerFactory(sht31Factory); + peripheralManager.registerFactory(sht3xFactory); registerDeviceSpecificPeripheralFactories(peripheralManager); } @@ -78,7 +78,7 @@ class DeviceDefinition { TDeviceConfiguration& config = configFile.config; private: - EnvironmentSht31Factory sht31Factory; + EnvironmentSht3xFactory sht3xFactory; }; template diff --git a/src/devices/UglyDucklingMk4.hpp b/src/devices/UglyDucklingMk4.hpp index e405bd89..62ee12fb 100644 --- a/src/devices/UglyDucklingMk4.hpp +++ b/src/devices/UglyDucklingMk4.hpp @@ -48,7 +48,7 @@ class UglyDucklingMk4 : public DeviceDefinition { // Device address is 0x44 = 68 return { R"({ - "type": "environment:sht31", + "type": "environment:sht3x", "name": "environment", "params": { "address": "0x44", diff --git a/src/peripherals/environment/Environment.hpp b/src/peripherals/environment/EnvironmentSht3x.hpp similarity index 68% rename from src/peripherals/environment/Environment.hpp rename to src/peripherals/environment/EnvironmentSht3x.hpp index bd90abbc..ac42937d 100644 --- a/src/peripherals/environment/Environment.hpp +++ b/src/peripherals/environment/EnvironmentSht3x.hpp @@ -7,7 +7,7 @@ #include #include -#include "EnvironmentComponent.hpp" +#include "EnvironmentSht3xComponent.hpp" using namespace farmhub::devices; using namespace farmhub::kernel; @@ -16,33 +16,33 @@ using std::make_unique; using std::unique_ptr; namespace farmhub::peripherals::environment { -class EnvironmentSht31 +class EnvironmentSht3x : public Peripheral { public: - EnvironmentSht31(const String& name, shared_ptr mqttRoot, I2CConfig config) + EnvironmentSht3x(const String& name, shared_ptr mqttRoot, I2CConfig config) : Peripheral(name, mqttRoot) - , sht31(name, mqttRoot, config) { + , sht3x(name, mqttRoot, config) { } void populateTelemetry(JsonObject& telemetryJson) override { - sht31.populateTelemetry(telemetryJson); + sht3x.populateTelemetry(telemetryJson); } private: - Sht31Component sht31; + EnvironmentSht3xComponent sht3x; }; -class EnvironmentSht31Factory +class EnvironmentSht3xFactory : public PeripheralFactory { public: - EnvironmentSht31Factory() - : PeripheralFactory("environment:sht31", "environment") { + EnvironmentSht3xFactory() + : PeripheralFactory("environment:sht3x", "environment") { } unique_ptr> createPeripheral(const String& name, const I2CDeviceConfig& deviceConfig, shared_ptr mqttRoot) override { auto i2cConfig = deviceConfig.parse(SHT_DEFAULT_ADDRESS, GPIO_NUM_NC, GPIO_NUM_NC); - Log.infoln("Creating SHT31 environment sensor %s with %s", name.c_str(), i2cConfig.toString().c_str()); - return make_unique(name, mqttRoot, i2cConfig); + Log.infoln("Creating SHT3x environment sensor %s with %s", name.c_str(), i2cConfig.toString().c_str()); + return make_unique(name, mqttRoot, i2cConfig); } }; diff --git a/src/peripherals/environment/EnvironmentComponent.hpp b/src/peripherals/environment/EnvironmentSht3xComponent.hpp similarity index 61% rename from src/peripherals/environment/EnvironmentComponent.hpp rename to src/peripherals/environment/EnvironmentSht3xComponent.hpp index e823fd92..96aea71c 100644 --- a/src/peripherals/environment/EnvironmentComponent.hpp +++ b/src/peripherals/environment/EnvironmentSht3xComponent.hpp @@ -15,36 +15,36 @@ using namespace farmhub::kernel; namespace farmhub::peripherals::environment { -class Sht31Component +class EnvironmentSht3xComponent : public Component, public TelemetryProvider { public: - Sht31Component( + EnvironmentSht3xComponent( const String& name, shared_ptr mqttRoot, I2CConfig config) : Component(name, mqttRoot) // TODO Add I2C manager to hand out wires , wire(1) - , sht31(config.address, &wire) { + , sht3x(config.address, &wire) { // TODO Add commands to soft/hard reset the sensor // TODO Add configuration for fast / slow measurement // TODO Add a separate task to do measurements to unblock telemetry collection? - Log.infoln("Initializing SHT31 environment sensor with %s", config.toString().c_str()); + Log.infoln("Initializing SHT3s environment sensor with %s", config.toString().c_str()); if (!wire.begin(config.sda, config.scl, 100000L)) { - Log.errorln("Failed to initialize I2C bus for SHT31 environment sensor"); + Log.errorln("Failed to initialize I2C bus for SHT3x environment sensor"); return; } - if (!sht31.begin()) { - Log.errorln("Failed to initialize SHT31 environment sensor: %d", - sht31.getError()); + if (!sht3x.begin()) { + Log.errorln("Failed to initialize SHT3x environment sensor: %d", + sht3x.getError()); return; } - if (!sht31.isConnected()) { - Log.errorln("SHT31 environment sensor is not connected: %d", - sht31.getError()); + if (!sht3x.isConnected()) { + Log.errorln("SHT3x environment sensor is not connected: %d", + sht3x.getError()); return; } initialized = true; @@ -54,18 +54,18 @@ class Sht31Component if (!initialized) { return; } - if (!sht31.read()) { - Log.errorln("Failed to read SHT31 environment sensor: %d", - sht31.getError()); + if (!sht3x.read()) { + Log.errorln("Failed to read SHT3x environment sensor: %d", + sht3x.getError()); return; } - json["temperature"] = sht31.getTemperature(); - json["humidity"] = sht31.getHumidity(); + json["temperature"] = sht3x.getTemperature(); + json["humidity"] = sht3x.getHumidity(); } private: TwoWire wire; - SHT31 sht31; + SHT31 sht3x; bool initialized = false; };