Compare commits

..

4 Commits

Author SHA1 Message Date
github-actions
0a653bf374 Update prebuilt objects [skip actions] 2023-10-19 00:09:58 +00:00
philippe44
2a77d09f11 fix Spotify queue modifications - release 2023-10-18 17:07:29 -07:00
philippe44
d03678ea81 misc changes (see PR) (#340)
* misc changes (see PR)

* macro only parsing
2023-10-16 21:50:50 -04:00
github-actions
5019b5bf0f Update prebuilt objects [skip actions] 2023-10-13 16:21:39 +00:00
18 changed files with 138 additions and 160 deletions

View File

@@ -77,7 +77,7 @@ void Batch::push() {
char* json_str = to_json_str(); char* json_str = to_json_str();
ESP_LOGV(TAG, "Metrics payload: %s", json_str); ESP_LOGV(TAG, "Metrics payload: %s", json_str);
time_t start_time = millis(); uint32_t start_time = gettime_ms();
status_code = metrics_http_post_request(json_str, _url); status_code = metrics_http_post_request(json_str, _url);
@@ -85,7 +85,7 @@ void Batch::push() {
_events.clear(); _events.clear();
} }
FREE_AND_NULL(json_str) FREE_AND_NULL(json_str)
ESP_LOGD(TAG, "Total duration for metrics call: %lu. ", millis() - start_time); ESP_LOGD(TAG, "Total duration for metrics call: %lu. ", gettime_ms() - start_time);
} }
void Batch::build_guid() { void Batch::build_guid() {

View File

@@ -45,7 +45,7 @@ class Event {
} }
private: private:
char* _name = nullptr; char* _name = nullptr;
time_t _time; uint32_t _time;
cJSON* _json = nullptr; cJSON* _json = nullptr;
}; };

View File

@@ -31,7 +31,7 @@ extern bool is_network_connected();
#define MAX_HTTP_RECV_BUFFER 512 #define MAX_HTTP_RECV_BUFFER 512
static bool metrics_usage_gen = false; static bool metrics_usage_gen = false;
static time_t metrics_usage_gen_time = 0; static uint32_t metrics_usage_gen_time = 0;
#ifndef METRICS_API_KEY #ifndef METRICS_API_KEY
#pragma message "Metrics API key needs to be passed from the environment" #pragma message "Metrics API key needs to be passed from the environment"
#define METRICS_API_KEY "ZZZ" #define METRICS_API_KEY "ZZZ"
@@ -56,7 +56,7 @@ static void metrics_timer_cb(void* timer_id) {
batch.push(); batch.push();
} }
} }
if (millis() > metrics_usage_gen_time && !metrics_usage_gen) { if (gettime_ms() > metrics_usage_gen_time && !metrics_usage_gen) {
metrics_usage_gen = true; metrics_usage_gen = true;
ESP_LOGV(TAG, "Generate command list to pull features"); ESP_LOGV(TAG, "Generate command list to pull features");
cJSON* cmdlist = get_cmd_list(); cJSON* cmdlist = get_cmd_list();
@@ -75,7 +75,7 @@ void metrics_init() {
} }
// set a 20 seconds delay before generating the // set a 20 seconds delay before generating the
// features so the system has time to boot // features so the system has time to boot
metrics_usage_gen_time = millis() + 20000; metrics_usage_gen_time = gettime_ms() + 20000;
} }
void metrics_event_playback(const char* source) { void metrics_event_playback(const char* source) {

View File

@@ -538,7 +538,7 @@ bool config_set_group_bit(int bit_num,bool flag){
return result; return result;
} }
void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size) { void config_set_default(nvs_type_t type, const char *key, const void * default_value, size_t blob_size) {
if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){ if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
ESP_LOGE(TAG, "Unable to lock config"); ESP_LOGE(TAG, "Unable to lock config");
return; return;
@@ -791,43 +791,6 @@ cJSON* cjson_update_number(cJSON** root, const char* key, int value) {
} }
return *root; 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(uint8_t,NVS_TYPE_U8);
IMPLEMENT_SET_DEFAULT(int8_t,NVS_TYPE_I8); IMPLEMENT_SET_DEFAULT(int8_t,NVS_TYPE_I8);

View File

@@ -9,11 +9,6 @@
extern "C" { extern "C" {
#endif #endif
#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 { \ #define PARSE_PARAM(S,P,C,V) do { \
char *__p; \ char *__p; \
if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \ if ((__p = strcasestr(S, P)) && (__p = strchr(__p, C))) V = atoi(__p+1); \
@@ -31,7 +26,7 @@ extern "C" {
sscanf(__p,"%" #I "[^,]", V); \ sscanf(__p,"%" #I "[^,]", V); \
} \ } \
} while (0) } while (0)
#endif
#define DECLARE_SET_DEFAULT(t) void config_set_default_## t (const char *key, t value); #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); #define DECLARE_GET_NUM(t) esp_err_t config_get_## t (const char *key, t * value);
#ifndef FREE_RESET #ifndef FREE_RESET
@@ -55,9 +50,6 @@ bool config_has_changes();
void config_commit_to_nvs(); void config_commit_to_nvs();
void config_start_timer(); void config_start_timer();
void config_init(); 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_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); void * config_alloc_get_str(const char *key, char *lead, char *fallback);
cJSON * config_alloc_get_cjson(const char *key); cJSON * config_alloc_get_cjson(const char *key);
@@ -65,7 +57,7 @@ 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); 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_get_uint16t_from_str(const char *key, uint16_t *value, uint16_t default_value);
void config_delete_key(const char *key); 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); void config_set_default(nvs_type_t type, const char *key, const void * default_value, size_t blob_size);
void * config_alloc_get(nvs_type_t nvs_type, const char *key) ; void * config_alloc_get(nvs_type_t nvs_type, const char *key) ;
bool wait_for_commit(); bool wait_for_commit();
char * config_alloc_get_json(bool bFormatted); char * config_alloc_get_json(bool bFormatted);

