Trim app and recovery binaries

This commit is contained in:
Sebastien L
2023-10-11 12:36:17 -04:00
parent 9ebe717e74
commit 484d8c54a8
34 changed files with 1977 additions and 1126 deletions

View File

@@ -0,0 +1,124 @@
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "Batch.h"
#include "esp_event.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_ota_ops.h"
#include "esp_tls.h"
#include "nvs_flash.h"
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
#include "esp_crt_bundle.h"
#endif
#include "esp_system.h"
#include "http_handlers.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "nvs_utilities.h"
#include "tools.h"
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <sys/param.h>
#if CONFIG_WITH_METRICS
static const char* const TAG = "MetricsBatch";
static const char* const feature_evt_name = "$feature_flag_called";
static const char* const feature_flag_name = "$feature_flag";
static const char* const feature_flag_response_name = "$feature_flag_response";
namespace Metrics {
Event& Batch::add_feature_event() { return add_event(feature_evt_name); }
void Batch::add_remove_feature_event(const char* name, bool active) {
if (!active) {
remove_feature_event(name);
} else {
add_event(feature_evt_name).add_property(feature_flag_name, name);
}
}
Event& Batch::add_feature_variant_event(const char* const name, const char* const value) {
return add_event(feature_evt_name)
.add_property(feature_flag_name, name)
.add_property(feature_flag_response_name, value);
}
void Batch::remove_feature_event(const char* name) {
for (Metrics::Event& e : _events) {
if (strcmp(e.get_name(), feature_evt_name) == 0) {
e.remove_property(feature_flag_name, name);
return;
}
}
}
cJSON* Batch::to_json() {
cJSON* batch_json = cJSON_CreateArray();
for (Metrics::Event& e : _events) {
cJSON_AddItemToArray(batch_json, e.to_json(_metrics_uid.c_str()));
}
cJSON* message = cJSON_CreateObject();
cJSON_AddItemToObject(message, "batch", batch_json);
cJSON_AddStringToObject(message, "api_key", _api_key);
return batch_json;
}
char* Batch::to_json_str() {
cJSON* json = to_json();
char* json_str = cJSON_PrintUnformatted(json);
cJSON_Delete(json);
return json_str;
}
void Batch::push() {
int status_code = 0;
if (_metrics_uid.empty() && !_warned) {
ESP_LOGW(TAG, "Metrics disabled; no CID found");
_warned = true;
return;
}
char* json_str = to_json_str();
ESP_LOGV(TAG, "Metrics payload: %s", json_str);
time_t start_time = millis();
status_code = metrics_http_post_request(json_str, _url);
if (status_code == 200 || status_code == 204) {
_events.clear();
}
FREE_AND_NULL(json_str)
ESP_LOGD(TAG, "Total duration for metrics call: %lu. ", millis() - start_time);
}
void Batch::build_guid() {
uint8_t raw[16];
std::ostringstream oss;
esp_fill_random(raw, 16);
std::for_each(std::begin(raw), std::end(raw), [&oss](const uint8_t& byte) {
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(byte);
});
_metrics_uid = oss.str();
}
void Batch::assign_id() {
size_t size = 0;
esp_err_t esp_err = ESP_OK;
_metrics_uid = std::string((char*)get_nvs_value_alloc_for_partition(
NVS_DEFAULT_PART_NAME, TAG, NVS_TYPE_BLOB, "cid", &size));
if (_metrics_uid[0] == 'G') {
ESP_LOGW(TAG, "Invalid ID. %s", _metrics_uid.c_str());
_metrics_uid.clear();
}
if (_metrics_uid.empty()) {
build_guid();
if (_metrics_uid.empty()) {
ESP_LOGE(TAG, "ID Failed");
return;
}
ESP_LOGW(TAG, "Metrics ID: %s", _metrics_uid.c_str());
esp_err = store_nvs_value_len_for_partition(NVS_DEFAULT_PART_NAME, TAG, NVS_TYPE_BLOB,
"cid", _metrics_uid.c_str(), _metrics_uid.length() + 1);
if (esp_err != ESP_OK) {
ESP_LOGE(TAG, "Store ID failed: %s", esp_err_to_name(esp_err));
}
}
}
} // namespace Metrics
#endif

View File

@@ -0,0 +1,46 @@
#pragma once
#include "Events.h"
#include <string>
#ifdef __cplusplus
namespace Metrics {
extern "C" {
#endif
#ifdef __cplusplus
class Batch {
private:
std::list<Event> _events;
bool _warned = false;
std::string _metrics_uid = nullptr;
const char* _api_key = nullptr;
const char* _url = nullptr;
void build_guid();
void assign_id();
public:
Batch() = default;
void configure(const char* api_key, const char* url) {
_api_key = api_key;
_url = url;
assign_id();
}
Event& add_feature_event();
void add_remove_feature_event(const char* name, bool active);
Event& add_feature_variant_event(const char* const name, const char* const value);
Event& add_event(const char* name) {
_events.emplace_back(name);
return _events.back();
}
bool has_events() const { return !_events.empty(); }
void remove_feature_event(const char* name);
cJSON* to_json();
char* to_json_str();
void push();
};
}
#endif
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,5 @@
idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
REQUIRES json tools platform_config wifi-manager esp-tls platform_config
PRIV_REQUIRES esp32 freertos
)

View File

@@ -0,0 +1,98 @@
#include "Events.h"
#include <algorithm>
#include "esp_app_format.h"
#include "esp_ota_ops.h"
#if CONFIG_WITH_METRICS
static const char* const TAG = "MetricsEvent";
namespace Metrics {
Event& Event::add_property(const char* name, const char* value) {
ESP_LOGV(TAG, "Adding property %s:%s to event %s",name,value,_name);
char* mutable_name = strdup_psram(name); // Cast away const-ness, be careful with this
auto elem = properties.find(mutable_name);
FREE_AND_NULL(mutable_name)
if (elem == properties.end()) {
ESP_LOGV(TAG, "Adding property %s:%s to event %s",name,value,_name);
properties.insert({strdup_psram(name), strdup_psram(value)});
} else {
ESP_LOGV(TAG, "Replacing value for property %s. Old: %s New: %s, Event: %s",name,elem->second,value,name);
FREE_AND_NULL(elem->second)
elem->second = strdup_psram(value);
}
return *this;
}
bool Event::has_property_value(const char* name, const char* value) const {
ESP_LOGV(TAG, "Checking if event %s property %s has value %s",_name, name,value);
return std::any_of(properties.begin(), properties.end(),
[name, value](const std::pair<const char* const, char*>& kv) {
ESP_LOGV(TAG, "Found property %s=%s", name,value);
return strcmp(kv.first, name) == 0 && strcmp(kv.second, value) == 0;
});
}
void Event::remove_property(const char* name, const char* value) {
auto it = properties.begin();
ESP_LOGV(TAG, "Removing event %s property %s=%s",_name, name,value);
while (it != properties.end()) {
if (strcmp(it->first, name) == 0 && strcmp(it->second, value)) {
properties.erase(it);
return;
}
}
ESP_LOGV(TAG, "Property %s=%s not found.", name,value);
}
cJSON* Event::properties_to_json() {
ESP_LOGV(TAG, "Event %s properties to json.",_name);
const esp_app_desc_t* desc = esp_ota_get_app_description();
#ifdef CONFIG_FW_PLATFORM_NAME
const char* platform = CONFIG_FW_PLATFORM_NAME;
#else
const char* platform = desc->project_name;
#endif
cJSON* prop_json = cJSON_CreateObject();
auto it = properties.begin();
while (it != properties.end()) {
cJSON_AddStringToObject(prop_json, it->first, it->second);
++it;
}
cJSON_AddStringToObject(prop_json, "platform", platform);
cJSON_AddStringToObject(prop_json, "build", desc->version);
dump_json_content("User properties for event:", prop_json, ESP_LOG_VERBOSE);
return prop_json;
}
cJSON* Event::to_json(const char* distinct_id) {
// The target structure looks like this
// {
// "event": "batched_event_name_1",
// "properties": {
// "distinct_id": "user distinct id",
// "account_type": "pro"
// },
// "timestamp": "[optional timestamp in ISO 8601 format]"
// }
ESP_LOGV(TAG,"Event %s to json",_name);
free_json();
_json = cJSON_CreateObject();
cJSON_AddStringToObject(_json, "name", _name);
cJSON_AddItemToObject(_json, "properties", properties_to_json());
char buf[26] = {};
strftime(buf, sizeof(buf), "%FT%TZ", gmtime(&_time));
// this will work too, if your compiler doesn't support %F or %T:
// strftime(buf, sizeof buf, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
cJSON_AddStringToObject(_json, "timestamp", buf);
cJSON* prop_json = properties_to_json();
cJSON_AddStringToObject(prop_json, "distinct_id", distinct_id);
dump_json_content("Full Event:", _json, ESP_LOG_VERBOSE);
return _json;
}
void Event::free_json() { cJSON_Delete(_json); }
void Event::update_time() {
if (_time == 0) {
_time = time(nullptr);
}
}
} // namespace Metrics
#endif

