-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/node config update #4
base: main
Are you sure you want to change the base?
Changes from 13 commits
3e54876
29f705b
9b48808
dd4f371
2a0eafd
fd0081f
23cf7eb
29c6ba3
02f696f
ce8240f
4a5582d
3405e8c
2258559
58a8bd7
364ec21
4684ed3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,10 +4,12 @@ | |
#include <EspNowPreferences.h> | ||
#include <esp_crt_bundle.h> | ||
|
||
#define SLEEP_TIME_US (1000LL * 1000LL * 60LL * 1LL) // 1 minute | ||
#define MICROSECONDS_PER_SECOND 1000000LL | ||
#define SLEEP_TIME_US (MICROSECONDS_PER_SECOND * 60LL * 1LL) // 1 minute | ||
|
||
#define FIRMWARE_VERSION 90201 | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest adding this the Configuration example as another example that demonstrate how configuration works. This to keep this node example to a minimum as Configuration is optional (and will make it easier for the reader). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. node_with_configuration and host_with_configuration have been added for Arduino, and the original node and host examples left as-is. |
||
// These structs are the application messages shared across the host and node device. | ||
#pragma pack(1) | ||
struct MyApplicationMessage { | ||
|
@@ -18,6 +20,10 @@ struct MySecondApplicationMessage { | |
uint8_t id = 0x02; | ||
double temperature; | ||
}; | ||
struct MyConfigMessageV1 { | ||
uint32_t sleep_seconds; | ||
uint8_t foo; | ||
}; | ||
#pragma pack(0) | ||
|
||
// Encyption key used for our own packet encryption (GCM). | ||
|
@@ -61,7 +67,6 @@ EspNowNode::OnLog _on_log = [](const std::string message, const esp_log_level_t | |
level = "unknown"; | ||
break; | ||
} | ||
|
||
Serial.println(("EspNowNode (" + level + "): " + message).c_str()); | ||
}; | ||
|
||
|
@@ -93,17 +98,40 @@ EspNowNode _esp_now_node(_esp_now_crypt, _esp_now_preferences, FIRMWARE_VERSION, | |
|
||
void setup() { | ||
Serial.begin(115200); | ||
uint32_t timestamp1 = millis(); | ||
|
||
_esp_now_preferences.initalizeNVS(); | ||
|
||
EspNowConfigEnvelope config_envelope; | ||
MyConfigMessageV1 *cfg; | ||
bool config_loaded = false; | ||
if (_esp_now_preferences.getConfig(&config_envelope)) { | ||
config_loaded = true; | ||
cfg = (MyConfigMessageV1*) config_envelope.payload; | ||
_on_log(("loaded sleep_seconds=" + std::to_string(cfg->sleep_seconds) + " foo=" + std::to_string(cfg->foo)).c_str(), ESP_LOG_DEBUG); | ||
} else { | ||
_on_log("no config loaded", ESP_LOG_INFO); | ||
} | ||
|
||
// Setup node, send message, and then go to sleep. | ||
if (_esp_now_node.setup()) { | ||
|
||
MySecondApplicationMessage message; | ||
message.temperature = 25.6; | ||
_esp_now_node.sendMessage(&message, sizeof(MySecondApplicationMessage)); | ||
} else { | ||
_on_log("setup failed", ESP_LOG_ERROR); | ||
} | ||
|
||
uint32_t timestamp2 = millis(); | ||
_on_log(("elapsed " + std::to_string(timestamp2 - timestamp1) + "ms").c_str(), ESP_LOG_DEBUG); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you elaborate on why you added this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has been removed. When it comes to battery powered nodes, the execution time and boot time are something I measure and try to minimize, but it shouldn't have been left in. |
||
uint32_t sleep_time_us = SLEEP_TIME_US; | ||
if (config_loaded) { | ||
sleep_time_us = cfg->sleep_seconds * MICROSECONDS_PER_SECOND; | ||
} | ||
|
||
esp_deep_sleep(SLEEP_TIME_US); | ||
_on_log(("sleeping for " + std::to_string(sleep_time_us / (MICROSECONDS_PER_SECOND)) + " seconds").c_str(), ESP_LOG_INFO); | ||
esp_deep_sleep(sleep_time_us); | ||
} | ||
|
||
void loop() {} | ||
void loop() {} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -10,6 +10,8 @@ | |||||
#include <optional> | ||||||
#include <string> | ||||||
|
||||||
struct EspNowConfigEnvelope; | ||||||
|
||||||
/** | ||||||
* @brief ESP Now Network: Host | ||||||
* | ||||||
|
@@ -73,6 +75,9 @@ class EspNowHost { | |||||
typedef std::function<std::optional<FirmwareUpdate>(uint64_t mac_address, uint32_t firmware_version)> | ||||||
FirmwareUpdateAvailable; | ||||||
|
||||||
typedef std::function<std::optional<EspNowConfigEnvelope>(uint64_t mac_address, uint8_t config_version)> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing documentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CPP beginner question: For these callbacks, is it correct that the memory allocation for the struct that is returned is done within the function? Off the heap? For the FirmwareUpdate, there isn't an example of what this looks like. Also, in EspNowHost.cpp the struct that is received from the callback isn't explicitly deleted. Is this taken care of some other way? Would it be better to pass into the callback a struct that the callback can optionally populate, and return an int that indicates if it did so or not? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "callbacks" we are using here are just pure function calls, in contrast to other observer patterns where callbacks might run at a later point by the scheduler (e.g. using FreeRTOS or similar queues,event busses etc). (and yes, I should a better "FirmwareUpdate" example). This is how my firmware update implementation looks like in my host: std::optional<EspNowHost::FirmwareUpdate> HostDriver::onFirmwareUpdate(uint64_t mac_address, uint32_t firmware_version) {
auto optdev = _device_manager.deviceForMac(mac_address);
if (!optdev) {
return std::optional<EspNowHost::FirmwareUpdate>{std::nullopt};
}
auto type = optdev->get().type();
auto name = optdev->get().name();
auto hardware = optdev->get().hardware();
auto firmware_mqtt_path =
_mqtt_remote.clientId() + "/firmware/current/" + type + "/" + hardware + "/" + makeMqttPathCompatible(name);
// Do we have a newer firmware version for this type?
auto update_information = _firmware_checker.getUpdateUrl(firmware_version, type, hardware);
if (update_information) {
_mqtt_remote.publishMessage(firmware_mqtt_path, "Updating to " + std::to_string(update_information->version), true);
EspNowHost::FirmwareUpdate firmware_update;
strncpy(firmware_update.wifi_ssid, _wifi_ssid, sizeof(firmware_update.wifi_ssid));
strncpy(firmware_update.wifi_password, _wifi_password, sizeof(firmware_update.wifi_password));
strncpy(firmware_update.url, update_information->url.c_str(), sizeof(firmware_update.url));
strncpy(firmware_update.md5, update_information->md5.c_str(), sizeof(firmware_update.md5));
return std::optional<EspNowHost::FirmwareUpdate>{firmware_update};
}
_mqtt_remote.publishMessage(firmware_mqtt_path, std::to_string(firmware_version), true);
return std::optional<EspNowHost::FirmwareUpdate>{std::nullopt};
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
ConfigUpdateAvailable; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I think we can drop the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
|
||||||
enum class WiFiInterface { | ||||||
AP, // Use the Access Point interface for ESP-NOW. | ||||||
STA, // Use the Station/Client interface for ESP-NOW. | ||||||
|
@@ -96,7 +101,7 @@ class EspNowHost { | |||||
*/ | ||||||
EspNowHost(EspNowCrypt &crypt, WiFiInterface wifi_interface, OnNewMessage on_new_message, | ||||||
OnApplicationMessage on_application_message, FirmwareUpdateAvailable firwmare_update = {}, | ||||||
OnLog on_log = {}); | ||||||
ConfigUpdateAvailable config_update = {}, OnLog on_log = {}); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing @param documentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @param docs added |
||||||
|
||||||
public: | ||||||
/** | ||||||
|
@@ -117,7 +122,8 @@ class EspNowHost { | |||||
|
||||||
void handleQueuedMessage(uint8_t *mac_addr, uint8_t *data); | ||||||
void handleDiscoveryRequest(uint8_t *mac_addr, uint32_t discovery_challenge); | ||||||
void handleChallengeRequest(uint8_t *mac_addr, uint32_t challenge_challenge, uint32_t firmware_version); | ||||||
void handleChallengeRequest(uint8_t *mac_addr, uint32_t challenge_challenge, uint32_t firmware_version, | ||||||
uint16_t config_version); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||||||
|
||||||
void sendMessageToTemporaryPeer(uint8_t *mac_addr, void *message, size_t length); | ||||||
|
||||||
|
@@ -137,6 +143,7 @@ class EspNowHost { | |||||
OnLog _on_log; | ||||||
OnNewMessage _on_new_message; | ||||||
FirmwareUpdateAvailable _firwmare_update; | ||||||
ConfigUpdateAvailable _config_update; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move up one line, and initialize |
||||||
OnApplicationMessage _on_application_message; | ||||||
}; | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,9 +57,10 @@ void EspNowHost::esp_now_on_data_callback(const esp_now_recv_info_t *esp_now_inf | |
|
||
EspNowHost::EspNowHost(EspNowCrypt &crypt, EspNowHost::WiFiInterface wifi_interface, OnNewMessage on_new_message, | ||
OnApplicationMessage on_application_message, FirmwareUpdateAvailable firwmare_update, | ||
OnLog on_log) | ||
ConfigUpdateAvailable config_update, OnLog on_log) | ||
: _crypt(crypt), _wifi_interface(wifi_interface), _on_log(on_log), _on_new_message(on_new_message), | ||
_firwmare_update(firwmare_update), _on_application_message(on_application_message) {} | ||
_firwmare_update(firwmare_update), _config_update(config_update), | ||
_on_application_message(on_application_message) {} | ||
|
||
void EspNowHost::newMessageTask(void *pvParameters) { | ||
EspNowHost *_this = (EspNowHost *)pvParameters; | ||
|
@@ -185,10 +186,11 @@ void EspNowHost::handleQueuedMessage(uint8_t *mac_addr, uint8_t *data) { | |
case MESSAGE_ID_CHALLENGE_REQUEST_V1: { | ||
EspNowChallengeRequestV1 *message = (EspNowChallengeRequestV1 *)data; | ||
auto firmware_version = message->firmware_version; | ||
log("Got challenge request from 0x" + toHex(mac_address) + | ||
", firmware version: " + std::to_string(firmware_version), | ||
auto config_version = message->config_version; | ||
log("Got challenge request from 0x" + toHex(mac_address) + ", firmware version: " + | ||
std::to_string(firmware_version) + ", config version: " + std::to_string(config_version), | ||
ESP_LOG_INFO); | ||
handleChallengeRequest(mac_addr, message->challenge_challenge, firmware_version); | ||
handleChallengeRequest(mac_addr, message->challenge_challenge, firmware_version, config_version); | ||
break; | ||
} | ||
|
||
|
@@ -206,7 +208,8 @@ void EspNowHost::handleDiscoveryRequest(uint8_t *mac_addr, uint32_t discovery_ch | |
sendMessageToTemporaryPeer(mac_addr, &message, sizeof(EspNowDiscoveryResponseV1)); | ||
} | ||
|
||
void EspNowHost::handleChallengeRequest(uint8_t *mac_addr, uint32_t challenge_challenge, uint32_t firmware_version) { | ||
void EspNowHost::handleChallengeRequest(uint8_t *mac_addr, uint32_t challenge_challenge, uint32_t firmware_version, | ||
uint16_t config_version) { | ||
uint64_t mac_address = macToMac(mac_addr); | ||
|
||
// Any firmware to update? | ||
|
@@ -225,6 +228,20 @@ void EspNowHost::handleChallengeRequest(uint8_t *mac_addr, uint32_t challenge_ch | |
} | ||
} | ||
|
||
if (_config_update) { | ||
log("checking for config_update", ESP_LOG_INFO); | ||
auto metadata = _config_update(mac_address, config_version); | ||
if (metadata) { | ||
log("config update: version=" + std::to_string(metadata->version) + " len=" + std::to_string(metadata->length), | ||
ESP_LOG_INFO); | ||
EspNowChallengeConfigResponseV1 message; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment in
|
||
message.challenge_challenge = challenge_challenge; | ||
memcpy(&message.envelope, &metadata, sizeof(EspNowConfigEnvelope)); | ||
sendMessageToTemporaryPeer(mac_addr, &message, sizeof(EspNowChallengeConfigResponseV1)); | ||
return; | ||
} | ||
} | ||
|
||
// No firmware update (early return above) | ||
EspNowChallengeResponseV1 message; | ||
message.challenge_challenge = challenge_challenge; | ||
|
@@ -294,4 +311,4 @@ std::string EspNowHost::toHex(uint64_t i) { | |
std::stringstream sstream; | ||
sstream << std::hex << i; | ||
return sstream.str(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -211,6 +211,7 @@ class EspNowNode { | |||||
OnStatus _on_status; | ||||||
EspNowCrypt &_crypt; | ||||||
esp_netif_t *_netif_sta; | ||||||
uint16_t _config_version; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
uint32_t _firmware_version; | ||||||
bool _setup_successful = false; | ||||||
CrtBundleAttach _crt_bundle_attach; | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,17 @@ class EspNowPreferences : public EspNowNetwork::Preferences { | |
*/ | ||
bool espNowGetMacForHost(uint8_t *buffer) override; | ||
|
||
/** | ||
* @brief Set the config envelope that contains the application-specific config data. | ||
*/ | ||
bool setConfig(EspNowConfigEnvelope *config_envelope) override; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is application specific and I don't think this should be part of Preferences/EspNowPreferences. Nodes that need uses configuration and needs to persist it, can do it themselves. The interface There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see the support for configuration as something that is built in, and handled as conveniently as possible for the Node, and likely used by a large percentage of Node implementations. The Node already has to create an instance of EspNowPreferences to pass to the EspNowNode constructor, and has to call the initalizeNVS() function, so having a convenience method for getting/setting the config on this class seems like a reasonable place for it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jamienz We can include it in |
||
|
||
/** | ||
* @brief Get the config envelope that contains the application-specific config data. | ||
* @param config_envelope the EspNowConfigEnvelope to copy the data into | ||
*/ | ||
bool getConfig(EspNowConfigEnvelope *config_envelope) override; | ||
|
||
/** | ||
* @brief After setting variables, call this to commit/save. | ||
* @return true on success. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
#ifndef __ESP_NOW_I_PREFERENCES_H__ | ||
#define __ESP_NOW_I_PREFERENCES_H__ | ||
|
||
#include "esp-now-config.h" | ||
#include <cstdint> | ||
|
||
namespace EspNowNetwork { | ||
|
@@ -26,6 +27,9 @@ class Preferences { | |
*/ | ||
virtual bool espNowGetMacForHost(uint8_t *buffer) = 0; | ||
|
||
virtual bool getConfig(EspNowConfigEnvelope *config_envelope) = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Se comment in |
||
virtual bool setConfig(EspNowConfigEnvelope *config_envelope) = 0; | ||
|
||
/** | ||
* @brief Commit any changes written. | ||
* @return true on success. | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -85,7 +85,6 @@ bool EspNowNode::setup() { | |||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); | ||||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); | ||||||
ESP_ERROR_CHECK(esp_wifi_start()); | ||||||
|
||||||
// TODO(johboh): this might unset WIFI6 for ESP32-C6, but getting current protocols and appending WIFI_PROTOCOL_LR and | ||||||
// then setting them again, fails with bad argument. Presumably a bug in esp_wifi_set_protocol not supporting | ||||||
// WIFI_PROTOCOL_11AX? | ||||||
|
@@ -150,6 +149,7 @@ bool EspNowNode::setup() { | |||||
if (_on_status) { | ||||||
_on_status(Status::HOST_DISCOVERY_STARTED); | ||||||
} | ||||||
|
||||||
// Ok so we have no valid host MAC address. | ||||||
// Announce our precence until we get a reply. | ||||||
|
||||||
|
@@ -194,6 +194,13 @@ bool EspNowNode::setup() { | |||||
return false; | ||||||
} | ||||||
|
||||||
EspNowConfigEnvelope config_envelope; | ||||||
if (_preferences.getConfig(&config_envelope)) { | ||||||
_config_version = config_envelope.version; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pass configuration version in constructor as for firmware version. |
||||||
} else { | ||||||
_config_version = 0; | ||||||
} | ||||||
|
||||||
_setup_successful = success; | ||||||
return success; | ||||||
} | ||||||
|
@@ -224,6 +231,8 @@ bool EspNowNode::sendMessage(void *message, size_t message_size, int16_t retries | |||||
// The challenge we expect to get back in the challenge/firmware response. | ||||||
request.challenge_challenge = esp_random(); | ||||||
request.firmware_version = _firmware_version; | ||||||
request.config_version = _config_version; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
log("challenge firmare version=" + std::to_string(_firmware_version) + " config version=" + std::to_string(_config_version), ESP_LOG_DEBUG); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
// First, we must request the challenge to use. | ||||||
bool got_challange = false; | ||||||
|
@@ -267,6 +276,25 @@ bool EspNowNode::sendMessage(void *message, size_t message_size, int16_t retries | |||||
} | ||||||
break; | ||||||
} | ||||||
case MESSAGE_ID_CHALLENGE_CONFIG_RESPONSE_V1: { | ||||||
log("Got challenge update config response.", ESP_LOG_INFO); | ||||||
EspNowChallengeConfigResponseV1 *response = (EspNowChallengeConfigResponseV1 *)decrypted_data.get(); | ||||||
// Validate the challenge for the challenge request/response pair | ||||||
if (response->challenge_challenge == request.challenge_challenge) { | ||||||
EspNowConfigEnvelope config_envelope; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here instead let the node have an optional callback with a |
||||||
std::memcpy(&config_envelope, &response->envelope, sizeof(EspNowConfigEnvelope)); | ||||||
_preferences.setConfig(&config_envelope); | ||||||
_preferences.commit(); | ||||||
log("Saved config (version=" + std::to_string(config_envelope.version) + " ). Restarting.", ESP_LOG_INFO); | ||||||
esp_restart(); | ||||||
} else { | ||||||
log("Challenge mismatch for challenge request/ config response (expected: " + | ||||||
std::to_string(request.challenge_challenge) + | ||||||
", got: " + std::to_string(response->challenge_challenge) + ")", | ||||||
ESP_LOG_WARN); | ||||||
} | ||||||
break; | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
|
@@ -421,4 +449,4 @@ void EspNowNode::handleFirmwareUpdate(char *wifi_ssid, char *wifi_password, char | |||||
} | ||||||
vTaskDelay(1000 / portTICK_PERIOD_MS); | ||||||
esp_restart(); | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless you address and merge #3 first :).
Also remember to update the Esp-idf example versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Esp-idf host example doesn't set up a WiFi connection, so isn't a working example. Is that intentional? Should I add in the code to connect it to an access point, or just add in the changes to support this new configuration feature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it is, as I wanted to keep the examples simple and that is more code involved setting up WiFi using ESP-IDF. I should have added a "setup WiFi here" comment to make it more complete, as I have here.
When I myself find examples, I appreciate when the examples are as minimal as possible, and doesn't contains extras that is not actually needed. The extras can be in dedicated examples describing these extra features. Of course, as you say, without WiFi the node is not fully functional (thus the comment).
As you are using the Ardunio library (and not necessarily have the ESP-IDF framework setup), you can skip modifying the esp-idf examples, I can adjust them afterwards.