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);
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,7 +8,12 @@
#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); \
@@ -26,7 +31,7 @@ extern "C" {
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,11 +1063,10 @@ 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;
}
else {
if(nvs_config && strlen(nvs_config)>0){
ESP_LOGD(TAG,"Parsing command %s",nvs_config);
@@ -1080,6 +1095,11 @@ cJSON * squeezelite_cb(){
// 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);
@@ -1099,7 +1119,6 @@ cJSON * squeezelite_cb(){
}
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();
}
#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();
}

View File

@@ -6,22 +6,23 @@
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "cmd_i2ctools.h"
#include "argtable3/argtable3.h"
#include "driver/i2c.h"
#include "platform_console.h"
#include "esp_log.h"
#include "string.h"
#include "stdio.h"
#include "platform_config.h"
#include "accessors.h"
#include "trace.h"
#include "messaging.h"
#include "display.h"
#include "config.h"
#include "tools.h"
#include "adac.h"
#include "argtable3/argtable3.h"
#include "config.h"
#include "display.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "messaging.h"
#include "platform_config.h"
#include "platform_console.h"
#include "stdio.h"
#include "string.h"
#include "tools.h"
#include "trace.h"
#include <stdio.h>
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
@@ -74,7 +75,7 @@ static struct {
struct arg_lit* clear;
struct arg_end* end;
} i2cconfig_args;
#if CONFIG_WITH_CONFIG_UI
static struct {
struct arg_int* data;
struct arg_int* miso;
@@ -84,7 +85,6 @@ static struct {
struct arg_lit* clear;
struct arg_end* end;
} spiconfig_args;
static struct {
struct arg_str* type;
struct arg_str* driver;
@@ -104,6 +104,7 @@ static struct {
struct arg_int* mode;
struct arg_end* end;
} i2cdisp_args;
#endif
bool is_i2c_started(i2c_port_t port) {
esp_err_t ret = ESP_OK;
@@ -120,7 +121,8 @@ bool is_i2c_started(i2c_port_t port){
ret = i2c_master_cmd_begin(port, cmd, 50 / portTICK_RATE_MS);
}
i2c_cmd_link_delete(cmd);
ESP_LOGD(TAG,"i2c is %s. %s",ret!=ESP_ERR_INVALID_STATE?"started":"not started", esp_err_to_name(ret));
ESP_LOGD(TAG, "i2c is %s. %s", ret != ESP_ERR_INVALID_STATE ? "started" : "not started",
esp_err_to_name(ret));
return (ret != ESP_ERR_INVALID_STATE);
}
@@ -129,32 +131,26 @@ typedef struct {
const char* description;
} i2c_db_t;
#if CONFIG_WITH_CONFIG_UI
// the list was taken from https://i2cdevices.org/addresses
// on 2020-01-16
static const i2c_db_t i2c_db[] = {
{ .address = 0x00, .description="Unknown"},
{ .address = 0x01, .description="Unknown"},
{ .address = 0x02, .description="Unknown"},
{ .address = 0x03, .description="Unknown"},
{ .address = 0x04, .description="Unknown"},
{ .address = 0x05, .description="Unknown"},
{ .address = 0x06, .description="Unknown"},
{ .address = 0x07, .description="Unknown"},
{ .address = 0x08, .description="Unknown"},
{ .address = 0x0c, .description="AK8975"},
{ .address = 0x0d, .description="AK8975"},
static const i2c_db_t i2c_db[] = {{.address = 0x00, .description = "Unknown"},
{.address = 0x01, .description = "Unknown"}, {.address = 0x02, .description = "Unknown"},
{.address = 0x03, .description = "Unknown"}, {.address = 0x04, .description = "Unknown"},
{.address = 0x05, .description = "Unknown"}, {.address = 0x06, .description = "Unknown"},
{.address = 0x07, .description = "Unknown"}, {.address = 0x08, .description = "Unknown"},
{.address = 0x0c, .description = "AK8975"}, {.address = 0x0d, .description = "AK8975"},
{.address = 0x0e, .description = "MAG3110 AK8975 IST-8310"},
{.address = 0x0f, .description = "AK8975"},
{.address = 0x10, .description = "VEML7700 VML6075 VEML6075 ES8388"},
{ .address = 0x11, .description="Si4713 SAA5246 SAA5243P/K SAA5243P/L SAA5243P/E SAA5243P/H ES8388"},
{.address = 0x11,
.description = "Si4713 SAA5246 SAA5243P/K SAA5243P/L SAA5243P/E SAA5243P/H ES8388"},
{.address = 0x12, .description = "SEN-17374"},
{.address = 0x13, .description = "VCNL40x0 SEN-17374"},
{.address = 0x18, .description = "MCP9808 LIS3DH LSM303 COM-15093"},
{.address = 0x19, .description = "MCP9808 LIS3DH LSM303 COM-15093"},
{ .address = 0x1a, .description="AC101 MCP9808"},
{ .address = 0x1b, .description="MCP9808"},
{.address = 0x1a, .description = "AC101 MCP9808"}, {.address = 0x1b, .description = "MCP9808"},
{.address = 0x1c, .description = "MCP9808 MMA845x FXOS8700"},
{.address = 0x1d, .description = "MCP9808 MMA845x ADXL345 FXOS8700"},
{.address = 0x1e, .description = "HMC5883 LSM303 MCP9808 LSM303 FXOS8700"},
@@ -169,14 +165,12 @@ static const i2c_db_t i2c_db[] = {
{.address = 0x27, .description = "MCP23008 MCP23017 HIH6130 TCA9554"},
{.address = 0x28, .description = "BNO055 CAP1188"},
{.address = 0x29, .description = "BNO055 VL53L0x VL6180X CAP1188 TCS34725 TSL2591"},
{ .address = 0x2a, .description="CAP1188"},
{ .address = 0x2b, .description="CAP1188"},
{.address = 0x2a, .description = "CAP1188"}, {.address = 0x2b, .description = "CAP1188"},
{.address = 0x2c, .description = "CAP1188 AD5248 AD5251 AD5252 CAT5171"},
{.address = 0x2d, .description = "CAP1188 AD5248 AD5251 AD5252 CAT5171"},
{.address = 0x2e, .description = "AD5248 AD5251 AD5252 LPS22HB"},
{.address = 0x2f, .description = "AD5248 AD5243 AD5251 AD5252"},
{ .address = 0x30, .description="SAA2502"},
{ .address = 0x31, .description="SAA2502"},
{.address = 0x30, .description = "SAA2502"}, {.address = 0x31, .description = "SAA2502"},
{.address = 0x33, .description = "MLX90640"},
{.address = 0x38, .description = "FT6x06 VEML6070 BMA150 SAA1064 SEN-15892 PCF8574AP"},
{.address = 0x39, .description = "TSL2561 APDS-9960 VEML6070 SAA1064 PCF8574AP"},
@@ -184,20 +178,30 @@ static const i2c_db_t i2c_db[] = {
{.address = 0x3b, .description = "SAA1064 PCF8569 PCF8574AP"},
{.address = 0x3c, .description = "SSD1305 SSD1306 PCF8578 PCF8569 SH1106 PCF8574AP"},
{.address = 0x3d, .description = "SSD1305 SSD1306 PCF8578 SH1106 PCF8574AP"},
{ .address = 0x3e, .description="PCF8574AP"},
{ .address = 0x3f, .description="PCF8574AP"},
{ .address = 0x40, .description="Si7021 HTU21D-F TMP007 TMP006 PCA9685 INA219 TEA6330 TEA6300 TDA9860 TEA6320 TDA8421 NE5751 INA260 PCF8574"},
{ .address = 0x41, .description="TMP007 TDA8421 TDA8424 STMPE610 PCF8574 STMPE811 NE5751 INA260 TDA8425 TMP006 TDA9860 PCA9685 INA219 TDA8426"},
{ .address = 0x42, .description="TMP007 TDA8417 HDC1008 PCF8574 INA260 TDA8415 TMP006 PCA9685 INA219"},
{.address = 0x3e, .description = "PCF8574AP"}, {.address = 0x3f, .description = "PCF8574AP"},
{.address = 0x40,
.description = "Si7021 HTU21D-F TMP007 TMP006 PCA9685 INA219 TEA6330 TEA6300 TDA9860 "
"TEA6320 TDA8421 NE5751 INA260 PCF8574"},
{.address = 0x41,
.description = "TMP007 TDA8421 TDA8424 STMPE610 PCF8574 STMPE811 NE5751 INA260 TDA8425 "
"TMP006 TDA9860 PCA9685 INA219 TDA8426"},
{.address = 0x42,
.description = "TMP007 TDA8417 HDC1008 PCF8574 INA260 TDA8415 TMP006 PCA9685 INA219"},
{.address = 0x43, .description = "TMP007 HDC1008 PCF8574 INA260 TMP006 PCA9685 INA219"},
{ .address = 0x44, .description="TMP007 TMP006 PCA9685 INA219 STMPE610 SHT31 ISL29125 STMPE811 TDA4688 TDA4672 TDA4780 TDA4670 TDA8442 TDA4687 TDA4671 TDA4680 INA260 PCF8574"},
{ .address = 0x45, .description="TMP007 TDA7433 PCF8574 TDA8376 INA260 TMP006 PCA9685 INA219 SHT31"},
{.address = 0x44,
.description = "TMP007 TMP006 PCA9685 INA219 STMPE610 SHT31 ISL29125 STMPE811 TDA4688 "
"TDA4672 TDA4780 TDA4670 TDA8442 TDA4687 TDA4671 TDA4680 INA260 PCF8574"},
{.address = 0x45,
.description = "TMP007 TDA7433 PCF8574 TDA8376 INA260 TMP006 PCA9685 INA219 SHT31"},
{.address = 0x46, .description = "TMP007 PCF8574 TDA8370 INA260 TMP006 PCA9685 INA219 TDA9150"},
{.address = 0x47, .description = "TMP007 PCF8574 INA260 TMP006 PCA9685 INA219"},
{.address = 0x48, .description = "PCA9685 INA219 PN532 TMP102 INA260 ADS1115 PCF8574 ADS7828"},
{ .address = 0x49, .description="TSL2561 PCA9685 INA219 TMP102 INA260 ADS1115 AS7262 PCF8574 ADS7828"},
{ .address = 0x4a, .description="ADS7828 PCF8574 ADS1115 INA260 PCA9685 MAX44009 INA219 TMP102"},
{ .address = 0x4b, .description="ADS7828 PCF8574 ADS1115 INA260 PCA9685 MAX44009 INA219 TMP102"},
{.address = 0x49,
.description = "TSL2561 PCA9685 INA219 TMP102 INA260 ADS1115 AS7262 PCF8574 ADS7828"},
{.address = 0x4a,
.description = "ADS7828 PCF8574 ADS1115 INA260 PCA9685 MAX44009 INA219 TMP102"},
{.address = 0x4b,
.description = "ADS7828 PCF8574 ADS1115 INA260 PCA9685 MAX44009 INA219 TMP102"},
{.address = 0x4c, .description = "PCA9685 INA219 INA260 PCF8574"},
{.address = 0x4d, .description = "PCA9685 INA219 INA260 PCF8574"},
{.address = 0x4e, .description = "PCA9685 INA219 INA260 PCF8574"},
@@ -215,23 +219,28 @@ static const i2c_db_t i2c_db[] = {
{.address = 0x5a, .description = "MPR121 MLX90614 CCS811 PCA9685 DRV2605"},
{.address = 0x5b, .description = "PCA9685 CCS811 MPR121"},
{.address = 0x5c, .description = "PCA9685 AM2315 MPR121"},
{ .address = 0x5d, .description="PCA9685 MPR121"},
{ .address = 0x5e, .description="PCA9685"},
{.address = 0x5d, .description = "PCA9685 MPR121"}, {.address = 0x5e, .description = "PCA9685"},
{.address = 0x5f, .description = "PCA9685 HTS221"},
{ .address = 0x60, .description="SI1132 Si5351A ATECC608A TSA5511 ATECC508A SAB3035 MCP4725A0 SAB3037 PCA9685 MCP4725A1 TEA5767 MPL3115A2 MPL115A2 Si1145"},
{ .address = 0x61, .description="Si5351A TSA5511 SAB3035 MCP4725A0 SAB3037 TEA6100 PCA9685 MCP4725A1"},
{ .address = 0x62, .description="SCD40-D-R2 TSA5511 SAB3035 UMA1014T SAB3037 PCA9685 MCP4725A1"},
{.address = 0x60,
.description = "SI1132 Si5351A ATECC608A TSA5511 ATECC508A SAB3035 MCP4725A0 SAB3037 "
"PCA9685 MCP4725A1 TEA5767 MPL3115A2 MPL115A2 Si1145"},
{.address = 0x61,
.description = "Si5351A TSA5511 SAB3035 MCP4725A0 SAB3037 TEA6100 PCA9685 MCP4725A1"},
{.address = 0x62,
.description = "SCD40-D-R2 TSA5511 SAB3035 UMA1014T SAB3037 PCA9685 MCP4725A1"},
{.address = 0x63, .description = "Si4713 TSA5511 SAB3035 UMA1014T SAB3037 PCA9685 MCP4725A1"},
{.address = 0x64, .description = "PCA9685 MCP4725A2 MCP4725A1"},
{.address = 0x65, .description = "PCA9685 MCP4725A2 MCP4725A1"},
{.address = 0x66, .description = "PCA9685 MCP4725A3 IS31FL3731 MCP4725A1"},
{.address = 0x67, .description = "PCA9685 MCP4725A3 MCP4725A1"},
{ .address = 0x68, .description="MPU-9250 ICM-20948 MPU6050 AMG8833 DS3231 PCA9685 PCF8573 PCF8523 DS1307 ITG3200"},
{ .address = 0x69, .description="MPU-9250 ICM-20948 MPU6050 AMG8833 PCA9685 PCF8573 ITG3200 SPS30"},
{.address = 0x68,
.description =
"MPU-9250 ICM-20948 MPU6050 AMG8833 DS3231 PCA9685 PCF8573 PCF8523 DS1307 ITG3200"},
{.address = 0x69,
.description = "MPU-9250 ICM-20948 MPU6050 AMG8833 PCA9685 PCF8573 ITG3200 SPS30"},
{.address = 0x6a, .description = "PCA9685 L3GD20H PCF8573"},
{.address = 0x6b, .description = "PCA9685 L3GD20H PCF8573"},
{ .address = 0x6c, .description="PCA9685"},
{ .address = 0x6d, .description="PCA9685"},
{.address = 0x6c, .description = "PCA9685"}, {.address = 0x6d, .description = "PCA9685"},
{.address = 0x6e, .description = "PCA9685"},
{.address = 0x6f, .description = "PCA9685 MCP7940N"},
{.address = 0x70, .description = "PCA9685 TCA9548 HT16K33 SHTC3"},
@@ -240,27 +249,25 @@ static const i2c_db_t i2c_db[] = {
{.address = 0x73, .description = "PCA9685 TCA9548 HT16K33"},
{.address = 0x74, .description = "PCA9685 TCA9548 HT16K33"},
{.address = 0x75, .description = "PCA9685 TCA9548 HT16K33"},
{ .address = 0x76, .description="BME688 BME680 MS5611 MS5607 HT16K33 PCA9685 BME280 BMP280 TCA9548"},
{ .address = 0x77, .description="PCA9685 TCA9548 HT16K33 IS31FL3731 BME280 BMP280 MS5607 BMP180 BMP085 BMA180 MS5611 BME680 BME688"},
{ .address = 0x78, .description="PCA9685"},
{ .address = 0x79, .description="PCA9685"},
{ .address = 0x7a, .description="PCA9685"},
{ .address = 0x7b, .description="PCA9685"},
{ .address = 0x7c, .description="PCA9685"},
{ .address = 0x7d, .description="PCA9685"},
{ .address = 0x7e, .description="PCA9685"},
{ .address = 0x7f, .description="PCA9685"},
{ .address = 0, .description = NULL}
};
{.address = 0x76,
.description = "BME688 BME680 MS5611 MS5607 HT16K33 PCA9685 BME280 BMP280 TCA9548"},
{.address = 0x77,
.description = "PCA9685 TCA9548 HT16K33 IS31FL3731 BME280 BMP280 MS5607 BMP180 BMP085 "
"BMA180 MS5611 BME680 BME688"},
{.address = 0x78, .description = "PCA9685"}, {.address = 0x79, .description = "PCA9685"},
{.address = 0x7a, .description = "PCA9685"}, {.address = 0x7b, .description = "PCA9685"},
{.address = 0x7c, .description = "PCA9685"}, {.address = 0x7d, .description = "PCA9685"},
{.address = 0x7e, .description = "PCA9685"}, {.address = 0x7f, .description = "PCA9685"},
{.address = 0, .description = NULL}};
const char* i2c_get_description(uint8_t address) {
uint8_t i = 0;
while(i2c_db[i].description && i2c_db[i].address!=address) i++;
while (i2c_db[i].description && i2c_db[i].address != address)
i++;
return i2c_db[i].description ? i2c_db[i].description : "Unlisted";
}
static esp_err_t i2c_get_port(int port, i2c_port_t *i2c_port)
{
#endif
static esp_err_t i2c_get_port(int port, i2c_port_t* i2c_port) {
if (port >= I2C_NUM_MAX) {
log_send_messaging(MESSAGING_ERROR, "Wrong port number: %d", port);
return ESP_FAIL;
@@ -281,32 +288,34 @@ static esp_err_t i2c_get_port(int port, i2c_port_t *i2c_port)
static esp_err_t i2c_master_driver_install(const char* cmdname) {
esp_err_t err = ESP_OK;
cmd_send_messaging(cmdname, MESSAGING_INFO, "Installing i2c driver on port %u\n", i2c_port);
if((err=i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0))!=ESP_OK){
cmd_send_messaging(cmdname,MESSAGING_ERROR,"Driver install failed! %s\n", esp_err_to_name(err));
if ((err = i2c_driver_install(i2c_port, I2C_MODE_MASTER, I2C_MASTER_RX_BUF_DISABLE,
I2C_MASTER_TX_BUF_DISABLE, 0)) != ESP_OK) {
cmd_send_messaging(
cmdname, MESSAGING_ERROR, "Driver install failed! %s\n", esp_err_to_name(err));
}
return err;
}
static esp_err_t i2c_master_driver_initialize(const char * cmdname, i2c_config_t * conf)
{
static esp_err_t i2c_master_driver_initialize(const char* cmdname, i2c_config_t* conf) {
esp_err_t err = ESP_OK;
cmd_send_messaging(cmdname,MESSAGING_INFO,"Initializing i2c driver configuration.\n mode = I2C_MODE_MASTER, \n scl_pullup_en = GPIO_PULLUP_ENABLE, \n i2c port = %u, \n sda_io_num = %u, \n sda_pullup_en = GPIO_PULLUP_ENABLE, \n scl_io_num = %u, \n scl_pullup_en = GPIO_PULLUP_ENABLE, \n master.clk_speed = %u\n", i2c_port, conf->sda_io_num,conf->scl_io_num,conf->master.clk_speed);
cmd_send_messaging(cmdname, MESSAGING_INFO,
"Initializing i2c driver configuration.\n mode = I2C_MODE_MASTER, \n scl_pullup_en = "
"GPIO_PULLUP_ENABLE, \n i2c port = %u, \n sda_io_num = %u, \n sda_pullup_en = "
"GPIO_PULLUP_ENABLE, \n scl_io_num = %u, \n scl_pullup_en = GPIO_PULLUP_ENABLE, \n "
"master.clk_speed = %u\n",
i2c_port, conf->sda_io_num, conf->scl_io_num, conf->master.clk_speed);
if ((err = i2c_param_config(i2c_port, conf)) != ESP_OK) {
cmd_send_messaging(cmdname,MESSAGING_ERROR,"i2c driver config load failed. %s\n", esp_err_to_name(err));
cmd_send_messaging(
cmdname, MESSAGING_ERROR, "i2c driver config load failed. %s\n", esp_err_to_name(err));
}
return err;
}
static int do_i2c_set_display(int argc, char **argv)
{
#if CONFIG_WITH_CONFIG_UI
static int do_i2c_set_display(int argc, char** argv) {
bool bHasi2cConfig = false, bHasSpiConfig = false;
int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cdisp_args);
display_config_t config = {
.back = -1,
.CS_pin = -1,
.RST_pin = -1,
.depth = 0
};
display_config_t config = {.back = -1, .CS_pin = -1, .RST_pin = -1, .depth = 0};
char* nvs_item = config_alloc_get(NVS_TYPE_STR, "i2c_config");
if (nvs_item && strlen(nvs_item) > 0) {
bHasi2cConfig = true;
@@ -341,28 +350,24 @@ static int do_i2c_set_display(int argc, char **argv)
if (i2cdisp_args.type->count) {
if (strcasecmp(i2c_name_type, i2cdisp_args.type->sval[0]) == 0) {
config.type = i2c_name_type;
}
else {
} else {
config.type = spi_name_type;
}
}
else {
} else {
config.type = i2c_name_type;
}
/* Check "--address" option */
if (strcasecmp(config.type, "I2C") == 0) {
if (i2cdisp_args.address->count > 0) {
config.address = i2cdisp_args.address->ival[0];
}
else {
} else {
config.address = 60;
}
if (!bHasi2cConfig) {
fprintf(f, "I2C bus has to be configured first. \n");
nerrors++;
}
}
else {
} else {
// SPI Bus connection
if (!bHasSpiConfig) {
fprintf(f, "SPI bus has to be configured first. \n");
@@ -371,8 +376,7 @@ static int do_i2c_set_display(int argc, char **argv)
/* Check "--speed" option */
if (i2cdisp_args.speed->count) {
config.speed = i2cdisp_args.speed->ival[0];
}
else {
} else {
config.speed = 8000000;
}
/* Check "--cs" option */
@@ -380,8 +384,7 @@ static int do_i2c_set_display(int argc, char **argv)
/* Check "--mode" option */
if (i2cdisp_args.mode->count) {
config.mode = i2cdisp_args.mode->ival[0];
}
else {
} else {
config.mode = 0;
}
}
@@ -436,15 +439,12 @@ static int do_i2c_set_display(int argc, char **argv)
return nerrors;
}
static int do_spiconfig_cmd(int argc, char** argv) {
static spi_bus_config_t spi_config = {
.mosi_io_num = -1,
static spi_bus_config_t spi_config = {.mosi_io_num = -1,
.sclk_io_num = -1,
.miso_io_num = -1,
.quadwp_io_num = -1,
.quadhd_io_num = -1
};
.quadhd_io_num = -1};
int dc = -1, host = 0;
esp_err_t err = ESP_OK;
int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&spiconfig_args);
@@ -476,21 +476,25 @@ static int do_spiconfig_cmd(int argc, char **argv){
nerrors += is_output_gpio(spiconfig_args.host, f, &host, true);
if (!nerrors) {
fprintf(f,"Configuring SPI data=%d clock=%d host=%u dc: %d\n", spi_config.mosi_io_num, spi_config.sclk_io_num, host, dc);
fprintf(f, "Configuring SPI data=%d clock=%d host=%u dc: %d\n", spi_config.mosi_io_num,
spi_config.sclk_io_num, host, dc);
err = spi_bus_initialize(host, &spi_config, SPI_DMA_CH_AUTO);
if (err != ESP_OK) {
if (err == ESP_ERR_INVALID_STATE) {
// if user is changing the host number, we need to try freeing both hosts
if((err = spi_bus_free(host))!=ESP_OK && (err = spi_bus_free(host==1?2:1))!=ESP_OK){
fprintf(f,"SPI bus init failed. Please clear SPI configuration, restart the device and try again. %s\n", esp_err_to_name(err));
if ((err = spi_bus_free(host)) != ESP_OK &&
(err = spi_bus_free(host == 1 ? 2 : 1)) != ESP_OK) {
fprintf(f,
"SPI bus init failed. Please clear SPI configuration, restart the device "
"and try again. %s\n",
esp_err_to_name(err));
nerrors++;
}
else if((err=spi_bus_initialize( host, &spi_config, SPI_DMA_CH_AUTO ))!=ESP_OK){
} else if ((err = spi_bus_initialize(host, &spi_config, SPI_DMA_CH_AUTO)) !=
ESP_OK) {
fprintf(f, "Failed to initialize SPI Bus. %s\n", esp_err_to_name(err));
nerrors++;
}
}
else {
} else {
fprintf(f, "SPI bus initialization failed. %s\n", esp_err_to_name(err));
nerrors++;
}
@@ -510,19 +514,16 @@ static int do_spiconfig_cmd(int argc, char **argv){
FREE_AND_NULL(buf);
return nerrors;
}
#endif
static int do_i2cconfig_cmd(int argc, char **argv)
{
static int do_i2cconfig_cmd(int argc, char** argv) {
esp_err_t err = ESP_OK;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
i2c_config_t conf = {.mode = I2C_MODE_MASTER,
.sda_io_num = 19,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = 18,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000
};
.master.clk_speed = 100000};
int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cconfig_args);
/* Check "--clear" option */
@@ -578,12 +579,10 @@ static int do_i2cconfig_cmd(int argc, char **argv)
if ((err = i2c_master_driver_initialize(argv[0], &conf)) == ESP_OK) {
if ((err = i2c_master_driver_install(argv[0])) != ESP_OK) {
nerrors++;
}
else {
} else {
fprintf(f, "i2c driver successfully started.\n");
}
}
else {
} else {
nerrors++;
}
}
@@ -602,8 +601,7 @@ static int do_i2cconfig_cmd(int argc, char **argv)
return nerrors;
}
static int do_i2cdump_cmd(int argc, char **argv)
{
static int do_i2cdump_cmd(int argc, char** argv) {
int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cdump_args);
if (nerrors != 0) {
return 1;
@@ -617,7 +615,8 @@ static int do_i2cdump_cmd(int argc, char **argv)
size = i2cdump_args.size->ival[0];
}
i2c_port_t loc_i2c_port = i2c_port;
if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != ESP_OK) {
if (i2cset_args.port->count &&
i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != ESP_OK) {
return 1;
}
@@ -691,8 +690,8 @@ static int do_i2cdump_cmd(int argc, char **argv)
FREE_AND_NULL(buf);
return 0;
}
static int do_i2cset_cmd(int argc, char **argv)
{
#if CONFIG_WITH_CONFIG_UI
static int do_i2cset_cmd(int argc, char** argv) {
int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cset_args);
if (nerrors != 0) {
return 1;
@@ -707,7 +706,8 @@ static int do_i2cset_cmd(int argc, char **argv)
}
i2c_port_t loc_i2c_port = i2c_port;
if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != ESP_OK) {
if (i2cset_args.port->count &&
i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != ESP_OK) {
return 1;
}
@@ -737,9 +737,8 @@ static int do_i2cset_cmd(int argc, char **argv)
// i2c_driver_delete(i2c_port);
return 0;
}
static int do_i2cget_cmd(int argc, char **argv)
{
#endif
static int do_i2cget_cmd(int argc, char** argv) {
int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cget_args);
if (nerrors != 0) {
return 1;
@@ -758,7 +757,8 @@ static int do_i2cget_cmd(int argc, char **argv)
len = i2cget_args.data_length->ival[0];
}
i2c_port_t loc_i2c_port = i2c_port;
if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != ESP_OK) {
if (i2cset_args.port->count &&
i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != ESP_OK) {
return 1;
}
@@ -846,6 +846,10 @@ esp_err_t cmd_i2ctools_scan_bus(FILE *f,int sda, int scl){
fprintf(f, "I2C driver install failed %s\n", esp_err_to_name(ret));
return ret;
}
#ifndef CONFIG_WITH_CONFIG_UI
fprintf(f, "\n 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
#endif
for (int i = 0; i < 128; i++) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
@@ -855,31 +859,44 @@ esp_err_t cmd_i2ctools_scan_bus(FILE *f,int sda, int scl){
ret = i2c_master_cmd_begin(i2c_port, cmd, 50 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) {
#ifndef CONFIG_WITH_CONFIG_UI
fprintf(f, "%02x ", i);
#endif
matches[++last_match - 1] = i;
}
#ifndef CONFIG_WITH_CONFIG_UI
else if (ret == ESP_ERR_TIMEOUT) {
fprintf(f, "UU ");
} else {
fprintf(f, "-- ");
}
#endif
}
i2c_driver_delete(i2c_port);
if (last_match) {
#if CONFIG_WITH_CONFIG_UI
fprintf(f, "i2c device detected (names provided by https://i2cdevices.org/addresses).\n");
for (int i = 0; i < last_match; i++) {
fprintf(f, "%u [%02xh]- %s\n", matches[i], matches[i], i2c_get_description(matches[i]));
}
}
else {
#endif
} else {
fprintf(f, "No i2c devices found with scl-%d and sda-%d\n", scl, sda);
}
return 0;
}
static int do_i2cdetect_cmd(int argc, char **argv)
{
static int do_i2cdetect_cmd(int argc, char** argv) {
uint8_t matches[128] = {};
int last_match = 0;
esp_err_t ret = ESP_OK;
i2c_port_t loc_i2c_port = i2c_port;
if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != ESP_OK) {
return 1;
}
// if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) !=
// ESP_OK) { return 1;
// }
int i2c_port;
const i2c_config_t* i2c = config_i2c_get(&i2c_port);
uint8_t address;
char* buf = NULL;
@@ -890,6 +907,10 @@ static int do_i2cdetect_cmd(int argc, char **argv)
return 1;
}
if (!GPIO_IS_VALID_OUTPUT_GPIO(i2c->scl_io_num) || !GPIO_IS_VALID_GPIO(i2c->scl_io_num) ||
!GPIO_IS_VALID_OUTPUT_GPIO(i2c->sda_io_num) || !GPIO_IS_VALID_GPIO(i2c->sda_io_num)) {
fprintf(f, "I2C bus configuration invalid.\r\n");
} else {
fprintf(f, "\n 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
for (int i = 0; i < 128; i += 16) {
@@ -913,19 +934,24 @@ static int do_i2cdetect_cmd(int argc, char **argv)
}
fprintf(f, "\r\n");
}
#if CONFIG_WITH_CONFIG_UI
if (last_match) {
fprintf(f,"\r\n------------------------------------------------------------------------------------"
"\r\nDetected the following devices (names provided by https://i2cdevices.org/addresses).");
fprintf(f, "\r\n-----------------------------------------------------------------------"
"-------------"
"\r\nDetected the following devices (names provided by "
"https://i2cdevices.org/addresses).");
for (int i = 0; i < last_match; i++) {
// printf("%02x = %s\r\n", matches[i], i2c_get_description(matches[i]));
fprintf(f,"\r\n%u [%02xh]- %s", matches[i], matches[i], i2c_get_description(matches[i]));
fprintf(f, "\r\n%u [%02xh]- %s", matches[i], matches[i],
i2c_get_description(matches[i]));
}
fprintf(f,"\r\n------------------------------------------------------------------------------------\r\n");
fprintf(f, "\r\n-----------------------------------------------------------------------"
"-------------\r\n");
}
#endif
}
fflush(f);
cmd_send_messaging(argv[0], MESSAGING_INFO, "%s", buf);
fclose(f);
@@ -978,6 +1004,7 @@ cJSON * i2c_set_display_cb(){
return values;
}
#if CONFIG_WITH_CONFIG_UI
static void register_i2c_set_display() {
char* supported_drivers = display_get_supported_drivers();
@@ -987,9 +1014,11 @@ static void register_i2c_set_display(){
i2cdisp_args.reset = arg_int0(NULL, "reset", "<n>", "Reset GPIO");
i2cdisp_args.hflip = arg_lit0(NULL, "hf", "Flip horizontally");
i2cdisp_args.vflip = arg_lit0(NULL, "vf", "Flip vertically");
i2cdisp_args.driver = arg_str0("d", "driver", supported_drivers?supported_drivers:"<string>", "Driver");
i2cdisp_args.driver =
arg_str0("d", "driver", supported_drivers ? supported_drivers : "<string>", "Driver");
i2cdisp_args.cs = arg_int0("b", "cs", "<n>", "SPI Only. CS GPIO (for SPI displays)");
i2cdisp_args.speed = arg_int0("s", "speed", "<n>","SPI Only. Bus Speed (Default 8000000). SPI interface can work up to 26MHz~40MHz");
i2cdisp_args.speed = arg_int0("s", "speed", "<n>",
"SPI Only. Bus Speed (Default 8000000). SPI interface can work up to 26MHz~40MHz");
i2cdisp_args.back = arg_int0("b", "back", "<n>", "Backlight GPIO (if applicable)");
i2cdisp_args.depth = arg_int0("p", "depth", "-1|1|4", "Bit Depth (only for SSD1326 displays)");
i2cdisp_args.type = arg_str0("t", "type", "<I2C|SPI>", "Interface (default I2C)");
@@ -998,82 +1027,76 @@ static void register_i2c_set_display(){
i2cdisp_args.clear = arg_lit0(NULL, "clear", "clear configuration and return");
i2cdisp_args.mode = arg_int0("m", "mode", "<n>", "SPI Only. Transaction Line Mode (Default 0)");
i2cdisp_args.end = arg_end(8);
const esp_console_cmd_t i2c_set_display= {
.command = CFG_TYPE_HW("display"),
const esp_console_cmd_t i2c_set_display = {.command = CFG_TYPE_HW("display"),
.help = desc_display,
.hint = NULL,
.func = &do_i2c_set_display,
.argtable = &i2cdisp_args
};
.argtable = &i2cdisp_args};
cmd_to_json_with_cb(&i2c_set_display, &i2c_set_display_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&i2c_set_display));
}
static void register_i2cdectect(void)
{
const esp_console_cmd_t i2cdetect_cmd = {
.command = "i2cdetect",
#endif
static void register_i2cdectect(void) {
const esp_console_cmd_t i2cdetect_cmd = {.command = "i2cdetect",
.help = "Scan I2C bus for devices",
.hint = NULL,
.func = &do_i2cdetect_cmd,
.argtable = NULL
};
.argtable = NULL};
cmd_to_json(&i2cdetect_cmd);
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cdetect_cmd));
}
static void register_i2cget(void)
{
i2cget_args.chip_address = arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
i2cget_args.register_address = arg_int0("r", "register", "<register_addr>", "Specify the address on that chip to read from");
i2cget_args.data_length = arg_int0("l", "length", "<length>", "Specify the length to read from that data address");
static void register_i2cget(void) {
i2cget_args.chip_address =
arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
i2cget_args.register_address = arg_int0(
"r", "register", "<register_addr>", "Specify the address on that chip to read from");
i2cget_args.data_length =
arg_int0("l", "length", "<length>", "Specify the length to read from that data address");
i2cget_args.end = arg_end(1);
const esp_console_cmd_t i2cget_cmd = {
.command = "i2cget",
const esp_console_cmd_t i2cget_cmd = {.command = "i2cget",
.help = "Read registers visible through the I2C bus",
.hint = NULL,
.func = &do_i2cget_cmd,
.argtable = &i2cget_args
};
.argtable = &i2cget_args};
cmd_to_json(&i2cget_cmd);
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cget_cmd));
}
static void register_i2cset(void)
{
i2cset_args.chip_address = arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
i2cset_args.register_address = arg_int0("r", "register", "<register_addr>", "Specify the address on that chip to read from");
i2cset_args.data = arg_intn(NULL, NULL, "<data>", 0, 256, "Specify the data to write to that data address");
#if CONFIG_WITH_CONFIG_UI
static void register_i2cset(void) {
i2cset_args.chip_address =
arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
i2cset_args.register_address = arg_int0(
"r", "register", "<register_addr>", "Specify the address on that chip to read from");
i2cset_args.data =
arg_intn(NULL, NULL, "<data>", 0, 256, "Specify the data to write to that data address");
i2cset_args.port = arg_intn("p", "port", "<n>", 0, 1, "Specify the i2c port (0|2)");
i2cset_args.end = arg_end(2);
const esp_console_cmd_t i2cset_cmd = {
.command = "i2cset",
const esp_console_cmd_t i2cset_cmd = {.command = "i2cset",
.help = "Set registers visible through the I2C bus",
.hint = NULL,
.func = &do_i2cset_cmd,
.argtable = &i2cset_args
};
.argtable = &i2cset_args};
cmd_to_json(&i2cset_cmd);
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cset_cmd));
}
static void register_i2cdump(void)
{
i2cdump_args.chip_address = arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
#endif
static void register_i2cdump(void) {
i2cdump_args.chip_address =
arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
i2cdump_args.size = arg_int0("s", "size", "<size>", "Specify the size of each read");
i2cdump_args.end = arg_end(3);
const esp_console_cmd_t i2cdump_cmd = {
.command = "i2cdump",
const esp_console_cmd_t i2cdump_cmd = {.command = "i2cdump",
.help = "Examine registers visible through the I2C bus",
.hint = NULL,
.func = &do_i2cdump_cmd,
.argtable = &i2cdump_args
};
.argtable = &i2cdump_args};
cmd_to_json(&i2cdump_cmd);
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cdump_cmd));
}
cJSON* i2config_cb() {
cJSON* values = cJSON_CreateObject();
int i2c_port;
@@ -1109,9 +1132,8 @@ cJSON * spiconfig_cb(){
}
return values;
}
static void register_spiconfig(void)
{
#if CONFIG_WITH_CONFIG_UI
static void register_spiconfig(void) {
spiconfig_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
spiconfig_args.clk = arg_int0("k", "clk", "<n>", "Clock GPIO");
spiconfig_args.data = arg_int0("d", "data", "<n>", "Data OUT GPIO");
@@ -1119,42 +1141,41 @@ static void register_spiconfig(void)
spiconfig_args.dc = arg_int0("c", "dc", "<n>", "DC GPIO");
spiconfig_args.host = arg_int0("h", "host", "1|2", "SPI Host Number");
spiconfig_args.end = arg_end(4);
const esp_console_cmd_t spiconfig_cmd = {
.command = CFG_TYPE_HW("spi"),
const esp_console_cmd_t spiconfig_cmd = {.command = CFG_TYPE_HW("spi"),
.help = desc_spiconfig,
.hint = NULL,
.func = &do_spiconfig_cmd,
.argtable = &spiconfig_args
};
.argtable = &spiconfig_args};
cmd_to_json_with_cb(&spiconfig_cmd, &spiconfig_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&spiconfig_cmd));
}
static void register_i2cconfig(void)
{
#endif
static void register_i2cconfig(void) {
i2cconfig_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
i2cconfig_args.port = arg_int0("p", "port", "0|1", "Port");
i2cconfig_args.freq = arg_int0("f", "speed", "int", "Frequency (Hz) e.g. 100000");
i2cconfig_args.sda = arg_int0("d", "sda", "<n>", "SDA GPIO. e.g. 19");
i2cconfig_args.scl = arg_int0("c", "scl", "<n>", "SCL GPIO. e.g. 18");
i2cconfig_args.end = arg_end(4);
const esp_console_cmd_t i2cconfig_cmd = {
.command = CFG_TYPE_HW("i2c"),
const esp_console_cmd_t i2cconfig_cmd = {.command = CFG_TYPE_HW("i2c"),
.help = desc_i2c,
.hint = NULL,
.func = &do_i2cconfig_cmd,
.argtable = &i2cconfig_args
};
.argtable = &i2cconfig_args};
cmd_to_json_with_cb(&i2cconfig_cmd, &i2config_cb);
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cconfig_cmd));
}
void register_i2ctools(void)
{
void register_i2ctools(void) {
register_i2cconfig();
#if CONFIG_WITH_CONFIG_UI
register_spiconfig();
#endif
register_i2cdectect();
register_i2cget();
#if CONFIG_WITH_CONFIG_UI
register_i2cset();
register_i2cdump();
register_i2c_set_display();
#endif
}

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;
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,6 +428,9 @@ 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 {