Skip to content

Commit

Permalink
Merge pull request #7 from kivancsikert/separate-peripheral-and-compo…
Browse files Browse the repository at this point in the history
…nent

Separate peripherals and components
  • Loading branch information
lptr authored Jan 2, 2024
2 parents c8fa774 + 8d5e6f8 commit 3599f22
Show file tree
Hide file tree
Showing 47 changed files with 1,272 additions and 530 deletions.
25 changes: 25 additions & 0 deletions Peripherals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Peripherals

* A **device** is a physical object that you can move around that has an MCU and can connect to the hub.

An example of a _device_ is an actual Ugly Duckling PCB, placed in the field, connected to whatever externals.

The device publishes telemetry, and receives commands under `/devices/ugly-duckling/$INSTANCE`.

* A **peripheral** is an independently meaningful functionality made available by the device. This can mean to measure and provide telemetry, and/or to be operated via direct commands and/or via published configuration.

An example of a _peripheral_ is a flow-control device that consists of a valve, a flow sensor, and optionally a thermometer.

A peripheral publishes telemetry and receives commands and configuration under `/peripherals/$TYPE/$NAME`.

* A **component** is a functional aspect of a peripheral connected to services. Components are created as part of the creation of a peripheral, and are not addressable.

An example of a _component_ is a valve that depends on a motor driver for operation.

A component can register its own commands under its owner peripheral’s topic.

A component will contribute to the telemetry published by its owner peripheral.

A component will receive the configuration of its owning peripheral; it needs to pick out the data it needs from it.

* A **service** is an internally addressable feature of the device, i.e. that it has a LED connected to a pin, or a motor driver on a certain set of pins, or even its raw pins themselves.
Empty file modified pio-docker
100644 → 100755
Empty file.
20 changes: 17 additions & 3 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,39 @@ default_envs = mk6
boards_dir = boards
build_cache_dir = .pio/build-cache

[base]
build_unflags =
-std=gnu++11
build_flags =
-std=gnu++17
lib_deps =
bblanchon/ArduinoJson@^6.21.3

[env:native]
extends = base
platform = native@~1.2.1
test_framework = googletest

[esp32base]
extends = base
platform = espressif32@~6.4.0
framework = espidf, arduino
extra_scripts =
pre:git-version.py
build_type = release
build_unflags =
-std=gnu++11
${base.build_unflags}
build_flags =
-std=gnu++17
${base.build_flags}
-D WM_NODEBUG=1
-D FARMHUB_REPORT_MEMORY

monitor_filters = esp32_exception_decoder
monitor_speed = 115200

lib_deps =
${base.lib_deps}
256dpi/MQTT@^2.5.1
bblanchon/ArduinoJson@^6.21.3
https://github.com/tzapu/WiFiManager.git#v2.0.16-rc.2
arduino-libraries/NTPClient@^3.2.1
thijse/ArduinoLog@^1.1.1
Expand Down
80 changes: 34 additions & 46 deletions src/devices/Device.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <chrono>
#include <memory>

#ifndef FARMHUB_LOG_LEVEL
#ifdef FARMHUB_DEBUG
Expand All @@ -19,6 +20,7 @@
#include <kernel/Task.hpp>

using namespace std::chrono;
using std::shared_ptr;
using namespace farmhub::kernel;

#if defined(MK4)
Expand All @@ -42,14 +44,12 @@ typedef farmhub::devices::Mk6Config TDeviceConfiguration;
#error "No device defined"
#endif