View File

@@ -0,0 +1,53 @@
#pragma once
#ifdef __cplusplus
#include "esp_log.h"
#include "tools.h"
#include <cJSON.h>
#include <ctime>
#include <list>
#include <map>
#include <stdio.h>
#include <string.h>
#include <string>
namespace Metrics {
struct StrCompare {
bool operator()(const char* a, const char* b) const { return strcmp(a, b) < 0; }
};
class Event {
public:
std::map<char*, char*, StrCompare> properties;
Event& add_property(const char* name, const char* value);
bool has_property_value(const char* name, const char* value) const;
void remove_property(const char* name, const char* value);
cJSON* properties_to_json();
cJSON* to_json(const char* distinct_id);
void free_json();
void update_time();
explicit Event(const char* name) {
_name = strdup_psram(name);
memset(&_time, 0x00, sizeof(_time));
}
const char* get_name() const { return _name; }
~Event() {
FREE_AND_NULL(_name);
// Iterate through the map and free the elements
for (auto& kv : properties) {
free((void*)kv.first);
free(kv.second);
}
properties.clear(); // Clear the map after freeing memory
FREE_AND_NULL(_json);
}
private:
char* _name = nullptr;
time_t _time;
cJSON* _json = nullptr;
};
} // namespace Metrics
#endif

View File

@@ -0,0 +1,148 @@
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "Metrics.h"
#include "Batch.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_ota_ops.h"
#include "esp_system.h"
#include "esp_tls.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "tools.h"
#include <cstdarg>
#include <cstdio>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include "cJSON.h"
#include "freertos/timers.h"
#include "network_manager.h"
#include "platform_config.h"
static const char* TAG = "metrics";
#if CONFIG_WITH_METRICS
extern bool is_network_connected();
#define METRICS_CLIENT_ID_LEN 50
#define MAX_HTTP_RECV_BUFFER 512
static bool metrics_usage_gen = false;
static time_t metrics_usage_gen_time = 0;
#ifndef METRICS_API_KEY
#pragma message "Metrics API key needs to be passed from the environment"
#define METRICS_API_KEY "ZZZ"
#endif
static const char* metrics_api_key =
static const char* parms_str = "params";
static const char* properties_str = "properties";
static const char* user_properties_str = "user_properties";
static const char* items_str = "items";
static const char* quantity_str = "quantity";
static const char* metrics_url = "https://app.posthog.com";
static TimerHandle_t timer;
extern cJSON* get_cmd_list();
Metrics::Batch batch;
static void metrics_timer_cb(void* timer_id) {
if (batch.has_events()) {
if (!is_network_connected()) {
ESP_LOGV(TAG, "Network not connected. can't flush");
} else {
ESP_LOGV(TAG, "Pushing events");
batch.push();
}
}
if (millis() > metrics_usage_gen_time && !metrics_usage_gen) {
metrics_usage_gen = true;
ESP_LOGV(TAG, "Generate command list to pull features");
cJSON* cmdlist = get_cmd_list();
dump_json_content("generated cmd list", cmdlist, ESP_LOG_VERBOSE);
cJSON_Delete(cmdlist);
}
}
void metrics_init() {
ESP_LOGV(TAG, "Initializing metrics");
batch.configure(metrics_api_key, metrics_url);
if (!timer) {
ESP_LOGE(TAG, "Metrics Timer failure");
} else {
ESP_LOGV(TAG, "Starting timer");
xTimerStart(timer, portMAX_DELAY);
}
// set a 20 seconds delay before generating the
// features so the system has time to boot
metrics_usage_gen_time = millis() + 20000;
}
void metrics_event_playback(const char* source) {
ESP_LOGV(TAG, "Playback event: %s", source);
auto event = batch.add_event("play").add_property("source", source);
}
void metrics_event_boot(const char* partition) {
ESP_LOGV(TAG, "Boot event %s", partition);
auto event = batch.add_event("start");
event.add_property("partition", partition);
}
void metrics_add_feature_variant(const char* name, const char* format, ...) {
va_list args;
ESP_LOGV(TAG, "Feature %s", name);
va_start(args, format);
// Determine the required buffer size
int size = vsnprintf(nullptr, 0, format, args);
va_end(args); // Reset the va_list
// Allocate buffer and format the string
std::vector<char> buffer(size + 1); // +1 for the null-terminator
va_start(args, format);
vsnprintf(buffer.data(), buffer.size(), format, args);
va_end(args);
// Now buffer.data() contains the formatted string
batch.add_feature_variant_event(name, buffer.data());
}
void metrics_add_feature(const char* name, bool active) {
ESP_LOGV(TAG, "Adding feature %s: %s", name, active ? "ACTIVE" : "INACTIVE");
batch.add_remove_feature_event(name, active);
}
void metrics_event(const char* name) {
ESP_LOGV(TAG, "Adding Event %s", name);
batch.add_event(name);
}
#else
static const char * not_enabled = " - (metrics not enabled, this is just marking where the call happens)";
void metrics_init(){
#pragma message("Metrics disabled")
ESP_LOGD(TAG,"Metrics init%s",not_enabled);
}
void metrics_event_boot(const char* partition){
ESP_LOGD(TAG,"Metrics Event Boot from partition %s%s",partition,not_enabled);
}
void metrics_event(const char* name){
ESP_LOGD(TAG,"Metrics Event %s%s",name,not_enabled);
}
void metrics_add_feature(const char* name, bool active) {
ESP_LOGD(TAG,"Metrics add feature %s%s%s",name,active?"ACTIVE":"INACTIVE",not_enabled);
}
void metrics_add_feature_variant(const char* name, const char* format, ...){
va_list args;
ESP_LOGV(TAG, "Feature %s", name);
va_start(args, format);
// Determine the required buffer size
int size = vsnprintf(nullptr, 0, format, args);
va_end(args); // Reset the va_list
// Allocate buffer and format the string
std::vector<char> buffer(size + 1); // +1 for the null-terminator
va_start(args, format);
vsnprintf(buffer.data(), buffer.size(), format, args);
va_end(args);
ESP_LOGD(TAG,"Metrics add feature %s variant %s%s",name,buffer.data(),not_enabled);
}
#endif

View File