View File

@@ -61,11 +61,13 @@ static struct {
struct arg_end* end; struct arg_end* end;
} i2cset_args; } i2cset_args;
#if CONFIG_WITH_CONFIG_UI
static struct { static struct {
struct arg_int* chip_address; struct arg_int* chip_address;
struct arg_int* size; struct arg_int* size;
struct arg_end* end; struct arg_end* end;
} i2cdump_args; } i2cdump_args;
#endif
static struct { static struct {
struct arg_int* port; struct arg_int* port;
@@ -560,12 +562,13 @@ static int do_i2cconfig_cmd(int argc, char** argv) {
nerrors += is_output_gpio(i2cconfig_args.sda, f, &conf.sda_io_num, true); nerrors += is_output_gpio(i2cconfig_args.sda, f, &conf.sda_io_num, true);
nerrors += is_output_gpio(i2cconfig_args.scl, f, &conf.scl_io_num, true); nerrors += is_output_gpio(i2cconfig_args.scl, f, &conf.scl_io_num, true);
#ifdef CONFIG_SQUEEZEAMP #ifdef CONFIG_I2C_LOCKED
if (i2c_port == I2C_NUM_0) { if (i2c_port == I2C_NUM_0) {
i2c_port = I2C_NUM_1; i2c_port = I2C_NUM_1;
fprintf(f, "can't use i2c port 0 on SqueezeAMP. Changing to port 1.\n"); fprintf(f, "can't use i2c port 0 when locked by config. Changing to port 1.\n");
} }
#endif #endif
if (!nerrors) { if (!nerrors) {
fprintf(f, "Uninstalling i2c driver from port %u if needed\n", i2c_port); fprintf(f, "Uninstalling i2c driver from port %u if needed\n", i2c_port);
if (is_i2c_started(i2c_port)) { if (is_i2c_started(i2c_port)) {
@@ -601,6 +604,7 @@ static int do_i2cconfig_cmd(int argc, char** argv) {
return nerrors; return nerrors;
} }
#if CONFIG_WITH_CONFIG_UI
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); int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cdump_args);
if (nerrors != 0) { if (nerrors != 0) {
@@ -690,7 +694,7 @@ static int do_i2cdump_cmd(int argc, char** argv) {
FREE_AND_NULL(buf); FREE_AND_NULL(buf);
return 0; return 0;
} }
#if CONFIG_WITH_CONFIG_UI
static int do_i2cset_cmd(int argc, char** argv) { static int do_i2cset_cmd(int argc, char** argv) {
int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cset_args); int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cset_args);
if (nerrors != 0) { if (nerrors != 0) {
@@ -738,6 +742,7 @@ static int do_i2cset_cmd(int argc, char** argv) {
return 0; return 0;
} }
#endif #endif
static int do_i2cget_cmd(int argc, char** argv) { static int do_i2cget_cmd(int argc, char** argv) {
int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cget_args); int nerrors = arg_parse_msg(argc, argv, (struct arg_hdr**)&i2cget_args);
if (nerrors != 0) { if (nerrors != 0) {
@@ -811,7 +816,9 @@ static int do_i2cget_cmd(int argc, char** argv) {
return 0; return 0;
} }
esp_err_t cmd_i2ctools_scan_bus(FILE* f, int sda, int scl) { esp_err_t cmd_i2ctools_scan_bus(FILE* f, int sda, int scl) {
#ifdef CONFIG_WITH_CONFIG_UI
uint8_t matches[128] = {}; uint8_t matches[128] = {};
#endif
int last_match = 0; int last_match = 0;
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
@@ -861,8 +868,9 @@ esp_err_t cmd_i2ctools_scan_bus(FILE* f, int sda, int scl) {
if (ret == ESP_OK) { if (ret == ESP_OK) {
#ifndef CONFIG_WITH_CONFIG_UI #ifndef CONFIG_WITH_CONFIG_UI
fprintf(f, "%02x ", i); fprintf(f, "%02x ", i);
#endif #else
matches[++last_match - 1] = i; matches[++last_match - 1] = i;
#endif
} }
#ifndef CONFIG_WITH_CONFIG_UI #ifndef CONFIG_WITH_CONFIG_UI
else if (ret == ESP_ERR_TIMEOUT) { else if (ret == ESP_ERR_TIMEOUT) {
@@ -888,8 +896,10 @@ esp_err_t cmd_i2ctools_scan_bus(FILE* f, int sda, int scl) {
return 0; return 0;
} }
static int do_i2cdetect_cmd(int argc, char** argv) { static int do_i2cdetect_cmd(int argc, char** argv) {
#ifdef CONFIG_WITH_CONFIG_UI
uint8_t matches[128] = {}; uint8_t matches[128] = {};
int last_match = 0; int last_match = 0;
#endif
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
i2c_port_t loc_i2c_port = i2c_port; i2c_port_t loc_i2c_port = i2c_port;
// if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) != // if (i2cset_args.port->count && i2c_get_port(i2cset_args.port->ival[0], &loc_i2c_port) !=
@@ -925,7 +935,9 @@ static int do_i2cdetect_cmd(int argc, char** argv) {
i2c_cmd_link_delete(cmd); i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) { if (ret == ESP_OK) {
fprintf(f, "%02x ", address); fprintf(f, "%02x ", address);
#ifdef CONFIG_WITH_CONFIG_UI
matches[++last_match - 1] = address; matches[++last_match - 1] = address;
#endif
} else if (ret == ESP_ERR_TIMEOUT) { } else if (ret == ESP_ERR_TIMEOUT) {
fprintf(f, "UU "); fprintf(f, "UU ");
} else { } else {
@@ -1082,7 +1094,7 @@ static void register_i2cset(void) {
cmd_to_json(&i2cset_cmd); cmd_to_json(&i2cset_cmd);
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cset_cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&i2cset_cmd));
} }
#endif
static void register_i2cdump(void) { static void register_i2cdump(void) {
i2cdump_args.chip_address = i2cdump_args.chip_address =
arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus"); arg_int1("c", "chip", "<chip_addr>", "Specify the address of the chip on that bus");
@@ -1096,6 +1108,7 @@ static void register_i2cdump(void) {
cmd_to_json(&i2cdump_cmd); cmd_to_json(&i2cdump_cmd);
ESP_ERROR_CHECK(esp_console_cmd_register(&i2cdump_cmd)); ESP_ERROR_CHECK(esp_console_cmd_register(&i2cdump_cmd));
} }
#endif
cJSON* i2config_cb() { cJSON* i2config_cb() {
cJSON* values = cJSON_CreateObject(); cJSON* values = cJSON_CreateObject();

View File

@@ -67,8 +67,10 @@ static void register_heap();
static void register_dump_heap(); static void register_dump_heap();
static void register_version(); static void register_version();
static void register_restart(); static void register_restart();
#if CONFIG_WITH_CONFIG_UI
static void register_deep_sleep(); static void register_deep_sleep();
static void register_light_sleep(); static void register_light_sleep();
#endif
static void register_factory_boot(); static void register_factory_boot();
static void register_restart_ota(); static void register_restart_ota();
static void register_set_services(); static void register_set_services();
@@ -556,6 +558,7 @@ static void register_tasks()
/** 'deep_sleep' command puts the chip into deep sleep mode */ /** 'deep_sleep' command puts the chip into deep sleep mode */
#if CONFIG_WITH_CONFIG_UI
static struct { static struct {
struct arg_int *wakeup_time; struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num; struct arg_int *wakeup_gpio_num;
@@ -619,6 +622,8 @@ static void register_deep_sleep()
}; };
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
} }
#endif
static int enable_disable(FILE * f,char * nvs_name, struct arg_lit *arg){ static int enable_disable(FILE * f,char * nvs_name, struct arg_lit *arg){
esp_err_t err = config_set_value(NVS_TYPE_STR, nvs_name, arg->count>0?"Y":"N"); esp_err_t err = config_set_value(NVS_TYPE_STR, nvs_name, arg->count>0?"Y":"N");
const char * name = arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary; const char * name = arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary;
@@ -737,6 +742,8 @@ static void register_set_services(){
cmd_to_json_with_cb(&cmd,&set_services_cb); cmd_to_json_with_cb(&cmd,&set_services_cb);
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
} }
#if CONFIG_WITH_CONFIG_UI
static struct { static struct {
struct arg_int *wakeup_time; struct arg_int *wakeup_time;
struct arg_int *wakeup_gpio_num; struct arg_int *wakeup_gpio_num;
@@ -828,4 +835,4 @@ static void register_light_sleep()
}; };
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
} }
#endif

View File

@@ -53,6 +53,7 @@ private:
std::atomic<states> state; std::atomic<states> state;
std::string credentials; std::string credentials;
bool zeroConf; bool zeroConf;
std::atomic<bool> flushed = false, notify = true;
int startOffset, volume = 0, bitrate = 160; int startOffset, volume = 0, bitrate = 160;
httpd_handle_t serverHandle; httpd_handle_t serverHandle;
@@ -60,6 +61,7 @@ private:
cspot_cmd_cb_t cmdHandler; cspot_cmd_cb_t cmdHandler;
cspot_data_cb_t dataHandler; cspot_data_cb_t dataHandler;
std::string lastTrackId; std::string lastTrackId;
cspot::TrackInfo trackInfo;
std::shared_ptr<cspot::LoginBlob> blob; std::shared_ptr<cspot::LoginBlob> blob;
std::unique_ptr<cspot::SpircHandler> spirc; std::unique_ptr<cspot::SpircHandler> spirc;
@@ -206,11 +208,13 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
trackStatus = TRACK_INIT; trackStatus = TRACK_INIT;
// memorize position for when track's beginning will be detected // memorize position for when track's beginning will be detected
startOffset = std::get<int>(event->data); startOffset = std::get<int>(event->data);
notify = !flushed;
flushed = false;
// Spotify servers do not send volume at connection // Spotify servers do not send volume at connection
spirc->setRemoteVolume(volume); spirc->setRemoteVolume(volume);
cmdHandler(CSPOT_START, 44100); cmdHandler(CSPOT_START, 44100);
CSPOT_LOG(info, "(re)start playing"); CSPOT_LOG(info, "(re)start playing at %d", startOffset);
break; break;
} }
case cspot::SpircHandler::EventType::PLAY_PAUSE: { case cspot::SpircHandler::EventType::PLAY_PAUSE: {
@@ -219,16 +223,14 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
break; break;
} }
case cspot::SpircHandler::EventType::TRACK_INFO: { case cspot::SpircHandler::EventType::TRACK_INFO: {
auto trackInfo = std::get<cspot::TrackInfo>(event->data); trackInfo = std::get<cspot::TrackInfo>(event->data);
cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
spirc->updatePositionMs(startOffset);
startOffset = 0;
break; break;
} }
case cspot::SpircHandler::EventType::FLUSH:
flushed = true;
__attribute__ ((fallthrough));
case cspot::SpircHandler::EventType::NEXT: case cspot::SpircHandler::EventType::NEXT:
case cspot::SpircHandler::EventType::PREV: case cspot::SpircHandler::EventType::PREV: {
case cspot::SpircHandler::EventType::FLUSH: {
cmdHandler(CSPOT_FLUSH); cmdHandler(CSPOT_FLUSH);
break; break;
} }
@@ -411,8 +413,13 @@ void cspotPlayer::runTask() {
uint32_t started; uint32_t started;
cmdHandler(CSPOT_QUERY_STARTED, &started); cmdHandler(CSPOT_QUERY_STARTED, &started);
if (started) { if (started) {
CSPOT_LOG(info, "next track's audio has reached DAC"); CSPOT_LOG(info, "next track's audio has reached DAC (offset %d)", startOffset);
spirc->notifyAudioReachedPlayback(); if (notify) spirc->notifyAudioReachedPlayback();
else notify = true;
cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
spirc->updatePositionMs(startOffset);
startOffset = 0;
trackStatus = TRACK_STREAM; trackStatus = TRACK_STREAM;
} }
} else if (trackStatus == TRACK_END) { } else if (trackStatus == TRACK_END) {

View File

@@ -204,8 +204,13 @@ void SpircHandler::handleFrame(std::vector<uint8_t>& data) {
CSPOT_LOG(debug, "Got replace frame"); CSPOT_LOG(debug, "Got replace frame");
playbackState->syncWithRemote(); playbackState->syncWithRemote();
trackQueue->updateTracks(playbackState->remoteFrame.state.position_ms, // 1st track is the current one, but update the position
trackQueue->updateTracks(
playbackState->remoteFrame.state.position_ms +
ctx->timeProvider->getSyncedTimestamp() -
playbackState->innerFrame.state.position_measured_at,
false); false);
this->notify(); this->notify();
sendEvent(EventType::FLUSH); sendEvent(EventType::FLUSH);

View File

@@ -11,8 +11,6 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_task.h" #include "esp_task.h"
#include "esp_tls.h" #include "esp_tls.h"
#include "esp_http_client.h" #include "esp_http_client.h"
@@ -24,7 +22,6 @@
#error CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS must be at least 2 #error CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS must be at least 2
#endif #endif
#include "cJSON.h"
const static char TAG[] = "tools"; const static char TAG[] = "tools";
/**************************************************************************************** /****************************************************************************************
@@ -328,13 +325,6 @@ static esp_err_t http_event_handler(esp_http_client_event_t *evt) {
return ESP_OK; 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) { void dump_json_content(const char* prefix, cJSON* json, int level) {
if (!json) { if (!json) {
ESP_LOG_LEVEL(level,TAG, "%s: empty!", prefix); ESP_LOG_LEVEL(level,TAG, "%s: empty!", prefix);

View File

@@ -9,10 +9,9 @@
*/ */
#pragma once #pragma once
#include "cJSON.h"
#include "time.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "cJSON.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -56,6 +55,13 @@ void* clone_obj_psram(void * source, size_t source_sz);
char* strdup_psram(const char * source); char* strdup_psram(const char * source);
const char* str_or_unknown(const char * str); const char* str_or_unknown(const char * str);
const char* str_or_null(const char * str); const char* str_or_null(const char * str);
void dump_json_content(const char* prefix, cJSON* json, int level);
#ifndef gettime_ms
// body is provided somewhere else...
uint32_t _gettime_ms_(void);
#define gettime_ms _gettime_ms_
#endif
typedef void (*http_download_cb_t)(uint8_t* data, size_t len, void *context); typedef void (*http_download_cb_t)(uint8_t* data, size_t len, void *context);
void http_download(char *url, size_t max, http_download_cb_t callback, void *context); void http_download(char *url, size_t max, http_download_cb_t callback, void *context);
@@ -74,9 +80,6 @@ void vTaskDeleteEXTRAM(TaskHandle_t xTask);
extern const char unknown_string_placeholder[]; extern const char unknown_string_placeholder[];
time_t millis();
void dump_json_content(const char* prefix, cJSON* json, int level);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

Binary file not shown.

Binary file not shown.

View File

@@ -144,18 +144,18 @@ LxoOkcXsNb/12jOV3iQSDfXDI41AgtFc694KCOjlg+UKizpemE53T5/cq37OqChP
qnlPyb6PYIhua/kgbH84ltba1xEDQ9i4UYfOMiJNZEzEdSfQ498= qnlPyb6PYIhua/kgbH84ltba1xEDQ9i4UYfOMiJNZEzEdSfQ498=
-----END CERTIFICATE----- -----END CERTIFICATE-----
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIIXzCCB0egAwIBAgIQBKU3dDawNcfrgz6Dj4jGVjANBgkqhkiG9w0BAQsFADA8 MIIIOzCCByOgAwIBAgIQDMnYcxFdn1+HZt0ULgDEwTANBgkqhkiG9w0BAQsFADA8
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
UlNBIDIwNDggTTAxMB4XDTIzMDcxMDAwMDAwMFoXDTI0MDYyMTIzNTk1OVowGzEZ UlNBIDIwNDggTTAxMB4XDTIzMTAxMDAwMDAwMFoXDTI0MDcxMDIzNTk1OVowGzEZ
MBcGA1UEAxMQczMuYW1hem9uYXdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP MBcGA1UEAxMQczMuYW1hem9uYXdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMhVo8GFFI8V3qJuBvo9Mcp9O2FkASu1No2kmnXcZn6rqV5Z9Rdz ADCCAQoCggEBANbrGvFbxrAM6TKussVOuHpCNsZX1V+jR6TAfzrO8VQWqiz+DCq+
DSTMBkgBz1UNxyOJ4HujSSjgqrDJ0Fv2uZ+xUfwh8wsZagpoMp6D53nOqzZxwmzi LlGFT2is2kRxY+hv2NswgK+Ie4SUdypQDrJ+mKFadmM2UOqIcFepA4EIUESAXZHg
rs7iGbBHZ0TvROel/zUEayjwm1CpEhM6fPGArITcAUcBZ0kaDLrStyijuqaQQasP lHJAv5460i6gp9Lh6imN3jGjC1Ax0rgrfBZA+uzIy8dorBEptIj/YpE4wD4WPHnk
cnjcsTili+vWk4VhPNXKg9lkwQaWmcj/iUENx2l4Fqe6e93CmlqhYPMyf4lH2oiz Y+wCDfMZdm6b6hqsrOkbqBWHH2zSH8TWfnYRguGe3NkLpM4pBNa2lTb1GBeZtm/W
MX7WZdR1w2bMIP/+J1AAuoll2y0CF60/7w+NidmpGQyujOUV2YPFYhJc0mLRyY0c TB3dEAaxcGCu+WQbicI6BxkbzklgxKTHZ2Lc3mL8P5Ca1yWQG3asdUxw0SIkCZVd
btSo589TxMmhGbKwMywKQbXLueAC/CK0fUUCAwEAAaOCBXwwggV4MB8GA1UdIwQY aCzQxqtLlXSxpe4xK2KjOfq2ZM+MZhq/tR0CAwEAAaOCBVgwggVUMB8GA1UdIwQY
MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBSP/UslcNwoVtOJhOQj MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBQi0miDsNb3pJw4EouH
scAlxpUVVjCCAqwGA1UdEQSCAqMwggKfghBzMy5hbWF6b25hd3MuY29tghIqLnMz AR3hoCAaqzCCAokGA1UdEQSCAoAwggJ8ghBzMy5hbWF6b25hd3MuY29tghIqLnMz
LmFtYXpvbmF3cy5jb22CJiouczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h LmFtYXpvbmF3cy5jb22CJiouczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiRzMy5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CHCou d3MuY29tgiRzMy5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CHCou
czMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CGnMzLnVzLWVhc3QtMS5hbWF6b25h czMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CGnMzLnVzLWVhc3QtMS5hbWF6b25h
@@ -164,29 +164,28 @@ LWNvbnRyb2wudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLiouczMtY29udHJvbC5k
dWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLHMzLWNvbnRyb2wuZHVh dWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLHMzLWNvbnRyb2wuZHVh
bHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgigqLnMzLWFjY2Vzc3BvaW50 bHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgigqLnMzLWFjY2Vzc3BvaW50
LnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgjIqLnMzLWFjY2Vzc3BvaW50LmR1YWxz LnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgjIqLnMzLWFjY2Vzc3BvaW50LmR1YWxz
dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIhKi5zMy51cy1lYXN0LTEudnBj dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYInKi5zMy1kZXByZWNhdGVkLnVz
ZS5hbWF6b25hd3MuY29tgicqLnMzLWRlcHJlY2F0ZWQudXMtZWFzdC0xLmFtYXpv LWVhc3QtMS5hbWF6b25hd3MuY29tgiVzMy1kZXByZWNhdGVkLnVzLWVhc3QtMS5h
bmF3cy5jb22CJXMzLWRlcHJlY2F0ZWQudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22C bWF6b25hd3MuY29tghtzMy1leHRlcm5hbC0xLmFtYXpvbmF3cy5jb22CHSouczMt
G3MzLWV4dGVybmFsLTEuYW1hem9uYXdzLmNvbYIdKi5zMy1leHRlcm5hbC0xLmFt ZXh0ZXJuYWwtMS5hbWF6b25hd3MuY29tghtzMy1leHRlcm5hbC0yLmFtYXpvbmF3
YXpvbmF3cy5jb22CG3MzLWV4dGVybmFsLTIuYW1hem9uYXdzLmNvbYIdKi5zMy1l cy5jb22CHSouczMtZXh0ZXJuYWwtMi5hbWF6b25hd3MuY29tMBMGA1UdIAQMMAow
eHRlcm5hbC0yLmFtYXpvbmF3cy5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW CAYGZ4EMAQIBMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8v KwYBBQUHAwIwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5yMm0wMS5hbWF6
Y3JsLnIybTAxLmFtYXpvbnRydXN0LmNvbS9yMm0wMS5jcmwwEwYDVR0gBAwwCjAI b250cnVzdC5jb20vcjJtMDEuY3JsMHUGCCsGAQUFBwEBBGkwZzAtBggrBgEFBQcw
BgZngQwBAgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsGAQUFBzABhiFodHRwOi8vb2Nz AYYhaHR0cDovL29jc3AucjJtMDEuYW1hem9udHJ1c3QuY29tMDYGCCsGAQUFBzAC
cC5yMm0wMS5hbWF6b250cnVzdC5jb20wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQu hipodHRwOi8vY3J0LnIybTAxLmFtYXpvbnRydXN0LmNvbS9yMm0wMS5jZXIwDAYD
cjJtMDEuYW1hem9udHJ1c3QuY29tL3IybTAxLmNlcjAMBgNVHRMBAf8EAjAAMIIB VR0TAQH/BAIwADCCAX0GCisGAQQB1nkCBAIEggFtBIIBaQFnAHYA7s3QZNXbGs7F
fgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdwDuzdBk1dsazsVct520zROiModGfLzs XLedtM0TojKHRny87N7DUUhZRnEftZsAAAGLG2S5PwAABAMARzBFAiEAxOJvK1tq
3sNRSFlGcR+1mwAAAYlCAMrQAAAEAwBIMEYCIQCJhWcUFTgBlQtPboTv0HorPNkz gzf5d6NPN4PZui5PhQePCg1bH/fVMd7T5ekCIHeOglhFW5BC0TNeHY+cfUOvDONT
l+/o8Fr1hFYfs+8bIQIhAOaCRLr5774k0iflsslqBMmubN3NC+mImezWIaMHPP26 DV4fAVcaWsBXErw9AHUASLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMA
AHUA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGJQgDK8gAABAMA AAGLG2S5LwAABAMARjBEAiBYu16xOFIzUE9aMrB1vrFkTsMC3veucw9k0Hv4qO2v
RjBEAiBalCI6KSCFXMTY15CgLEHtqeE/vyihg8FWthX4JgPcFwIgWkI//yF+tqBi XAIgTTMRV9sq+ytuTU53XdoKLF33qeUZHGqop7O7xoFEENMAdgDatr9rP7W2Ip+b
Gs3sHuuGvG4v7rytkUM4Dli6sWAYXsYAdgA7U3d1Pi25gE6LMFsG/kA7Z9hPw/TH wrtca+hwkXFsu1GEhTS9pD0wSNf7qwAAAYsbZLlmAAAEAwBHMEUCIQCD6n2r6DVx
vQANLXJv4frUFwAAAYlCAMsyAAAEAwBHMEUCIQDfn+j2sutaXvO5yZ86z6OFgq1u UMUgtfPRi3ieJdlObPzsrc0aGVhl6gCP5QIgbzkLqEOfJ7bozTgYzUO8ZDvAd0te
WhNwM2MnEHfGhO+yCAIgLYi8SX6hZRGRZFxIrV1ekcRSk+9yN8oHNfOQOuGQQOEw cdpZv182ZEQ974wwDQYJKoZIhvcNAQELBQADggEBAHEAZnHOPS7CoRXWTqWbQu5V
DQYJKoZIhvcNAQELBQADggEBAIprc7vYrAerkphosDZ2frlk9gYBnlgTYT0wJXLC 8x2uFNl5jjsbSH68xnviW24ShllvFyHH1iD2R3ov/3Ri8T8TJ0bA14u+3D/iPZk1
4ZTnlhXuMbL5gYmM8T1suPv5p2JphVFEsSS8PlmdQX45mPAlk8lHjHSZJRgL/r1Y BgX2/YdL/0/CptgISLhBtugBk2+MsRry6i7hhbf4/pIyL1uWHUeDuYrW882Xw1ul
7wWYjKB3oHl5N/5UZTtDTtfxtjd/3Vts6citxzhVwGxH+fQspligvb1Sgy8/ojjV uD1wyC1z2CqXZWy0WL0zMV6Yfp/eFnCJ9tj0NoullHGhgk9RTU3qULwuAQmzMqS7
lSz+QwpK2PrxRwvWPt0XImPj+5xPi8MOhRCh32Edtqy5fQkZiG/mYEvMMpq73kv0 XlhmgdzVPDq3s8qCawHDU6yDdT5FAO3SJ23Dj2efM7enBzq0jryfEZn9vTCRJfKI
uWbcjMFN2XbSOSYkjjseVoTBly65MAxFyAFAdg9HMJeJ5hbegpl4PARhgdL8sG3h yCZT56FgMdEqvmQutAV5UCiL1acK8YU2zUQSxuWn+AyDivV4JSOYw8ho1yZrZZI=
jEenWaFiCjwQ4iCPFeGk+vumNkL1c5OaLFZwhoZQ+KLkAQc=
-----END CERTIFICATE----- -----END CERTIFICATE-----

BIN
server_certs/r2m01.cer.32 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.33 Normal file

Binary file not shown.

View File

@@ -1,16 +1,16 @@
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----
MIIIXzCCB0egAwIBAgIQBKU3dDawNcfrgz6Dj4jGVjANBgkqhkiG9w0BAQsFADA8 MIIIOzCCByOgAwIBAgIQDMnYcxFdn1+HZt0ULgDEwTANBgkqhkiG9w0BAQsFADA8
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
UlNBIDIwNDggTTAxMB4XDTIzMDcxMDAwMDAwMFoXDTI0MDYyMTIzNTk1OVowGzEZ UlNBIDIwNDggTTAxMB4XDTIzMTAxMDAwMDAwMFoXDTI0MDcxMDIzNTk1OVowGzEZ
MBcGA1UEAxMQczMuYW1hem9uYXdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP MBcGA1UEAxMQczMuYW1hem9uYXdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMhVo8GFFI8V3qJuBvo9Mcp9O2FkASu1No2kmnXcZn6rqV5Z9Rdz ADCCAQoCggEBANbrGvFbxrAM6TKussVOuHpCNsZX1V+jR6TAfzrO8VQWqiz+DCq+
DSTMBkgBz1UNxyOJ4HujSSjgqrDJ0Fv2uZ+xUfwh8wsZagpoMp6D53nOqzZxwmzi LlGFT2is2kRxY+hv2NswgK+Ie4SUdypQDrJ+mKFadmM2UOqIcFepA4EIUESAXZHg
rs7iGbBHZ0TvROel/zUEayjwm1CpEhM6fPGArITcAUcBZ0kaDLrStyijuqaQQasP lHJAv5460i6gp9Lh6imN3jGjC1Ax0rgrfBZA+uzIy8dorBEptIj/YpE4wD4WPHnk
cnjcsTili+vWk4VhPNXKg9lkwQaWmcj/iUENx2l4Fqe6e93CmlqhYPMyf4lH2oiz Y+wCDfMZdm6b6hqsrOkbqBWHH2zSH8TWfnYRguGe3NkLpM4pBNa2lTb1GBeZtm/W
MX7WZdR1w2bMIP/+J1AAuoll2y0CF60/7w+NidmpGQyujOUV2YPFYhJc0mLRyY0c TB3dEAaxcGCu+WQbicI6BxkbzklgxKTHZ2Lc3mL8P5Ca1yWQG3asdUxw0SIkCZVd
btSo589TxMmhGbKwMywKQbXLueAC/CK0fUUCAwEAAaOCBXwwggV4MB8GA1UdIwQY aCzQxqtLlXSxpe4xK2KjOfq2ZM+MZhq/tR0CAwEAAaOCBVgwggVUMB8GA1UdIwQY
MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBSP/UslcNwoVtOJhOQj MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBQi0miDsNb3pJw4EouH
scAlxpUVVjCCAqwGA1UdEQSCAqMwggKfghBzMy5hbWF6b25hd3MuY29tghIqLnMz AR3hoCAaqzCCAokGA1UdEQSCAoAwggJ8ghBzMy5hbWF6b25hd3MuY29tghIqLnMz
LmFtYXpvbmF3cy5jb22CJiouczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h LmFtYXpvbmF3cy5jb22CJiouczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiRzMy5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CHCou d3MuY29tgiRzMy5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CHCou
czMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CGnMzLnVzLWVhc3QtMS5hbWF6b25h czMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CGnMzLnVzLWVhc3QtMS5hbWF6b25h
@@ -19,29 +19,28 @@ LWNvbnRyb2wudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLiouczMtY29udHJvbC5k
dWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLHMzLWNvbnRyb2wuZHVh dWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLHMzLWNvbnRyb2wuZHVh
bHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgigqLnMzLWFjY2Vzc3BvaW50 bHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgigqLnMzLWFjY2Vzc3BvaW50
LnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgjIqLnMzLWFjY2Vzc3BvaW50LmR1YWxz LnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgjIqLnMzLWFjY2Vzc3BvaW50LmR1YWxz
dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIhKi5zMy51cy1lYXN0LTEudnBj dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYInKi5zMy1kZXByZWNhdGVkLnVz
ZS5hbWF6b25hd3MuY29tgicqLnMzLWRlcHJlY2F0ZWQudXMtZWFzdC0xLmFtYXpv LWVhc3QtMS5hbWF6b25hd3MuY29tgiVzMy1kZXByZWNhdGVkLnVzLWVhc3QtMS5h
bmF3cy5jb22CJXMzLWRlcHJlY2F0ZWQudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22C bWF6b25hd3MuY29tghtzMy1leHRlcm5hbC0xLmFtYXpvbmF3cy5jb22CHSouczMt
G3MzLWV4dGVybmFsLTEuYW1hem9uYXdzLmNvbYIdKi5zMy1leHRlcm5hbC0xLmFt ZXh0ZXJuYWwtMS5hbWF6b25hd3MuY29tghtzMy1leHRlcm5hbC0yLmFtYXpvbmF3
YXpvbmF3cy5jb22CG3MzLWV4dGVybmFsLTIuYW1hem9uYXdzLmNvbYIdKi5zMy1l cy5jb22CHSouczMtZXh0ZXJuYWwtMi5hbWF6b25hd3MuY29tMBMGA1UdIAQMMAow
eHRlcm5hbC0yLmFtYXpvbmF3cy5jb20wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQW CAYGZ4EMAQIBMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8v KwYBBQUHAwIwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5yMm0wMS5hbWF6
Y3JsLnIybTAxLmFtYXpvbnRydXN0LmNvbS9yMm0wMS5jcmwwEwYDVR0gBAwwCjAI b250cnVzdC5jb20vcjJtMDEuY3JsMHUGCCsGAQUFBwEBBGkwZzAtBggrBgEFBQcw
BgZngQwBAgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsGAQUFBzABhiFodHRwOi8vb2Nz AYYhaHR0cDovL29jc3AucjJtMDEuYW1hem9udHJ1c3QuY29tMDYGCCsGAQUFBzAC
cC5yMm0wMS5hbWF6b250cnVzdC5jb20wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQu hipodHRwOi8vY3J0LnIybTAxLmFtYXpvbnRydXN0LmNvbS9yMm0wMS5jZXIwDAYD
cjJtMDEuYW1hem9udHJ1c3QuY29tL3IybTAxLmNlcjAMBgNVHRMBAf8EAjAAMIIB VR0TAQH/BAIwADCCAX0GCisGAQQB1nkCBAIEggFtBIIBaQFnAHYA7s3QZNXbGs7F
fgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdwDuzdBk1dsazsVct520zROiModGfLzs XLedtM0TojKHRny87N7DUUhZRnEftZsAAAGLG2S5PwAABAMARzBFAiEAxOJvK1tq
3sNRSFlGcR+1mwAAAYlCAMrQAAAEAwBIMEYCIQCJhWcUFTgBlQtPboTv0HorPNkz gzf5d6NPN4PZui5PhQePCg1bH/fVMd7T5ekCIHeOglhFW5BC0TNeHY+cfUOvDONT
l+/o8Fr1hFYfs+8bIQIhAOaCRLr5774k0iflsslqBMmubN3NC+mImezWIaMHPP26 DV4fAVcaWsBXErw9AHUASLDja9qmRzQP5WoC+p0w6xxSActW3SyB2bu/qznYhHMA
AHUA2ra/az+1tiKfm8K7XGvocJFxbLtRhIU0vaQ9MEjX+6sAAAGJQgDK8gAABAMA AAGLG2S5LwAABAMARjBEAiBYu16xOFIzUE9aMrB1vrFkTsMC3veucw9k0Hv4qO2v
RjBEAiBalCI6KSCFXMTY15CgLEHtqeE/vyihg8FWthX4JgPcFwIgWkI//yF+tqBi XAIgTTMRV9sq+ytuTU53XdoKLF33qeUZHGqop7O7xoFEENMAdgDatr9rP7W2Ip+b
Gs3sHuuGvG4v7rytkUM4Dli6sWAYXsYAdgA7U3d1Pi25gE6LMFsG/kA7Z9hPw/TH wrtca+hwkXFsu1GEhTS9pD0wSNf7qwAAAYsbZLlmAAAEAwBHMEUCIQCD6n2r6DVx
vQANLXJv4frUFwAAAYlCAMsyAAAEAwBHMEUCIQDfn+j2sutaXvO5yZ86z6OFgq1u UMUgtfPRi3ieJdlObPzsrc0aGVhl6gCP5QIgbzkLqEOfJ7bozTgYzUO8ZDvAd0te
WhNwM2MnEHfGhO+yCAIgLYi8SX6hZRGRZFxIrV1ekcRSk+9yN8oHNfOQOuGQQOEw cdpZv182ZEQ974wwDQYJKoZIhvcNAQELBQADggEBAHEAZnHOPS7CoRXWTqWbQu5V
DQYJKoZIhvcNAQELBQADggEBAIprc7vYrAerkphosDZ2frlk9gYBnlgTYT0wJXLC 8x2uFNl5jjsbSH68xnviW24ShllvFyHH1iD2R3ov/3Ri8T8TJ0bA14u+3D/iPZk1
4ZTnlhXuMbL5gYmM8T1suPv5p2JphVFEsSS8PlmdQX45mPAlk8lHjHSZJRgL/r1Y BgX2/YdL/0/CptgISLhBtugBk2+MsRry6i7hhbf4/pIyL1uWHUeDuYrW882Xw1ul
7wWYjKB3oHl5N/5UZTtDTtfxtjd/3Vts6citxzhVwGxH+fQspligvb1Sgy8/ojjV uD1wyC1z2CqXZWy0WL0zMV6Yfp/eFnCJ9tj0NoullHGhgk9RTU3qULwuAQmzMqS7
lSz+QwpK2PrxRwvWPt0XImPj+5xPi8MOhRCh32Edtqy5fQkZiG/mYEvMMpq73kv0 XlhmgdzVPDq3s8qCawHDU6yDdT5FAO3SJ23Dj2efM7enBzq0jryfEZn9vTCRJfKI
uWbcjMFN2XbSOSYkjjseVoTBly65MAxFyAFAdg9HMJeJ5hbegpl4PARhgdL8sG3h yCZT56FgMdEqvmQutAV5UCiL1acK8YU2zUQSxuWn+AyDivV4JSOYw8ho1yZrZZI=
jEenWaFiCjwQ4iCPFeGk+vumNkL1c5OaLFZwhoZQ+KLkAQc=
-----END CERTIFICATE----- -----END CERTIFICATE-----