namespace farmhub { namespace devices {
namespace farmhub::devices {

#ifdef FARMHUB_DEBUG
class ConsolePrinter : public Print {
public:
ConsolePrinter() {
Serial.begin(115200);

static const String spinner = "|/-\\";
static const int spinnerLength = spinner.length();
Task::loop("console", 8192, 1, [this](Task& task) {
Expand Down Expand Up @@ -159,13 +159,20 @@ void printLogLine(Print* printer, int level) {
class ConsoleProvider {
public:
ConsoleProvider() {
Serial.begin(115200);
#ifdef FARMHUB_DEBUG
Log.begin(FARMHUB_LOG_LEVEL, &consolePrinter);
Log.setSuffix(printLogLine);
#else
Log.begin(FARMHUB_LOG_LEVEL, &Serial);
#endif
Log.infoln("Starting up...");
Log.infoln(F(" ______ _ _ _"));
Log.infoln(F(" | ____| | | | | | |"));
Log.infoln(F(" | |__ __ _ _ __ _ __ ___ | |__| |_ _| |__"));
Log.infoln(F(" | __/ _` | '__| '_ ` _ \\| __ | | | | '_ \\"));
Log.infoln(F(" | | | (_| | | | | | | | | | | | |_| | |_) |"));
Log.infoln(F(" |_| \\__,_|_| |_| |_| |_|_| |_|\\__,_|_.__/ %s"), VERSION);
Log.infoln("");
}
};

Expand All @@ -178,22 +185,20 @@ class MemoryTelemetryProvider : public TelemetryProvider {

class MqttTelemetryPublisher : public TelemetryPublisher {
public:
MqttTelemetryPublisher(MqttDriver::MqttRoot& mqtt, TelemetryCollector& telemetryCollector)
: mqttRoot(mqtt)
MqttTelemetryPublisher(shared_ptr<MqttDriver::MqttRoot> mqttRoot, TelemetryCollector& telemetryCollector)
: mqttRoot(mqttRoot)
, telemetryCollector(telemetryCollector) {
}

void publishTelemetry() {
mqttRoot.publish("telemetry", [&](JsonObject& json) { telemetryCollector.collect(json); });
mqttRoot->publish("telemetry", [&](JsonObject& json) { telemetryCollector.collect(json); });
}

private:
MqttDriver::MqttRoot& mqttRoot;
shared_ptr<MqttDriver::MqttRoot> mqttRoot;
TelemetryCollector& telemetryCollector;
};

typedef std::function<void(const JsonObject&, JsonObject&)> CommandHandler;

class Device : ConsoleProvider {
public:
Device() {
Expand All @@ -213,19 +218,17 @@ class Device : ConsoleProvider {

// deviceTelemetryCollector.registerProvider("peripherals", peripheralManager);

registerCommand(echoCommand);
registerCommand(pingCommand);
mqttDeviceRoot->registerCommand(echoCommand);
mqttDeviceRoot->registerCommand(pingCommand);
// TODO Add reset-wifi command
// registerCommand(resetWifiCommand);
registerCommand(restartCommand);
registerCommand(sleepCommand);
registerCommand(fileListCommand);
registerCommand(fileReadCommand);
registerCommand(fileWriteCommand);
registerCommand(fileRemoveCommand);
registerCommand(httpUpdateCommand);

peripheralManager.begin();
// mqttDeviceRoot->registerCommand(resetWifiCommand);
mqttDeviceRoot->registerCommand(restartCommand);
mqttDeviceRoot->registerCommand(sleepCommand);
mqttDeviceRoot->registerCommand(fileListCommand);
mqttDeviceRoot->registerCommand(fileReadCommand);
mqttDeviceRoot->registerCommand(fileWriteCommand);
mqttDeviceRoot->registerCommand(fileRemoveCommand);
mqttDeviceRoot->registerCommand(httpUpdateCommand);

#if defined(MK4)
deviceDefinition.motorDriver.wakeUp();
Expand All @@ -235,9 +238,14 @@ class Device : ConsoleProvider {
deviceDefinition.motorDriver.wakeUp();
#endif

kernel.begin();
// We want RTC to be in sync before we start setting up peripherals
kernel.getRtcInSyncState().awaitSet();

peripheralManager.begin();

kernel.getKernelReadyState().awaitSet();

mqttDeviceRoot.publish(
mqttDeviceRoot->publish(
"init",
[&](JsonObject& json) {
// TODO Remove redundanty mentions of "ugly-duckling"
Expand Down Expand Up @@ -265,33 +273,13 @@ class Device : ConsoleProvider {
task.delayUntil(milliseconds(60000));
}

void registerCommand(const String& name, CommandHandler handler) {
String suffix = "commands/" + name;
mqttDeviceRoot.subscribe(suffix, MqttDriver::QoS::ExactlyOnce, [this, name, suffix, handler](const String&, const JsonObject& request) {
// Clear topic
mqttDeviceRoot.clear(suffix, MqttDriver::Retention::Retain, MqttDriver::QoS::ExactlyOnce);
DynamicJsonDocument responseDoc(2048);
auto response = responseDoc.to<JsonObject>();
handler(request, response);
if (response.size() > 0) {
mqttDeviceRoot.publish("responses/" + name, responseDoc, MqttDriver::Retention::NoRetain, MqttDriver::QoS::ExactlyOnce);
}
});
}

void registerCommand(Command& command) {
registerCommand(command.name, [&](const JsonObject& request, JsonObject& response) {
command.handle(request, response);
});
}

TDeviceDefinition deviceDefinition;
TDeviceConfiguration& deviceConfig = deviceDefinition.config;
Kernel<TDeviceConfiguration> kernel { deviceConfig, deviceDefinition.statusLed };
PeripheralManager peripheralManager { kernel.mqtt, deviceConfig.peripherals };

TelemetryCollector deviceTelemetryCollector;
MqttDriver::MqttRoot mqttDeviceRoot = kernel.mqtt.forRoot("devices/ugly-duckling/" + deviceConfig.instance.get());
shared_ptr<MqttDriver::MqttRoot> mqttDeviceRoot = kernel.mqtt.forRoot("devices/ugly-duckling/" + deviceConfig.instance.get());
MqttTelemetryPublisher deviceTelemetryPublisher { mqttDeviceRoot, deviceTelemetryCollector };
PingCommand pingCommand { deviceTelemetryPublisher };

Expand All @@ -310,4 +298,4 @@ class Device : ConsoleProvider {
HttpUpdateCommand httpUpdateCommand { kernel.version };
};

}} // namespace farmhub::devices
} // namespace farmhub::devices
8 changes: 4 additions & 4 deletions src/devices/DeviceDefinition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using namespace farmhub::kernel;
using namespace farmhub::kernel::drivers;

namespace farmhub { namespace devices {
namespace farmhub::devices {

class DeviceConfiguration : public ConfigurationSection {
public:
Expand All @@ -23,8 +23,8 @@ class DeviceConfiguration : public ConfigurationSection {
Property<String> model;
Property<String> instance;

MqttDriver::Config mqtt { this, "mqtt" };
RtcDriver::Config ntp { this, "ntp" };
NamedConfigurationEntry<MqttDriver::Config> mqtt { this, "mqtt" };
NamedConfigurationEntry<RtcDriver::Config> ntp { this, "ntp" };

ArrayProperty<JsonAsString> peripherals { this, "peripherals" };

Expand Down Expand Up @@ -73,4 +73,4 @@ class BatteryPoweredDeviceDefinition : public DeviceDefinition<TDeviceConfigurat
BatteryDriver batteryDriver;
};

}} // namespace farmhub::devices
} // namespace farmhub::devices
Loading

0 comments on commit 3599f22

Please sign in to comment.