@@ -0,0 +1,18 @@
#pragma once
#include <stdbool.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
void metrics_event_playback(const char* source);
void metrics_event_boot(const char* partition);
void metrics_event(const char* name);
void metrics_add_feature(const char* name, bool active);
void metrics_add_feature_variant(const char* name, const char* format, ...);
void metrics_init();
void metrics_flush();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,163 @@
#include "http_handlers.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_tls.h"
#include "tools.h"
#include <sys/param.h>
#if CONFIG_WITH_METRICS
static const char* TAG = "metrics_http";
static char* output_buffer; // Buffer to store response of http request from
// event handler
static int output_len = 0; // Stores number of bytes read
#define MAX_HTTP_OUTPUT_BUFFER 2048
// Common function signature for event handlers
typedef void (*HttpEventHandler)(esp_http_client_event_t* evt);
static void handle_http_error(esp_http_client_event_t* evt) { ESP_LOGV(TAG, "ERROR"); }
static void handle_http_connected(esp_http_client_event_t* evt) {
ESP_LOGV(TAG, "ON_CONNECTED");
}
static void handle_http_header_sent(esp_http_client_event_t* evt) {
ESP_LOGV(TAG, "HEADER_SENT");
}
static void handle_http_on_header(esp_http_client_event_t* evt) {
ESP_LOGV(TAG, "ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
}
static void handle_http_on_data(esp_http_client_event_t* evt) {
ESP_LOGV(TAG, "ON_DATA, len=%d", evt->data_len);
ESP_LOGV(TAG, "ON_DATA, len=%d", evt->data_len);
// Clean the buffer in case of a new request
if (output_len == 0 && evt->user_data) {
// we are just starting to copy the output data into the use
ESP_LOGV(TAG, "Resetting buffer");
memset(evt->user_data, 0, MAX_HTTP_OUTPUT_BUFFER);
}
/*
* Check for chunked encoding is added as the URL for chunked encoding used in this example
* returns binary data. However, event handler can also be used in case chunked encoding is
* used.
*/
// If user_data buffer is configured, copy the response into the buffer
int copy_len = 0;
if (evt->user_data) {
ESP_LOGV(TAG, "Not Chunked response, with user data");
// The last byte in evt->user_data is kept for the NULL character in
// case of out-of-bound access.
copy_len = MIN(evt->data_len, (MAX_HTTP_OUTPUT_BUFFER - output_len));
if (copy_len) {
memcpy(evt->user_data + output_len, evt->data, copy_len);
}
} else {
int content_len = esp_http_client_get_content_length(evt->client);
if (esp_http_client_is_chunked_response(evt->client)) {
esp_http_client_get_chunk_length(evt->client, &content_len);
}
if (output_buffer == NULL) {
// We initialize output_buffer with 0 because it is used by
// strlen() and similar functions therefore should be null
// terminated.
size_t len=(content_len + 1) * sizeof(char);
ESP_LOGV(TAG, "Init buffer %d",len);
output_buffer = (char*)malloc_init_external(len);
output_len = 0;
if (output_buffer == NULL) {
ESP_LOGE(TAG, "Buffer alloc failed.");
return;
}
}
copy_len = MIN(evt->data_len, (content_len - output_len));
if (copy_len) {
memcpy(output_buffer + output_len, evt->data, copy_len);
}
}
output_len += copy_len;
}
static void handle_http_on_finish(esp_http_client_event_t* evt) {
ESP_LOGD(TAG, "ON_FINISH");
if (output_buffer != NULL) {
ESP_LOGV(TAG, "Response: %s", output_buffer);
free(output_buffer);
output_buffer = NULL;
}
output_len = 0;
}
static void handle_http_disconnected(esp_http_client_event_t* evt) {
ESP_LOGI(TAG, "DISCONNECTED");
int mbedtls_err = 0;
esp_err_t err =
esp_tls_get_and_clear_last_error((esp_tls_error_handle_t)evt->data, &mbedtls_err, NULL);
if (err != 0) {
ESP_LOGI(TAG, "Last error : %s", esp_err_to_name(err));
ESP_LOGI(TAG, "Last mbedtls err 0x%x", mbedtls_err);
}
if (output_buffer != NULL) {
free(output_buffer);
output_buffer = NULL;
}
output_len = 0;
}
static const HttpEventHandler eventHandlers[] = {
handle_http_error, // HTTP_EVENT_ERROR
handle_http_connected, // HTTP_EVENT_ON_CONNECTED
handle_http_header_sent, // HTTP_EVENT_HEADERS_SENT
handle_http_header_sent, // HTTP_EVENT_HEADER_SENT (alias for HTTP_EVENT_HEADERS_SENT)
handle_http_on_header, // HTTP_EVENT_ON_HEADER
handle_http_on_data, // HTTP_EVENT_ON_DATA
handle_http_on_finish, // HTTP_EVENT_ON_FINISH
handle_http_disconnected // HTTP_EVENT_DISCONNECTED
};
esp_err_t metrics_http_event_handler(esp_http_client_event_t* evt) {
if (evt->event_id < 0 || evt->event_id >= sizeof(eventHandlers) / sizeof(eventHandlers[0])) {
ESP_LOGE(TAG, "Invalid event ID: %d", evt->event_id);
return ESP_FAIL;
}
eventHandlers[evt->event_id](evt);
return ESP_OK;
}
int metrics_http_post_request(const char* payload, const char* url) {
int status_code = 0;
esp_http_client_config_t config = {.url = url,
.disable_auto_redirect = false,
.event_handler = metrics_http_event_handler,
.transport_type = HTTP_TRANSPORT_OVER_SSL,
.user_data = NULL, // local_response_buffer, // Pass address of
// local buffer to get response
.skip_cert_common_name_check = true
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_set_method(client, HTTP_METHOD_POST);
if (err == ESP_OK) {
err = esp_http_client_set_header(client, "Content-Type", "application/json");
}
if (err == ESP_OK) {
ESP_LOGV(TAG, "Setting payload: %s", payload);
err = esp_http_client_set_post_field(client, payload, strlen(payload));
}
if (err == ESP_OK) {
err = esp_http_client_perform(client);
}
if (err == ESP_OK) {
status_code = esp_http_client_get_status_code(client);
ESP_LOGD(TAG, "metrics call Status = %d, content_length = %d",
esp_http_client_get_status_code(client), esp_http_client_get_content_length(client));
} else {
status_code = 500;
ESP_LOGW(TAG, "metrics call Status failed: %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
return status_code;
}
#endif

View File

@@ -0,0 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
int metrics_http_post_request(const char* payload, const char* url);
#ifdef __cplusplus
}
#endif

View File

@@ -13,14 +13,14 @@ esp_err_t store_nvs_value_len(nvs_type_t type, const char *key, void * data, siz
esp_err_t store_nvs_value(nvs_type_t type, const char *key, void * data);
esp_err_t get_nvs_value(nvs_type_t type, const char *key, void*value, const uint8_t buf_size);
void * get_nvs_value_alloc(nvs_type_t type, const char *key);
void * get_nvs_value_alloc_for_partition(const char * partition,const char * ns,nvs_type_t type, const char *key, size_t * size);
esp_err_t erase_nvs_for_partition(const char * partition, const char * ns,const char *key);
esp_err_t store_nvs_value_len_for_partition(const char * partition,const char * ns,nvs_type_t type, const char *key, const void * data,size_t data_len);
void * get_nvs_value_alloc_for_partition(const char * partition,const char * name_space,nvs_type_t type, const char *key, size_t * size);
esp_err_t erase_nvs_for_partition(const char * partition, const char * name_space,const char *key);
esp_err_t store_nvs_value_len_for_partition(const char * partition,const char * name_space,nvs_type_t type, const char *key, const void * data,size_t data_len);
esp_err_t erase_nvs(const char *key);
void print_blob(const char *blob, size_t len);
const char *type_to_str(nvs_type_t type);
nvs_type_t str_to_type(const char *type);
esp_err_t erase_nvs_partition(const char * partition, const char * ns);
esp_err_t erase_nvs_partition(const char * partition, const char * name_space);
void erase_settings_partition();
#ifdef __cplusplus
}

View File

@@ -634,7 +634,7 @@ cJSON * config_alloc_get_cjson(const char *key){
}
return conf_json;
}
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value){
esp_err_t config_set_cjson(const char *key, cJSON *value, bool free_cjson){
char * value_str = cJSON_PrintUnformatted(value);
if(value_str==NULL){
ESP_LOGE(TAG, "Unable to print cJSON for key [%s]", key);
@@ -642,9 +642,14 @@ esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value){
}
esp_err_t err = config_set_value(NVS_TYPE_STR,key, value_str);
free(value_str);
cJSON_Delete(value);
if(free_cjson){
cJSON_Delete(value);
}
return err;
}
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value){
return config_set_cjson(key, value, true);
}
void config_get_uint16t_from_str(const char *key, uint16_t *value, uint16_t default_value){
char * str_value = config_alloc_get(NVS_TYPE_STR, key);
if(str_value == NULL){
@@ -786,6 +791,44 @@ cJSON* cjson_update_number(cJSON** root, const char* key, int value) {
}
return *root;
}
bool config_parse_param_int(const char * config,const char * param, char delimiter,int * value){
const char *p;
if(!value){
return false;
}
if ((p = strcasestr(config, param)) && (p = strchr(p, delimiter))) {
*value = atoi(p+1);
return true;
}
return false;
}
bool config_parse_param_float(const char * config,const char * param, char delimiter,double * value){
const char *p;
if(!value){
return false;
}
if ((p = strcasestr(config, param)) && (p = strchr(p, delimiter))) {
*value = atof(p+1);
return true;
}
return false;
}
bool config_parse_param_str(const char *source, const char *param, char delimiter, char *value, size_t value_size) {
char *p;
if ((p = strstr(source, param)) && (p = strchr(p, delimiter))) {
while (*++p == ' '); // Skip spaces
// Read the value into the buffer, making sure not to overflow
snprintf(value, value_size, "%s", p);
char *end = strchr(value, ',');
if (end) {
*end = '\0'; // Null-terminate at the comma, if found
}
return true;
}
return false;
}
IMPLEMENT_SET_DEFAULT(uint8_t,NVS_TYPE_U8);
IMPLEMENT_SET_DEFAULT(int8_t,NVS_TYPE_I8);
IMPLEMENT_SET_DEFAULT(uint16_t,NVS_TYPE_U16);

View File

@@ -8,25 +8,30 @@
#ifdef __cplusplus
extern "C" {
#endif
#define PARSE_WITH_FUNC 1
#ifdef PARSE_WITH_FUNC
#define PARSE_PARAM(S,P,C,V) config_parse_param_int(S,P,C,(int*)&V)
#define PARSE_PARAM_STR(S,P,C,V,I) config_parse_param_str(S,P,C,V,I)
#define PARSE_PARAM_FLOAT(S,P,C,V) config_parse_param_float(S,P,C,&V)
#else
#define PARSE_PARAM(S,P,C,V) do { \
char *__p; \
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \
} while (0)
#define PARSE_PARAM(S,P,C,V) do { \
char *__p; \
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \
} while (0)
#define PARSE_PARAM_FLOAT(S,P,C,V) do { \
char *__p; \
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atof(__p+1); \
} while (0)
#define PARSE_PARAM_STR(S,P,C,V,I) do { \
char *__p; \
if ((__p = strstr(S, P)) && (__p = strchr(__p, C))) { \
while (*++__p == ' '); \
sscanf(__p,"%" #I "[^,]", V); \
} \
} while (0)
#define PARSE_PARAM_FLOAT(S,P,C,V) do { \
char *__p; \
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atof(__p+1); \
} while (0)
#define PARSE_PARAM_STR(S,P,C,V,I) do { \
char *__p; \
if ((__p = strstr(S, P)) && (__p = strchr(__p, C))) { \
while (*++__p == ' '); \
sscanf(__p,"%" #I "[^,]", V); \
} \
} while (0)
#endif
#define DECLARE_SET_DEFAULT(t) void config_set_default_## t (const char *key, t value);
#define DECLARE_GET_NUM(t) esp_err_t config_get_## t (const char *key, t * value);
#ifndef FREE_RESET
@@ -50,10 +55,14 @@ bool config_has_changes();
void config_commit_to_nvs();
void config_start_timer();
void config_init();
bool config_parse_param_int(const char * config,const char * param, char delimiter,int * value);
bool config_parse_param_float(const char * config,const char * param, char delimiter,double * value);
bool config_parse_param_str(const char *source, const char *param, char delimiter, char *value, size_t value_size);
void * config_alloc_get_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
void * config_alloc_get_str(const char *key, char *lead, char *fallback);
cJSON * config_alloc_get_cjson(const char *key);
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value);
esp_err_t config_set_cjson(const char *key, cJSON *value, bool free_cjson);
void config_get_uint16t_from_str(const char *key, uint16_t *value, uint16_t default_value);
void config_delete_key(const char *key);
void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);

