Skip to content

Commit

Permalink
Merge pull request #769 from SignalK/less_virtual
Browse files Browse the repository at this point in the history
More smart pointers
  • Loading branch information
mairas authored Oct 10, 2024
2 parents 232f72b + 84c1533 commit dceddde
Show file tree
Hide file tree
Showing 55 changed files with 944 additions and 800 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
`ConfigItem`. Web configuration cards are now only rendered for objects that
have been wrapped in a `ConfigItem` call.

- [Smart pointers](https://medium.com/@lucky_rydar/guide-over-smart-pointers-in-c-46ed8b04448c)
are used internally throughout the project. This reduces the risk of memory
leaks or crashes due to dangling pointers. It is also possible, but not
mandatory, to use smart pointers in your own code.

### Migrating Existing Projects

- Update your project's `platformio.ini` file to use the new version of SensESP:
Expand Down
31 changes: 18 additions & 13 deletions examples/freertos_tasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,32 +36,37 @@ void setup() {
SetupLogging();

SensESPMinimalAppBuilder builder;
SensESPMinimalApp *sensesp_app = builder.set_hostname("async")->get_app();
auto sensesp_app = builder.set_hostname("async")->get_app();

auto *networking = new Networking("/system/networking", "", "");
auto *http_server = new HTTPServer();
auto networking = std::make_shared<Networking>("/system/networking", "", "");
auto http_server = std::make_shared<HTTPServer>();

// create the SK delta object
auto sk_delta_queue_ = new SKDeltaQueue();
auto sk_delta_queue_ = std::make_shared<SKDeltaQueue>();

// create the websocket client
auto ws_client_ = new SKWSClient("/system/sk", sk_delta_queue_, "", 0);
auto ws_client_ =
std::make_shared<SKWSClient>("/system/sk", sk_delta_queue_, "", 0);

ws_client_->connect_to(
new LambdaConsumer<SKWSConnectionState>([](SKWSConnectionState input) {
auto output_consumer = std::make_shared<LambdaConsumer<SKWSConnectionState>>(
[](SKWSConnectionState input) {
ESP_LOGD("Example", "SKWSConnectionState: %d", input);
}));
});

ws_client_->connect_to(output_consumer);

// create the MDNS discovery object
auto mdns_discovery_ = new MDNSDiscovery();
auto mdns_discovery_ = std::make_shared<MDNSDiscovery>();

// create a system status controller and a led blinker

auto *system_status_controller = new SystemStatusController();
auto *system_status_led = new SystemStatusLed(LED_BUILTIN);
auto system_status_controller = std::make_shared<SystemStatusController>();
auto system_status_led = std::make_shared<SystemStatusLed>(LED_BUILTIN);

system_status_controller->connect_to(system_status_led);
ws_client_->get_delta_tx_count_producer().connect_to(system_status_led);
system_status_controller->connect_to(
system_status_led->get_system_status_consumer());
ws_client_->get_delta_tx_count_producer().connect_to(
system_status_led->get_delta_tx_count_consumer());

// create a new task for toggling the output pin

Expand Down
2 changes: 1 addition & 1 deletion examples/join_and_zip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

using namespace sensesp;

SensESPMinimalApp* sensesp_app;
std::shared_ptr<SensESPMinimalApp> sensesp_app;

void setup() {
SetupLogging();
Expand Down
35 changes: 20 additions & 15 deletions examples/minimal_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,27 +35,32 @@ void setup() {
// manually create Networking and HTTPServer objects to enable
// the HTTP configuration interface

auto* networking = new Networking("/system/networking", "", "");
auto* http_server = new HTTPServer();
auto networking = std::make_shared<Networking>("/system/networking", "", "");
auto http_server = std::make_shared<HTTPServer>();

auto* digin1 = new DigitalInputCounter(input_pin1, INPUT, RISING, read_delay);
auto* digin2 = new DigitalInputCounter(input_pin2, INPUT, CHANGE, read_delay);
auto digin1 = std::make_shared<DigitalInputCounter>(input_pin1, INPUT, RISING,
read_delay);
auto digin2 = std::make_shared<DigitalInputCounter>(input_pin2, INPUT, CHANGE,
read_delay);

auto* scaled1 = new Linear(2, 1, "/digin1/scale");
auto* scaled2 = new Linear(4, -1, "/digin2/scale");
auto scaled1 = std::make_shared<Linear>(2, 1, "/digin1/scale");
auto scaled2 = std::make_shared<Linear>(4, -1, "/digin2/scale");
digin1->connect_to(scaled1);

scaled1->connect_to(new LambdaTransform<int, int>([](int input) {
Serial.printf("millis: %d\n", millis());
Serial.printf("Counter 1: %d\n", input);
return input;
}));

digin2->connect_to(scaled2)->connect_to(
new LambdaTransform<int, int>([](int input) {
auto lambda_transform1 =
std::make_shared<LambdaTransform<int, int>>([](int input) {
Serial.printf("millis: %d\n", millis());
Serial.printf("Counter 1: %d\n", input);
return input;
});
scaled1->connect_to(lambda_transform1);
auto lambda_transform2 =
std::make_shared<LambdaTransform<int, int>>([](int input) {
Serial.printf("Counter 2: %d\n", input);
return input;
}));
});

digin2->connect_to(scaled2)->connect_to(lambda_transform2);

pinMode(output_pin1, OUTPUT);
event_loop()->onRepeat(
Expand Down
6 changes: 3 additions & 3 deletions examples/raw_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ void setup() {
SetupLogging();

SensESPAppBuilder builder;
SensESPApp *sensesp_app = builder.set_hostname("json_demo")
->set_wifi_client("Hat Labs Sensors", "kanneluuri2406")
->get_app();
auto sensesp_app = builder.set_hostname("json_demo")
->set_wifi_client("Hat Labs Sensors", "kanneluuri2406")
->get_app();

event_loop()->onRepeat(1000, []() { toggler.set(!toggler.get()); });

Expand Down
2 changes: 1 addition & 1 deletion examples/repeat_transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

using namespace sensesp;

SensESPMinimalApp* sensesp_app;
std::shared_ptr<SensESPMinimalApp> sensesp_app;

void setup() {
SetupLogging();
Expand Down
6 changes: 3 additions & 3 deletions examples/smart_switch/remote_switch_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ void setup() {
controller->connect_to(new BoolSKPutRequest(sk_path));

// Also connect the controller to an onboard LED...
controller->connect_to(led);
controller->connect_to(led->on_off_consumer_);

// Connect a physical button that will feed manual click types into the
// controller...
Expand All @@ -92,7 +92,7 @@ void setup() {
->set_title("Click Type")
->set_sort_order(1000);

pr->connect_to(click_type)->connect_to(controller);
pr->connect_to(click_type)->connect_to(controller->click_consumer_);

// In addition to the manual button "click types", a
// SmartSwitchController accepts explicit state settings via
Expand All @@ -105,7 +105,7 @@ void setup() {
// sent across the Signal K network when the controlling device
// confirms it has made the change in state.
auto* sk_listener = new SKValueListener<bool>(sk_path);
sk_listener->connect_to(controller);
sk_listener->connect_to(controller->swich_consumer_);
}

void loop() { event_loop()->tick(); }
14 changes: 8 additions & 6 deletions examples/smart_switch/smart_switch_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void setup() {
// Create the global SensESPApp() object.
sensesp_app = builder.set_hostname("sk-engine-lights")
->set_sk_server("192.168.10.3", 3000)
->set_wifi_client(client("YOUR_WIFI_SSID", "YOUR_WIFI_PASSWORD")
->set_wifi_client("YOUR_WIFI_SSID", "YOUR_WIFI_PASSWORD")
->get_app();

// Define the SK Path that represents the load this device controls.
Expand All @@ -73,9 +73,10 @@ void setup() {
// electric light. Also connect this pin's state to an LED to get
// a visual indicator of load's state.
auto* load_switch = new DigitalOutput(PIN_RELAY);
load_switch->connect_to(new RgbLed(PIN_LED_R, PIN_LED_G, PIN_LED_B,
config_path_status_light, LED_ON_COLOR,
LED_OFF_COLOR));
load_switch->connect_to(
(new RgbLed(PIN_LED_R, PIN_LED_G, PIN_LED_B, config_path_status_light,
LED_ON_COLOR, LED_OFF_COLOR))
->on_off_consumer_);

// Create a switch controller to handle the user press logic and
// connect it to the load switch...
Expand All @@ -87,7 +88,8 @@ void setup() {
DigitalInputState* btn = new DigitalInputState(PIN_BUTTON, INPUT, 100);
PressRepeater* pr = new PressRepeater();
btn->connect_to(pr);
pr->connect_to(new ClickType(config_path_button_c))->connect_to(controller);
pr->connect_to(new ClickType(config_path_button_c))
->connect_to(controller->click_consumer_);

// In addition to the manual button "click types", a
// SmartSwitchController accepts explicit state settings via
Expand All @@ -98,7 +100,7 @@ void setup() {
// This allows any device on the SignalK network that can make
// such a request to also control the state of our switch.
auto* sk_listener = new StringSKPutRequestListener(sk_path);
sk_listener->connect_to(controller);
sk_listener->connect_to(controller->truthy_string_consumer_);

// Finally, connect the load switch to an SKOutput so it reports its state
// to the Signal K server. Since the load switch only reports its state
Expand Down
26 changes: 13 additions & 13 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,25 @@ upload_speed = 2000000
monitor_speed = 115200

lib_deps =
mairas/ReactESP @ ^3.0.0
bblanchon/ArduinoJson @ ^7.0.0
pfeerick/elapsedMillis @ ^1.0.6
bxparks/AceButton @ ^1.10.1
mairas/ReactESP @ ^3.0.0
bblanchon/ArduinoJson @ ^7.0.0
pfeerick/elapsedMillis @ ^1.0.6
bxparks/AceButton @ ^1.10.1
build_unflags = -Werror=reorder
board_build.partitions = min_spiffs.csv
monitor_filters = esp32_exception_decoder
extra_scripts =
pre:extra_script.py
pre:extra_script.py
check_skip_packages = true
build_flags =
-D LED_BUILTIN=2
; Max (and default) debugging level in Arduino ESP32 Core
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
; Arduino Core bug workaround: define the log tag for the Arduino
; logging macros.
-D TAG='"Arduino"'
; Use the ESP-IDF logging library - required by SensESP.
-D USE_ESP_IDF_LOG
-D LED_BUILTIN=2
; Max (and default) debugging level in Arduino ESP32 Core
-D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE
; Arduino Core bug workaround: define the log tag for the Arduino
; logging macros.
-D TAG='"Arduino"'
; Use the ESP-IDF logging library - required by SensESP.
-D USE_ESP_IDF_LOG

; Uncomment the following to use the OTA interface for flashing.
; "mydevice" must correspond to the device hostname.
Expand Down
44 changes: 1 addition & 43 deletions src/sensesp/controllers/smart_switch_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace sensesp {
SmartSwitchController::SmartSwitchController(bool auto_initialize,
String config_path,
const char* sk_sync_paths[])
: BooleanTransform(config_path), auto_initialize_{auto_initialize} {
: FileSystemSaveable(config_path), auto_initialize_{auto_initialize} {
if (sk_sync_paths != NULL) {
sync_paths_.clear();
int i = 0;
Expand All @@ -27,48 +27,6 @@ SmartSwitchController::SmartSwitchController(bool auto_initialize,
}
}

void SmartSwitchController::set(const bool& new_value) {
is_on_ = new_value;
this->emit(is_on_);
}

void SmartSwitchController::set(const ClickTypes& new_value) {
if (!ClickType::is_click(new_value)) {
// Ignore button presses (we only want interpreted clicks)
return;
}

if (new_value == ClickTypes::UltraLongSingleClick) {
// Long clicks reboot the system...
ESP.restart();
return;
}

// All other click types toggle the current state...
is_on_ = !is_on_;
this->emit(is_on_);

if (new_value == ClickTypes::DoubleClick) {
// Sync any specified sync paths...
for (auto& path : sync_paths_) {
ESP_LOGD(__FILENAME__, "Sync status to %s", path.sk_sync_path_.c_str());
path.put_request_->set(is_on_);
}
}
}

void SmartSwitchController::set(const String& new_value) {
if (TextToTruth::is_valid_true(new_value)) {
is_on_ = true;
} else if (TextToTruth::is_valid_false(new_value)) {
is_on_ = false;
} else {
// All other values simply toggle...
is_on_ = !is_on_;
}
this->emit(is_on_);
}

bool SmartSwitchController::to_json(JsonObject& root) {
JsonArray jPaths = root["sync_paths"].to<JsonArray>();
for (auto& path : sync_paths_) {
Expand Down
51 changes: 45 additions & 6 deletions src/sensesp/controllers/smart_switch_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
#define _smart_switch_controller_h

#include "sensesp/signalk/signalk_put_request.h"
#include "sensesp/system/lambda_consumer.h"
#include "sensesp/system/valueconsumer.h"
#include "sensesp/transforms/click_type.h"
#include "sensesp/transforms/truth_text.h"
#include "sensesp/ui/config_item.h"

namespace sensesp {
Expand Down Expand Up @@ -41,9 +43,7 @@ namespace sensesp {
* @see TextToTruth
* @see ClickType
*/
class SmartSwitchController : public BooleanTransform,
public ValueConsumer<ClickTypes>,
public ValueConsumer<String> {
class SmartSwitchController : public ValueProducer<bool>, FileSystemSaveable {
public:
/**
* The constructor
Expand All @@ -62,15 +62,54 @@ class SmartSwitchController : public BooleanTransform,
*/
SmartSwitchController(bool auto_initialize = true, String config_path = "",
const char* sk_sync_paths[] = NULL);
void set(const bool& new_value) override;
void set(const String& new_value) override;
void set(const ClickTypes& new_value) override;

// For reading and writing the configuration of this transformation
virtual bool to_json(JsonObject& doc) override;
virtual bool from_json(const JsonObject& config) override;

public:
LambdaConsumer<ClickTypes> click_consumer_{[this](ClickTypes new_value) {
if (!ClickType::is_click(new_value)) {
// Ignore button presses (we only want interpreted clicks)
return;
}

if (new_value == ClickTypes::UltraLongSingleClick) {
// Long clicks reboot the system...
ESP.restart();
return;
}

// All other click types toggle the current state...
this->is_on_ = !this->is_on_;
this->emit(this->is_on_);

if (new_value == ClickTypes::DoubleClick) {
// Sync any specified sync paths...
for (auto& path : sync_paths_) {
ESP_LOGD(__FILENAME__, "Sync status to %s", path.sk_sync_path_.c_str());
path.put_request_->set(this->is_on_);
}
}
}};

LambdaConsumer<String> truthy_string_consumer_{[this](String new_value) {
if (TextToTruth::is_valid_true(new_value)) {
this->is_on_ = true;
} else if (TextToTruth::is_valid_false(new_value)) {
this->is_on_ = false;
} else {
// All other values simply toggle...
this->is_on_ = !this->is_on_;
}
this->emit(this->is_on_);
}};

LambdaConsumer<bool> swich_consumer_{[this](bool value) {
this->is_on_ = value;
this->emit(is_on_);
}};

/// Used to store configuration internally.
class SyncPath {
public:
Expand Down
Loading

0 comments on commit dceddde

Please sign in to comment.