View File

@@ -8,7 +8,7 @@ idf_component_register( SRCS
cmd_config.c
INCLUDE_DIRS .
REQUIRES nvs_flash
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite tools)
PRIV_REQUIRES console app_update tools services spi_flash platform_config vfs pthread wifi-manager platform_config newlib telnet display squeezelite tools metrics)
set_source_files_properties(cmd_config.c
PROPERTIES COMPILE_FLAGS

View File

@@ -1,6 +1,6 @@
idf_component_register( SRC_DIRS .
INCLUDE_DIRS .
PRIV_REQUIRES bootloader_support
PRIV_REQUIRES bootloader_support json
)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--undefined=esp_app_desc")

View File

@@ -3,9 +3,10 @@
#include "application_name.h"
#include "esp_err.h"
#include "esp_app_format.h"
#include "cJSON.h"
#include "stdbool.h"
extern esp_err_t process_recovery_ota(const char * bin_url, char * bin_buffer, uint32_t length);
extern cJSON * gpio_list;
const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
.magic_word = ESP_APP_DESC_MAGIC_WORD,
.version = PROJECT_VER,
@@ -26,7 +27,12 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
.date = "",
#endif
};
cJSON * get_gpio_list(bool refresh){
if(!gpio_list){
gpio_list = cJSON_CreateArray();
}
return gpio_list;
}
void register_optional_cmd(void) {
}

View File

@@ -43,12 +43,25 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
extern void register_audio_config(void);
extern void register_rotary_config(void);
extern void register_ledvu_config(void);
extern void register_nvs();
extern cJSON * get_gpio_list_handler(bool refresh);
void register_optional_cmd(void) {
#if CONFIG_WITH_CONFIG_UI
register_rotary_config();
register_ledvu_config();
#endif
register_audio_config();
}
register_ledvu_config();
register_nvs();
}
cJSON * get_gpio_list(bool refresh){
#if CONFIG_WITH_CONFIG_UI
return get_gpio_list_handler(refresh);
#else
return cJSON_CreateArray();
#endif
}
extern int squeezelite_main(int argc, char **argv);

View File

@@ -20,7 +20,10 @@
#include "tools.h"
#include "cJSON.h"
#include "cmd_i2ctools.h"
#if defined(CONFIG_WITH_METRICS)
#include "metrics.h"
#endif
#include "cmd_system.h"
const char * desc_squeezelite ="Squeezelite Options";
const char * desc_dac= "DAC Options";
const char * desc_cspotc= "Spotify (cSpot) Options";
@@ -330,9 +333,8 @@ static int do_bt_source_cmd(int argc, char **argv){
char *buf = NULL;
size_t buf_size = 0;
// char value[100] ={0};
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
@@ -441,9 +443,8 @@ static int do_audio_cmd(int argc, char **argv){
int nerrors = arg_parse(argc, argv,(void **)&audio_args);
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
@@ -529,9 +530,8 @@ static int do_spdif_cmd(int argc, char **argv){
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
@@ -568,9 +568,8 @@ static int do_rotary_cmd(int argc, char **argv){
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
@@ -640,9 +639,8 @@ static int do_cspot_config(int argc, char **argv){
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
@@ -699,9 +697,8 @@ static int do_ledvu_cmd(int argc, char **argv){
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
@@ -759,10 +756,8 @@ static int do_i2s_cmd(int argc, char **argv)
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
ESP_LOGE(TAG, "do_i2s_cmd: Failed to open memstream");
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
@@ -873,7 +868,9 @@ cJSON * i2s_cb(){
cJSON * values = cJSON_CreateObject();
const i2s_platform_config_t * i2s_conf= config_dac_get( );
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("i2s",i2s_conf->pin.data_out_num>=0);
#endif
if(i2s_conf->pin.bck_io_num>0 ) {
cJSON_AddNumberToObject(values,i2s_args.clock->hdr.longopts,i2s_conf->pin.bck_io_num);
}
@@ -910,6 +907,11 @@ cJSON * i2s_cb(){
cJSON * spdif_cb(){
cJSON * values = cJSON_CreateObject();
const i2s_platform_config_t * spdif_conf= config_spdif_get( );
if(spdif_conf->pin.data_out_num>=0) {
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("spdif","enabled");
#endif
}
if(spdif_conf->pin.bck_io_num>0 ) {
cJSON_AddNumberToObject(values,"clock",spdif_conf->pin.bck_io_num);
}
@@ -928,7 +930,9 @@ cJSON * rotary_cb(){
bool raw_mode = p && (*p == '1' || *p == 'Y' || *p == 'y');
free(p);
const rotary_struct_t *rotary= config_rotary_get();
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("rotary",GPIO_IS_VALID_GPIO(rotary->A ));
#endif
if(GPIO_IS_VALID_GPIO(rotary->A ) && rotary->A>=0 && GPIO_IS_VALID_GPIO(rotary->B) && rotary->B>=0){
cJSON_AddNumberToObject(values,rotary_args.A->hdr.longopts,rotary->A);
cJSON_AddNumberToObject(values,rotary_args.B->hdr.longopts,rotary->B);
@@ -947,7 +951,11 @@ cJSON * rotary_cb(){
cJSON * ledvu_cb(){
cJSON * values = cJSON_CreateObject();
const ledvu_struct_t *ledvu= config_ledvu_get();
if(GPIO_IS_VALID_GPIO(ledvu->gpio )){
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("led_vu","enabled");
#endif
}
if(GPIO_IS_VALID_GPIO(ledvu->gpio) && ledvu->gpio>=0 && ledvu->length > 0){
cJSON_AddNumberToObject(values,"gpio",ledvu->gpio);
cJSON_AddNumberToObject(values,"length",ledvu->length);
@@ -965,8 +973,14 @@ cJSON * audio_cb(){
cJSON * values = cJSON_CreateObject();
char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
cJSON_AddStringToObject(values,"jack_behavior",(strcmp(p,"1") == 0 ||strcasecmp(p,"y") == 0)?"Headphones":"Subwoofer");
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("jack_mute",atoi(p)>=0);
#endif
FREE_AND_NULL(p);
p = config_alloc_get_default(NVS_TYPE_STR, "loudness", "0", 0);
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("loudness",atoi(p)>=0);
#endif
cJSON_AddStringToObject(values,"loudness",p);
FREE_AND_NULL(p);
return values;
@@ -976,6 +990,9 @@ cJSON * bt_source_cb(){
char * p = config_alloc_get_default(NVS_TYPE_STR, "a2dp_sink_name", NULL, 0);
if(p){
cJSON_AddStringToObject(values,"sink_name",p);
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("btsource",strlen(p)>0);
#endif
}
FREE_AND_NULL(p);
// p = config_alloc_get_default(NVS_TYPE_STR, "a2dp_ctmt", NULL, 0);
@@ -1026,9 +1043,8 @@ static int do_squeezelite_cmd(int argc, char **argv)
int nerrors = arg_parse_msg(argc, argv,(struct arg_hdr ** )&squeezelite_args);
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
fprintf(f,"Not yet implemented!");
@@ -1047,59 +1063,62 @@ cJSON * squeezelite_cb(){
char *buf = NULL;
size_t buf_size = 0;
int nerrors=1;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
log_send_messaging(MESSAGING_ERROR,"Unable to parse squeezelite parameters");
return values;
}
if(nvs_config && strlen(nvs_config)>0){
ESP_LOGD(TAG,"Parsing command %s",nvs_config);
argv = (char **) calloc(22, sizeof(char *));
if (argv == NULL) {
FREE_AND_NULL(nvs_config);
fclose(f);
return values;
}
size_t argc = esp_console_split_argv(nvs_config, argv,22);
if (argc != 0) {
nerrors = arg_parse(argc, argv,(void **)&squeezelite_args);
ESP_LOGD(TAG,"Parsing completed");
}
}
if (nerrors == 0) {
get_str_parm_json(squeezelite_args.buffers, values);
get_str_parm_json(squeezelite_args.codecs, values);
get_lit_parm_json(squeezelite_args.header_format, values);
get_str_parm_json(squeezelite_args.log_level, values);
// get_str_parm_json(squeezelite_args.log_level_all, values);
// get_str_parm_json(squeezelite_args.log_level_decode, values);
// get_str_parm_json(squeezelite_args.log_level_output, values);
// get_str_parm_json(squeezelite_args.log_level_slimproto, values);
// get_str_parm_json(squeezelite_args.log_level_stream, values);
get_str_parm_json(squeezelite_args.mac_addr, values);
get_str_parm_json(squeezelite_args.output_device, values);
#if defined(CONFIG_WITH_METRICS)
if(squeezelite_args.output_device->sval[0]!=NULL && strlen(squeezelite_args.output_device->sval[0])>0){
metrics_add_feature_variant("output",squeezelite_args.output_device->sval[0]);
}
#endif
get_str_parm_json(squeezelite_args.model_name, values);
get_str_parm_json(squeezelite_args.name, values);
get_int_parm_json(squeezelite_args.rate, values);
get_str_parm_json(squeezelite_args.rates, values);
get_str_parm_json(squeezelite_args.server, values);
get_int_parm_json(squeezelite_args.timeout, values);
char * p = cJSON_Print(values);
ESP_LOGD(TAG,"%s",p);
free(p);
}
else {
if(nvs_config && strlen(nvs_config)>0){
ESP_LOGD(TAG,"Parsing command %s",nvs_config);
argv = (char **) calloc(22, sizeof(char *));
if (argv == NULL) {
FREE_AND_NULL(nvs_config);
fclose(f);
return values;
}
size_t argc = esp_console_split_argv(nvs_config, argv,22);
if (argc != 0) {
nerrors = arg_parse(argc, argv,(void **)&squeezelite_args);
ESP_LOGD(TAG,"Parsing completed");
}
}
if (nerrors == 0) {
get_str_parm_json(squeezelite_args.buffers, values);
get_str_parm_json(squeezelite_args.codecs, values);
get_lit_parm_json(squeezelite_args.header_format, values);
get_str_parm_json(squeezelite_args.log_level, values);
// get_str_parm_json(squeezelite_args.log_level_all, values);
// get_str_parm_json(squeezelite_args.log_level_decode, values);
// get_str_parm_json(squeezelite_args.log_level_output, values);
// get_str_parm_json(squeezelite_args.log_level_slimproto, values);
// get_str_parm_json(squeezelite_args.log_level_stream, values);
get_str_parm_json(squeezelite_args.mac_addr, values);
get_str_parm_json(squeezelite_args.output_device, values);
get_str_parm_json(squeezelite_args.model_name, values);
get_str_parm_json(squeezelite_args.name, values);
get_int_parm_json(squeezelite_args.rate, values);
get_str_parm_json(squeezelite_args.rates, values);
get_str_parm_json(squeezelite_args.server, values);
get_int_parm_json(squeezelite_args.timeout, values);
char * p = cJSON_Print(values);
ESP_LOGD(TAG,"%s",p);
free(p);
}
else {
arg_print_errors(f, squeezelite_args.end, desc_squeezelite);
}
fflush (f);
if(strlen(buf)>0){
log_send_messaging(nerrors?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
}
fclose(f);
FREE_AND_NULL(buf);
arg_print_errors(f, squeezelite_args.end, desc_squeezelite);
}
fflush (f);
if(strlen(buf)>0){
log_send_messaging(nerrors?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
}
fclose(f);
FREE_AND_NULL(buf);
FREE_AND_NULL(nvs_config);
FREE_AND_NULL(argv);
return values;
@@ -1212,9 +1231,8 @@ static int do_register_known_templates_config(int argc, char **argv){
char *buf = NULL;
size_t buf_size = 0;
cJSON * config_name =NULL;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
return 1;
}
if(nerrors >0){
@@ -1396,7 +1414,7 @@ void register_ledvu_config(void){
void register_audio_config(void){
audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time.");
audio_args.loudness = arg_int0("l", "loudness","0-10","Sets the loudness level, from 0 to 10. 0 will disable the loudness completely.");
audio_args.loudness = arg_int0("l", "loudness","0-10","Sets a loudness level, from 0 to 10. 0 will disable the loudness completely. Note that LMS has priority over setting this value, so use it only when away from your server.");
audio_args.end = arg_end(6);
audio_args.end = arg_end(6);
const esp_console_cmd_t cmd = {
@@ -1468,22 +1486,36 @@ static void register_squeezelite_config(void){
cmd_to_json_with_cb(&cmd,&squeezelite_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
}
void dummy_register_cmd(){
}
void register_config_cmd(void){
if(!is_dac_config_locked()){
register_known_templates_config();
register_known_templates_config();
}
#ifdef CONFIG_CSPOT_SINK
register_cspot_config();
#endif
register_bt_source_config();
#if CONFIG_WITH_CONFIG_UI
if(!is_dac_config_locked()){
register_i2s_config();
}
else {
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("i2s",true);
#endif
}
if(!is_spdif_config_locked()){
register_spdif_config();
}
else {
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature("spdif",true);
#endif
}
#endif
register_optional_cmd();
}

File diff suppressed because it is too large Load Diff

View File

@@ -589,6 +589,7 @@ void register_nvs()
.func = &list_entries,
.argtable = &list_args
};
MEMTRACE_PRINT_DELTA_MESSAGE("registering list_entries_cmd");
ESP_ERROR_CHECK(esp_console_cmd_register(&list_entries_cmd));
MEMTRACE_PRINT_DELTA_MESSAGE("registering set_cmd");

View File

@@ -62,7 +62,7 @@ static int perform_ota_update(int argc, char **argv)
const esp_console_cmd_t cmd = {
.command = "update",
.help = "Updates the application binary from the provided URL",
.help = "Update from URL",
.hint = NULL,
.func = &perform_ota_update,
.argtable = &ota_args

View File

@@ -31,6 +31,9 @@
#include "messaging.h"
#include "platform_console.h"
#include "tools.h"
#if defined(CONFIG_WITH_METRICS)
#include "Metrics.h"
#endif
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
#pragma message("Runtime stats enabled")
@@ -73,24 +76,42 @@ static void register_set_services();
static void register_tasks();
#endif
extern BaseType_t network_manager_task;
FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size){
FILE *f = open_memstream(buf, buf_size);
if (f == NULL) {
cmd_send_messaging(cmdname,MESSAGING_ERROR,"Unable to open memory stream.");
}
return f;
}
void register_system()
{
register_free();
register_set_services();
register_setdevicename();
register_free();
register_heap();
register_dump_heap();
register_setdevicename();
register_version();
register_restart();
register_deep_sleep();
register_light_sleep();
register_factory_boot();
register_restart_ota();
#if WITH_TASKS_INFO
register_tasks();
#endif
#if CONFIG_WITH_CONFIG_UI
register_deep_sleep();
register_light_sleep();
#endif
}
void simple_restart()
{
log_send_messaging(MESSAGING_WARNING,"Rebooting.");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
/* 'version' command */
static int get_version(int argc, char **argv)
{
@@ -128,36 +149,23 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
{
if(is_recovery_running){
if(partition_subtype ==ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
// log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
simple_restart();
}
}
else {
if(partition_subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY){
log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
return ESP_OK;
// log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
simple_restart();
}
}
esp_err_t err = ESP_OK;
bool bFound=false;
log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
// log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
const esp_partition_t *partition;
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, partition_subtype, NULL);
if(it == NULL){
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Cannot iterate through partitions");
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Partitions error");
}
else
{
@@ -166,15 +174,11 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
ESP_LOGD(TAG, "Releasing partition iterator");
esp_partition_iterator_release(it);
if(partition != NULL){
log_send_messaging(MESSAGING_INFO, "Found application partition %s sub type %u", partition->label,partition_subtype);
log_send_messaging(MESSAGING_INFO, "Rebooting to %s", partition->label);
err=esp_ota_set_boot_partition(partition);
if(err!=ESP_OK){
bFound=false;
log_send_messaging(MESSAGING_ERROR,"Unable to select partition for reboot: %s",esp_err_to_name(err));
}
else{
bFound=true;
}
}
else
{
@@ -183,13 +187,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
}
ESP_LOGD(TAG, "Yielding to other processes");
taskYIELD();
if(bFound) {
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration changes. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
simple_restart();
}
return ESP_OK;
@@ -197,46 +195,31 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
static int restart(int argc, char **argv)
{
log_send_messaging(MESSAGING_WARNING, "\n\nPerforming a simple restart to the currently active partition.");
if(!wait_for_commit()){
cmd_send_messaging(argv[0],MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
simple_restart();
return 0;
}
void simple_restart()
{
log_send_messaging(MESSAGING_WARNING,"System reboot requested.");
if(!wait_for_commit()){
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
}
vTaskDelay(750/ portTICK_PERIOD_MS);
esp_restart();
}
esp_err_t guided_restart_ota(){
log_send_messaging(MESSAGING_WARNING,"System reboot to Application requested");
log_send_messaging(MESSAGING_WARNING,"Booting to Squeezelite");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
esp_err_t guided_factory(){
log_send_messaging(MESSAGING_WARNING,"System reboot to recovery requested");
log_send_messaging(MESSAGING_WARNING,"Booting to recovery");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return ESP_FAIL; // return fail. This should never return... we're rebooting!
}
static int restart_factory(int argc, char **argv)
{
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into recovery");
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Booting to Recovery");
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
return 0; // return fail. This should never return... we're rebooting!
}
static int restart_ota(int argc, char **argv)
{
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into ota app 0");
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Booting to Squeezelite");
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
return 0; // return fail. This should never return... we're rebooting!
}
@@ -248,7 +231,9 @@ static void register_restart()
.hint = NULL,
.func = &restart,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static void register_restart_ota()
@@ -259,7 +244,9 @@ static void register_restart_ota()
.hint = NULL,
.func = &restart_ota,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
@@ -271,7 +258,9 @@ static void register_factory_boot()
.hint = NULL,
.func = &restart_factory,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
/** 'free' command prints available heap memory */
@@ -287,11 +276,14 @@ static void register_free()
{
const esp_console_cmd_t cmd = {
.command = "free",
.help = "Get the current size of free heap memory",
.help = "Get free heap memory",
.hint = NULL,
.func = &free_mem,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
}
static int dump_heap(int argc, char **argv)
@@ -303,16 +295,16 @@ static int dump_heap(int argc, char **argv)
/* 'heap' command prints minumum heap size */
static int heap_size(int argc, char **argv)
{
ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM),
heap_caps_get_free_size(MALLOC_CAP_DMA),
heap_caps_get_minimum_free_size(MALLOC_CAP_DMA),
heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
// ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
// heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
// heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
// heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
// heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
// heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
// heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM),
// heap_caps_get_free_size(MALLOC_CAP_DMA),
// heap_caps_get_minimum_free_size(MALLOC_CAP_DMA),
// heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
cmd_send_messaging(argv[0],MESSAGING_INFO,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
@@ -457,9 +449,8 @@ static int setdevicename(int argc, char **argv)
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
nerrors+=setnamevar("a2dp_dev_name", f, name);
@@ -488,11 +479,13 @@ static void register_heap()
{
const esp_console_cmd_t heap_cmd = {
.command = "heap",
.help = "Get minimum size of free heap memory found during execution",
.help = "Get minimum size of free heap memory",
.hint = NULL,
.func = &heap_size,
};
#if CONFIG_WITH_CONFIG_UI
cmd_to_json(&heap_cmd);
#endif
ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) );
}
@@ -521,6 +514,7 @@ static void register_setdevicename()
.func = &setdevicename,
.argtable = &name_args
};
cmd_to_json_with_cb(&set_name,&setdevicename_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&set_name));
}
@@ -618,9 +612,7 @@ static void register_deep_sleep()
const esp_console_cmd_t cmd = {
.command = "deep_sleep",
.help = "Enter deep sleep mode. "
"Two wakeup modes are supported: timer and GPIO. "
"If no wakeup option is specified, will sleep indefinitely.",
.help = "Enter deep sleep mode. ",
.hint = NULL,
.func = &deep_sleep,
.argtable = &deep_sleep_args
@@ -649,9 +641,8 @@ static int do_set_services(int argc, char **argv)
}
char *buf = NULL;
size_t buf_size = 0;
FILE *f = open_memstream(&buf, &buf_size);
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
if (f == NULL) {
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
return 1;
}
@@ -674,7 +665,7 @@ static int do_set_services(int argc, char **argv)
if(err!=ESP_OK){
nerrors++;
fprintf(f,"Error setting telnet service to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
fprintf(f,"Error setting telnet to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
}
else {
fprintf(f,"Telnet service changed to %s\n",set_services_args.telnet->sval[0]);
@@ -706,7 +697,6 @@ cJSON * set_services_cb(){
#if WITH_TASKS_INFO
console_set_bool_parameter(values,"stats",set_services_args.stats);
#endif
if ((p = config_alloc_get(NVS_TYPE_STR, "telnet_enable")) != NULL) {
if(strcasestr("YX",p)!=NULL){
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Telnet Only");
@@ -717,7 +707,9 @@ cJSON * set_services_cb(){
else {
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Disabled");
}
#if defined(CONFIG_WITH_METRICS)
metrics_add_feature_variant("telnet",p);
#endif
FREE_AND_NULL(p);
}

View File

@@ -17,7 +17,7 @@ void register_system();
esp_err_t guided_factory();
esp_err_t guided_restart_ota();
void simple_restart();
FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size);
#ifdef __cplusplus
}
#endif

View File

@@ -37,6 +37,9 @@ extern bool bypass_network_manager;
#define JOIN_TIMEOUT_MS (10000)
#include "platform_console.h"
// To enable wifi configuration from the command line, uncomment the line below
// define WIFI_CMDLINE 1
extern EventGroupHandle_t network_event_group;
extern const int CONNECTED_BIT;
@@ -53,13 +56,6 @@ static struct {
// todo: implement access point config - cmd_to_json(&i2cdetect_cmd);
///** Arguments used by 'join' function */
//static struct {
// struct arg_int *autoconnect;
// struct arg_end *end;
//} auto_connect_args;
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
@@ -72,27 +68,7 @@ static void event_handler(void* arg, esp_event_base_t event_base,
xEventGroupSetBits(network_event_group, CONNECTED_BIT);
}
}
//bool wait_for_wifi(){
//
// bool connected=(xEventGroupGetBits(wifi_event_group) & CONNECTED_BIT)!=0;
//
// if(!connected){
// ESP_LOGD(TAG,"Waiting for WiFi...");
// connected = (xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
// pdFALSE, pdTRUE, JOIN_TIMEOUT_MS / portTICK_PERIOD_MS)& CONNECTED_BIT)!=0;
// if(!connected){
// ESP_LOGD(TAG,"wifi timeout.");
// }
// else
// {
// ESP_LOGI(TAG,"WiFi Connected!");
// }
// }
//
//
// return connected;
//
//}
static void initialise_wifi(void)
{
static bool initialized = false;
@@ -204,8 +180,10 @@ void register_wifi_join()
void register_wifi()
{
#ifdef WIFI_CMDLINE
register_wifi_join();
if(bypass_network_manager){
initialise_wifi();
}
#endif
}

View File

@@ -27,7 +27,9 @@
#include "platform_config.h"
#include "telnet.h"
#include "tools.h"
#if defined(CONFIG_WITH_METRICS)
#include "metrics.h"
#endif
#include "messaging.h"
#include "config.h"
@@ -85,14 +87,22 @@ cJSON * get_cmd_list(){
}
void console_set_bool_parameter(cJSON * root,char * nvs_name, struct arg_lit *arg){
char * p=NULL;
if(!root) {
bool enabled = false;
if(!root) {
ESP_LOGE(TAG,"Invalid json parameter. Cannot set %s from %s",arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary,nvs_name);
return;
}
if ((p = config_alloc_get(NVS_TYPE_STR, nvs_name)) != NULL) {
cJSON_AddBoolToObject(root,arg->hdr.longopts,strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0);
enabled = strcmp(p,"1") == 0 || strcasecmp(p,"y") == 0;
cJSON_AddBoolToObject(root,arg->hdr.longopts,enabled);
FREE_AND_NULL(p);
}
#if defined(CONFIG_WITH_METRICS)
if(enabled){
metrics_add_feature(nvs_name,"enabled");
}
#endif
}
struct arg_end *getParmsEnd(struct arg_hdr * * argtable){
if(!argtable) return NULL;
@@ -360,8 +370,6 @@ void console_start() {
register_system();
MEMTRACE_PRINT_DELTA_MESSAGE("Registering config commands");
register_config_cmd();
MEMTRACE_PRINT_DELTA_MESSAGE("Registering nvs commands");
register_nvs();
MEMTRACE_PRINT_DELTA_MESSAGE("Registering wifi commands");
register_wifi();

View File

@@ -1,5 +1,5 @@
idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
REQUIRES json tools platform_config display wifi-manager
REQUIRES json tools platform_config display wifi-manager esp-tls platform_config
PRIV_REQUIRES soc esp32
)

View File

@@ -47,6 +47,7 @@ cJSON * gpio_list=NULL;
#define STR(macro) QUOTE(macro)
#endif
extern cJSON * get_gpio_list(bool refresh);
bool are_statistics_enabled(){
#if defined(CONFIG_FREERTOS_USE_TRACE_FACILITY) && defined (CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS)
return true;
@@ -592,7 +593,7 @@ const gpio_exp_config_t* config_gpio_exp_get(int index) {
PARSE_PARAM(item, "intr", '=', config.intr);
PARSE_PARAM(item, "base", '=', config.base);
PARSE_PARAM(item, "count", '=', config.count);
PARSE_PARAM_STR(item, "model", '=', config.model, 31);
PARSE_PARAM_STR(item, "model", '=', config.model, sizeof(config.model)-1);
if ((p = strcasestr(item, "port")) != NULL) {
char port[8] = "";
@@ -646,6 +647,12 @@ const set_GPIO_struct_t * get_gpio_struct(){
#endif
#ifdef CONFIG_LED_RED_GPIO
gpio_struct.red.gpio = CONFIG_LED_RED_GPIO;
#endif
#if defined(CONFIG_POWER_GPIO) && CONFIG_POWER_GPIO != -1
gpio_struct.power.gpio = CONFIG_POWER_GPIO;
#endif
#ifdef CONFIG_POWER_GPIO_LEVEL
gpio_struct.power.level = CONFIG_POWER_GPIO_LEVEL;
#endif
if(nvs_item){
HANDLE_GPIO_STRUCT_MEMBER(amp,false);
@@ -658,6 +665,7 @@ const set_GPIO_struct_t * get_gpio_struct(){
HANDLE_GPIO_STRUCT_MEMBER(vcc,false);
HANDLE_GPIO_STRUCT_MEMBER(gnd,false);
HANDLE_GPIO_STRUCT_MEMBER(ir,false);
HANDLE_GPIO_STRUCT_MEMBER(power,false);
free(nvs_item);
}
@@ -823,6 +831,7 @@ cJSON * get_GPIO_nvs_list(cJSON * list) {
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,jack,"other");
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,green,"other");
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,red,"other");
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,power,"other");
ADD_GPIO_STRUCT_MEMBER_TO_ARRAY(ilist,gpios,spkfault,"other");
return ilist;
}
@@ -1169,7 +1178,7 @@ cJSON * get_psram_gpio_list(cJSON * list){
/****************************************************************************************
*
*/
cJSON * get_gpio_list(bool refresh) {
cJSON * get_gpio_list_handler(bool refresh) {
gpio_num_t gpio_num;
if(gpio_list && !refresh){
return gpio_list;

View File

@@ -73,6 +73,7 @@ typedef struct {
gpio_with_level_t green;
gpio_with_level_t red;
gpio_with_level_t spkfault;
gpio_with_level_t power;
} set_GPIO_struct_t;
typedef struct {
@@ -117,7 +118,7 @@ bool is_spdif_config_locked();
esp_err_t free_gpio_entry( gpio_entry_t ** gpio);
gpio_entry_t * get_gpio_by_name(char * name,char * group, bool refresh);
gpio_entry_t * get_gpio_by_no(int gpionum, bool refresh);
cJSON * get_gpio_list(bool refresh);
bool is_dac_config_locked();
bool are_statistics_enabled();
const rotary_struct_t * config_rotary_get();

View File

@@ -1,6 +1,6 @@
idf_component_register( SRCS operator.cpp tools.c trace.c
REQUIRES esp_common pthread
PRIV_REQUIRES esp_http_client esp-tls
PRIV_REQUIRES esp_http_client esp-tls json
INCLUDE_DIRS .
)

View File

@@ -24,6 +24,7 @@
#error CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS must be at least 2
#endif
#include "cJSON.h"
const static char TAG[] = "tools";
/****************************************************************************************
@@ -318,11 +319,30 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt) {
return ESP_FAIL;
}
break;
}
default:
break;
}
}
return ESP_OK;
}
time_t millis() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
void dump_json_content(const char* prefix, cJSON* json, int level) {
if (!json) {
ESP_LOG_LEVEL(level,TAG, "%s: empty!", prefix);
return;
}
char* output = cJSON_Print(json);
if (output) {
ESP_LOG_LEVEL(level,TAG, "%s: \n%s", prefix, output);
}
FREE_AND_NULL(output);
}

View File

@@ -9,6 +9,10 @@
*/
#pragma once
#include "cJSON.h"
#include "time.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef __cplusplus
extern "C" {
@@ -70,6 +74,9 @@ void vTaskDeleteEXTRAM(TaskHandle_t xTask);
extern const char unknown_string_placeholder[];
time_t millis();
void dump_json_content(const char* prefix, cJSON* json, int level);
#ifdef __cplusplus
}
#endif

View File

@@ -44,7 +44,7 @@ typedef struct session_context {
char * sess_ip_address;
u16_t port;
} session_context_t;
extern cJSON * get_gpio_list(bool refresh);
union sockaddr_aligned {
struct sockaddr sa;

View File

@@ -1,4 +1,4 @@
idf_component_register(SRC_DIRS .
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets led_strip
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets led_strip metrics
LDFRAGMENTS "linker.lf"
)

View File

@@ -74,6 +74,16 @@ menu "Squeezelite-ESP32"
select I2C_LOCKED
select TARGET_LOCKED
endchoice
config WITH_CONFIG_UI
bool "Enable config UI"
default n
help
Enable configuring system options with the UI
config WITH_METRICS
bool "Enable Metrics"
default n
help
Enable capturing and reporting anonymous metrics
config RELEASE_API
string "Software update URL"
default "https://api.github.com/repos/sle118/squeezelite-esp32/releases"

View File

@@ -47,6 +47,9 @@
#include "accessors.h"
#include "cmd_system.h"
#include "tools.h"
#if defined(CONFIG_WITH_METRICS)
#include "Metrics.h"
#endif
const char unknown_string_placeholder[] = "unknown";
const char null_string_placeholder[] = "null";
@@ -68,7 +71,68 @@ bool cold_boot=true;
extern const char _ctype_[];
const char* __ctype_ptr__ = _ctype_;
#endif
typedef struct {
const char *key;
const char *value;
} DefaultStringVal;
typedef struct {
const char *key;
unsigned int uint_value;
bool is_signed;
} DefaultNumVal;
const DefaultNumVal defaultNumVals[] = {
{"ota_erase_blk", OTA_FLASH_ERASE_BLOCK, 0},
{"ota_stack", OTA_STACK_SIZE, 0},
{"ota_prio", OTA_TASK_PRIOTITY, 1}
};
const DefaultStringVal defaultStringVals[] = {
{"equalizer", ""},
{"loudness", "0"},
{"actrls_config", ""},
{"lms_ctrls_raw", "n"},
{"rotary_config", CONFIG_ROTARY_ENCODER},
{"display_config", CONFIG_DISPLAY_CONFIG},
{"eth_config", CONFIG_ETH_CONFIG},
{"i2c_config", CONFIG_I2C_CONFIG},
{"spi_config", CONFIG_SPI_CONFIG},
{"set_GPIO", CONFIG_SET_GPIO},
{"sleep_config", ""},
{"led_brightness", ""},
{"spdif_config", ""},
{"dac_config", ""},
{"dac_controlset", ""},
{"jack_mutes_amp", "n"},
{"gpio_exp_config", CONFIG_GPIO_EXP_CONFIG},
{"bat_config", ""},
{"metadata_config", ""},
{"telnet_enable", ""},
{"telnet_buffer", "40000"},
{"telnet_block", "500"},
{"stats", "n"},
{"rel_api", CONFIG_RELEASE_API},
{"pollmx", "600"},
{"pollmin", "15"},
{"ethtmout", "8"},
{"dhcp_tmout", "8"},
{"target", CONFIG_TARGET},
{"led_vu_config", ""},
#ifdef CONFIG_BT_SINK
{"bt_sink_pin", STR(CONFIG_BT_SINK_PIN)},
{"bt_sink_volume", "127"},
// Note: register_default_with_mac("bt_name", CONFIG_BT_NAME); is a special case
{"enable_bt_sink", STR(CONFIG_BT_SINK)},
{"a2dp_dev_name", CONFIG_A2DP_DEV_NAME},
{"a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS)},
{"a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS)},
{"a2dp_sink_name", CONFIG_A2DP_SINK_NAME},
{"autoexec", "1"},
#ifdef CONFIG_AIRPLAY_SINK
{"airplay_port", CONFIG_AIRPLAY_PORT},
{"enable_airplay", STR(CONFIG_AIRPLAY_SINK)}
#endif
#endif
};
static bool bNetworkConnected=false;
// as an exception _init function don't need include
@@ -80,7 +144,9 @@ extern void target_init(char *target);
const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); }
const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); }
bool is_recovery_running;
bool is_network_connected(){
return bNetworkConnected;
}
void cb_connection_got_ip(nm_state_t new_state, int sub_state){
const char *hostname;
static ip4_addr_t ip;
@@ -163,7 +229,7 @@ void set_log_level(char * tag, char * level){
}
#define DEFAULT_NAME_WITH_MAC(var,defval) char var[strlen(defval)+sizeof(macStr)]; strcpy(var,defval); strcat(var,macStr)
void register_default_string_val(const char * key, char * value){
void register_default_string_val(const char * key, const char * value){
char * existing =(char *)config_alloc_get(NVS_TYPE_STR,key );
ESP_LOGD(TAG,"Register default called with: %s= %s",key,value );
if(!existing) {
@@ -175,7 +241,15 @@ void register_default_string_val(const char * key, char * value){
}
FREE_AND_NULL(existing);
}
void register_single_default_num_val(const DefaultNumVal *entry) {
char number_buffer[101] = {};
if (entry->is_signed) {
snprintf(number_buffer, sizeof(number_buffer) - 1, "%d", entry->uint_value);
} else {
snprintf(number_buffer, sizeof(number_buffer) - 1, "%u", entry->uint_value);
}
register_default_string_val(entry->key, number_buffer);
}
char * alloc_get_string_with_mac(const char * val) {
uint8_t mac[6];
char macStr[LOCAL_MAC_SIZE + 1];
@@ -188,7 +262,7 @@ char * alloc_get_string_with_mac(const char * val) {
strcat(fullvalue, macStr);
}
else {
ESP_LOGE(TAG,"Memory allocation failed when getting mac value for %s", val);
ESP_LOGE(TAG,"malloc failed for value %s", val);
}
return fullvalue;
@@ -200,7 +274,7 @@ void register_default_with_mac(const char* key, char* defval) {
FREE_AND_NULL(fullvalue);
}
else {
ESP_LOGE(TAG,"Memory allocation failed when registering default value for %s", key);
ESP_LOGE(TAG,"malloc failed for value %s", key);
}
}
@@ -227,70 +301,20 @@ void register_default_nvs(){
#ifdef CONFIG_AIRPLAY_SINK
register_default_with_mac("airplay_name", CONFIG_AIRPLAY_NAME);
register_default_string_val("airplay_port", CONFIG_AIRPLAY_PORT);
register_default_string_val( "enable_airplay", STR(CONFIG_AIRPLAY_SINK));
#endif
#ifdef CONFIG_BT_SINK
register_default_string_val( "bt_sink_pin", STR(CONFIG_BT_SINK_PIN));
register_default_string_val( "bt_sink_volume", "127");
register_default_with_mac("bt_name", CONFIG_BT_NAME);
register_default_string_val( "enable_bt_sink", STR(CONFIG_BT_SINK));
register_default_string_val("a2dp_dev_name", CONFIG_A2DP_DEV_NAME);
register_default_string_val("a2dp_ctmt", STR(CONFIG_A2DP_CONNECT_TIMEOUT_MS));
register_default_string_val("a2dp_ctrld", STR(CONFIG_A2DP_CONTROL_DELAY_MS));
register_default_string_val("a2dp_sink_name", CONFIG_A2DP_SINK_NAME);
#endif
register_default_with_mac("host_name", DEFAULT_HOST_NAME);
register_default_with_mac("ap_ssid", CONFIG_DEFAULT_AP_SSID);
register_default_string_val("autoexec","1");
register_default_with_mac("autoexec1",CONFIG_DEFAULT_COMMAND_LINE " -n " DEFAULT_HOST_NAME);
for (int i = 0; i < sizeof(defaultStringVals) / sizeof(DefaultStringVal); ++i) {
register_default_string_val(defaultStringVals[i].key, defaultStringVals[i].value);
}
for (int i = 0; i < sizeof(defaultNumVals) / sizeof(DefaultNumVal); ++i) {
register_single_default_num_val(&defaultNumVals[i]);
}
register_default_string_val("release_url", CONFIG_SQUEEZELITE_ESP32_RELEASE_URL);
register_default_string_val("ap_ip_address",CONFIG_DEFAULT_AP_IP);
register_default_string_val("ap_ip_gateway",CONFIG_DEFAULT_AP_GATEWAY );
register_default_string_val("ap_ip_netmask",CONFIG_DEFAULT_AP_NETMASK);
register_default_string_val("ap_channel",STR(CONFIG_DEFAULT_AP_CHANNEL));
register_default_string_val("ap_pwd", CONFIG_DEFAULT_AP_PASSWORD);
register_default_string_val("bypass_wm", "0");
register_default_string_val("equalizer", "");
register_default_string_val("loudness", "0");
register_default_string_val("actrls_config", "");
register_default_string_val("lms_ctrls_raw", "n");
register_default_string_val("rotary_config", CONFIG_ROTARY_ENCODER);
char number_buffer[101] = {};
snprintf(number_buffer,sizeof(number_buffer)-1,"%u",OTA_FLASH_ERASE_BLOCK);
register_default_string_val( "ota_erase_blk", number_buffer);
snprintf(number_buffer,sizeof(number_buffer)-1,"%u",OTA_STACK_SIZE);
register_default_string_val( "ota_stack", number_buffer);
snprintf(number_buffer,sizeof(number_buffer)-1,"%d",OTA_TASK_PRIOTITY);
register_default_string_val( "ota_prio", number_buffer);
register_default_string_val( "display_config", CONFIG_DISPLAY_CONFIG);
register_default_string_val( "eth_config", CONFIG_ETH_CONFIG);
register_default_string_val( "i2c_config", CONFIG_I2C_CONFIG);
register_default_string_val( "spi_config", CONFIG_SPI_CONFIG);
register_default_string_val( "set_GPIO", CONFIG_SET_GPIO);
register_default_string_val( "sleep_config", "");
register_default_string_val( "led_brightness", "");
register_default_string_val( "spdif_config", "");
register_default_string_val( "dac_config", "");
register_default_string_val( "dac_controlset", "");
register_default_string_val( "jack_mutes_amp", "n");
register_default_string_val("gpio_exp_config", CONFIG_GPIO_EXP_CONFIG);
register_default_string_val( "bat_config", "");
register_default_string_val( "metadata_config", "");
register_default_string_val( "telnet_enable", "");
register_default_string_val( "telnet_buffer", "40000");
register_default_string_val( "telnet_block", "500");
register_default_string_val( "stats", "n");
register_default_string_val( "rel_api", CONFIG_RELEASE_API);
register_default_string_val("pollmx","600");
register_default_string_val("pollmin","15");
register_default_string_val("ethtmout","8");
register_default_string_val("dhcp_tmout","8");
register_default_string_val("target", CONFIG_TARGET);
register_default_string_val("led_vu_config", "");
wait_for_commit();
ESP_LOGD(TAG,"Done setting default values in nvs.");
}
@@ -303,7 +327,7 @@ uint32_t halSTORAGE_RebootCounterUpdate(int32_t xValue) {
}
RebootCounter = (xValue != 0) ? (RebootCounter + xValue) : 0;
RecoveryRebootCounter = (xValue != 0) && is_recovery_running ? (RecoveryRebootCounter + xValue) : 0;
return (RebootCounter) ;
return RebootCounter ;
}
void handle_ap_connect(nm_state_t new_state, int sub_state){
@@ -356,11 +380,17 @@ void app_main()
}
}
char * fwurl = NULL;
MEMTRACE_PRINT_DELTA();
ESP_LOGI(TAG,"Starting app_main");
initialize_nvs();
MEMTRACE_PRINT_DELTA();
#if defined(CONFIG_WITH_METRICS)
ESP_LOGI(TAG,"Setting up metrics.");
metrics_init();
MEMTRACE_PRINT_DELTA();
#endif
ESP_LOGI(TAG,"Setting up telnet.");
init_telnet(); // align on 32 bits boundaries
MEMTRACE_PRINT_DELTA();
@@ -371,7 +401,6 @@ void app_main()
network_event_group = xEventGroupCreate();
ESP_LOGD(TAG,"Clearing CONNECTED_BIT from wifi group");
xEventGroupClearBits(network_event_group, CONNECTED_BIT);
ESP_LOGI(TAG,"Registering default values");
register_default_nvs();
MEMTRACE_PRINT_DELTA();
@@ -399,7 +428,10 @@ void app_main()
led_vu_color_yellow(LED_VU_BRIGHT);
}
}
#if defined(CONFIG_WITH_METRICS)
metrics_event_boot(is_recovery_running?"recovery":"ota");
#endif
ESP_LOGD(TAG,"Getting firmware OTA URL (if any)");
fwurl = process_ota_url();
@@ -457,6 +489,9 @@ void app_main()
taskYIELD();
}
ESP_LOGI(TAG,"Updating firmware from link: %s",fwurl);
#if defined(CONFIG_WITH_METRICS)
metrics_event("fw_update");
#endif
start_ota(fwurl, NULL, 0);
}
else {