diff --git a/.cproject b/.cproject index b12dcf52..174d5e84 100644 --- a/.cproject +++ b/.cproject @@ -1,198 +1,114 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - make - - - - all - - true - - true - - true - - - - - - make - - - - size-components - - true - - true - - true - - - - - - make - - - - flash - - true - - true - - true - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + -j8 + all + true + false + true + + + make + size-components + true + true + true + + + make + flash + true + true + true + + + make + -j8 app PROJECT_NAME="recovery.custom" EXTRA_CFLAGS=" -DRECOVERY_APPLICATION=1" + recovery + true + false + true + + + python + ${IDF_PATH}/tools/windows/eclipse_make.py -j8 + app + true + true + true + + + diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml index bbdb7cd9..5151056a 100644 --- a/.settings/language.settings.xml +++ b/.settings/language.settings.xml @@ -1,28 +1,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + diff --git a/Makefile b/Makefile index 70212238..db0f7915 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,14 @@ -# -# This is a project Makefile. It is assumed the directory this Makefile resides in is a -# project subdirectory. -# +# build system (Jenkins) should pass the following +#export PROJECT_BUILD_NAME="${build_version_prefix}${BUILD_NUMBER}" +#export PROJECT_BUILD_TARGET="${config_target}" -PROJECT_NAME := squeezelite -include $(IDF_PATH)/make/project.mk +PROJECT_BUILD_NAME?=local +PROJECT_CONFIG_TARGET?=custom +PROJECT_NAME?=squeezelite.$(PROJECT_CONFIG_TARGET) +PROJECT_VER?="$(PROJECT_BUILD_NAME)-$(PROJECT_CONFIG_TARGET)" +RECOVERY_APPLICATION?=0 +CFLAGS?= + + + +include $(IDF_PATH)/make/project.mk diff --git a/Makefile_std.mk b/Makefile_std.mk new file mode 100644 index 00000000..55e809c3 --- /dev/null +++ b/Makefile_std.mk @@ -0,0 +1,2 @@ +PROJECT_NAME?= squeezelite +include $(IDF_PATH)/make/project.mk \ No newline at end of file diff --git a/components/squeezelite-ota/cmd_ota.c b/components/squeezelite-ota/cmd_ota.c new file mode 100644 index 00000000..e65b5e71 --- /dev/null +++ b/components/squeezelite-ota/cmd_ota.c @@ -0,0 +1,50 @@ +/* Console example — various system commands + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "../squeezelite-ota/cmd_ota.h" + +#include +#include +#include +#include "esp_log.h" +#include "esp_console.h" +#include "esp_system.h" +#include "esp_sleep.h" +#include "esp_spi_flash.h" +#include "driver/rtc_io.h" +#include "driver/uart.h" +#include "argtable3/argtable3.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "soc/rtc_cntl_reg.h" +#include "esp32/rom/uart.h" +#include "sdkconfig.h" + +static const char * TAG = "platform_esp32"; + +/* 'heap' command prints minumum heap size */ +static int perform_ota_update(int argc, char **argv) +{ + + return 0; +} + +static void register_ota_cmd() +{ + const esp_console_cmd_t cmd = { + .command = "ota_update", + .help = "Updates the application binary from the provided URL", + .hint = NULL, + .func = &perform_ota_update, + }; + ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) ); +} + + + diff --git a/components/squeezelite-ota/cmd_ota.h b/components/squeezelite-ota/cmd_ota.h new file mode 100644 index 00000000..c14cdffb --- /dev/null +++ b/components/squeezelite-ota/cmd_ota.h @@ -0,0 +1,19 @@ +/* Console example — various system commands + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +// Register system functions +static void register_ota_cmd(); +#ifdef __cplusplus +} +#endif diff --git a/components/squeezelite-ota/component.mk b/components/squeezelite-ota/component.mk new file mode 100644 index 00000000..e7f8bb89 --- /dev/null +++ b/components/squeezelite-ota/component.mk @@ -0,0 +1,9 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +# todo: add support for https +COMPONENT_ADD_INCLUDEDIRS := . +CFLAGS += -D LOG_LOCAL_LEVEL=ESP_LOG_DEBUG -D CONFIG_OTA_ALLOW_HTTP=1 +#COMPONENT_EMBED_TXTFILES := ${PROJECT_PATH}/server_certs/ca_cert.pem \ No newline at end of file diff --git a/components/squeezelite-ota/protocol_examples_common.h b/components/squeezelite-ota/protocol_examples_common.h new file mode 100644 index 00000000..e250db14 --- /dev/null +++ b/components/squeezelite-ota/protocol_examples_common.h @@ -0,0 +1,59 @@ +/* Common functions for protocol examples, to establish Wi-Fi or Ethernet connection. + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include "esp_err.h" +#include "tcpip_adapter.h" + +#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET +#define EXAMPLE_INTERFACE TCPIP_ADAPTER_IF_ETH +#endif + +#ifdef CONFIG_EXAMPLE_CONNECT_WIFI +#define EXAMPLE_INTERFACE TCPIP_ADAPTER_IF_STA +#endif + +/** + * @brief Configure Wi-Fi or Ethernet, connect, wait for IP + * + * This all-in-one helper function is used in protocols examples to + * reduce the amount of boilerplate in the example. + * + * It is not intended to be used in real world applications. + * See examples under examples/wifi/getting_started/ and examples/ethernet/ + * for more complete Wi-Fi or Ethernet initialization code. + * + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + * + * @return ESP_OK on successful connection + */ +esp_err_t example_connect(); + +/** + * Counterpart to example_connect, de-initializes Wi-Fi or Ethernet + */ +esp_err_t example_disconnect(); + +/** + * @brief Configure stdin and stdout to use blocking I/O + * + * This helper function is used in ASIO examples. It wraps installing the + * UART driver and configuring VFS layer to use UART driver for console I/O. + */ +esp_err_t example_configure_stdin_stdout(); + +#ifdef __cplusplus +} +#endif diff --git a/components/squeezelite-ota/squeezelite-ota.c b/components/squeezelite-ota/squeezelite-ota.c new file mode 100644 index 00000000..e7e700f5 --- /dev/null +++ b/components/squeezelite-ota/squeezelite-ota.c @@ -0,0 +1,107 @@ +/* OTA example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_ota_ops.h" +#include "esp_http_client.h" +#include "esp_https_ota.h" +#include "string.h" +#include +#include "nvs.h" +#include "nvs_flash.h" + +#include "esp_err.h" +#include "tcpip_adapter.h" + + +static const char *TAG = "simple_ota_example"; +extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start"); +extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end"); + +#define OTA_URL_SIZE 256 +static char ota_status[31]={0}; +static uint8_t ota_pct=0; +const char * ota_get_status(){ + return ota_status; +} +uint8_t ota_get_pct_complete(){ + return ota_pct; +} +esp_err_t _http_event_handler(esp_http_client_event_t *evt) +{ + switch (evt->event_id) { + case HTTP_EVENT_ERROR: + ESP_LOGD(TAG, "HTTP_EVENT_ERROR"); + break; + case HTTP_EVENT_ON_CONNECTED: + ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED"); + break; + case HTTP_EVENT_HEADER_SENT: + ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT"); + break; + case HTTP_EVENT_ON_HEADER: + ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value); + break; + case HTTP_EVENT_ON_DATA: + ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len); + break; + case HTTP_EVENT_ON_FINISH: + ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH"); + break; + case HTTP_EVENT_DISCONNECTED: + ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED"); + break; + } + return ESP_OK; +} + +void ota_task(void *pvParameter, const char * bin_url) +{ + ESP_LOGI(TAG, "Starting OTA example"); + + esp_http_client_config_t config = { + .url = bin_url, + .cert_pem = (char *)server_cert_pem_start, + .event_handler = _http_event_handler, + }; + + // todo: review how certificates work + config.skip_cert_common_name_check = true; + + esp_err_t ret = esp_https_ota(&config); + if (ret == ESP_OK) { + esp_restart(); + } else { + ESP_LOGE(TAG, "Firmware upgrade failed"); + } + while (1) { + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +void start_ota(const char * bin_url) +{ + // Initialize NVS. + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + // todo: If we ever change the size of the nvs partition, we need to figure out a mechanism to enlarge the nvs. + // 1.OTA app partition table has a smaller NVS partition size than the non-OTA + // partition table. This size mismatch may cause NVS initialization to fail. + // 2.NVS partition contains data in new format and cannot be recognized by this version of code. + // If this happens, we erase NVS partition and initialize NVS again. + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK(err); + + xTaskCreate(&ota_task, "ota_task", 8192, NULL, 5, NULL); +} diff --git a/components/wifi-manager/http_server.c b/components/wifi-manager/http_server.c index 026abca1..e8875f43 100644 --- a/components/wifi-manager/http_server.c +++ b/components/wifi-manager/http_server.c @@ -45,10 +45,6 @@ static const char array_separator[]=","; /* @brief task handle for the http server */ static TaskHandle_t task_http_server = NULL; -#ifndef CONFIG_IS_RECOVERY_MODE -#define CONFIG_IS_RECOVERY_MODE 0 -#endif - /** * @brief embedded binary data. * @see file "component.mk" @@ -225,7 +221,7 @@ void http_server_netconn_serve(struct netconn *conn) { netconn_write(conn, http_ok_json_no_cache_hdr, sizeof(http_ok_json_no_cache_hdr) - 1, NETCONN_NOCOPY); autoexec_flag = wifi_manager_get_flag(); - snprintf(buff,buflen-1, json_start, CONFIG_IS_RECOVERY_MODE, autoexec_flag); + snprintf(buff,buflen-1, json_start, RECOVERY_APPLICATION, autoexec_flag); netconn_write(conn, buff, strlen(buff), NETCONN_NOCOPY); do { snprintf(autoexec_name,sizeof(autoexec_name)-1,"autoexec%u",i); diff --git a/components/wifi-manager/wifi_manager.c b/components/wifi-manager/wifi_manager.c index 24b95b16..93e8c446 100644 --- a/components/wifi-manager/wifi_manager.c +++ b/components/wifi-manager/wifi_manager.c @@ -55,14 +55,14 @@ Contains the freeRTOS task and all necessary support #include "lwip/netdb.h" #include "lwip/ip4_addr.h" -#ifndef SQUEEZELITE_ESP32_BASE_RELEASE -#define SQUEEZELITE_ESP32_BASE_RELEASE "unknown" -#endif + #ifndef SQUEEZELITE_ESP32_RELEASE_URL #define SQUEEZELITE_ESP32_RELEASE_URL "https://github.com/sle118/squeezelite-esp32/releases" #endif - - +#if RECOVERY_APPLICATION +extern const char * ota_get_status(); +extern uint8_t ota_get_pct_complete(); +#endif /* objects used to manipulate the main queue of events */ QueueHandle_t wifi_manager_queue; @@ -406,9 +406,11 @@ void wifi_manager_clear_ip_info_json(){ void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code){ wifi_config_t *config = wifi_manager_get_wifi_sta_config(); if(config){ - +#if !RECOVERY_APPLICATION const char ip_info_json_format[] = ",\"ip\":\"%s\",\"netmask\":\"%s\",\"gw\":\"%s\",\"urc\":%d}\n"; - +#else + const char ip_info_json_format[] = ",\"ip\":\"%s\",\"netmask\":\"%s\",\"gw\":\"%s\",\"urc\":%d, \"ota_dsc\":\"%s\", \"ota_pct\":%d}\n"; +#endif memset(ip_info_json, 0x00, JSON_IP_INFO_SIZE); @@ -431,7 +433,12 @@ void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code) ip, netmask, gw, - (int)update_reason_code); + (int)update_reason_code +#if RECOVERY_APPLICATION + ,ota_get_status(), + ota_get_pct_complete() +#endif + ); } else{ /* notify in the json output the reason code why this was updated without a connection */ @@ -439,7 +446,12 @@ void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code) "0", "0", "0", - (int)update_reason_code); + (int)update_reason_code +#if RECOVERY_APPLICATION + ,"", + 0 +#endif +); } } else{ diff --git a/components/wifi-manager/wifi_manager.h b/components/wifi-manager/wifi_manager.h index e280fdd5..d32076d9 100644 --- a/components/wifi-manager/wifi_manager.h +++ b/components/wifi-manager/wifi_manager.h @@ -40,6 +40,13 @@ extern "C" { #include "esp_wifi.h" #include "esp_wifi_types.h" +#ifndef RECOVERY_APPLICATION +#define RECOVERY_APPLICATION 0 +#else +#undef RECOVERY_APPLICATION +#define RECOVERY_APPLICATION 1 +#endif + #define DEFAULT_COMMAND_LINE CONFIG_DEFAULT_COMMAND_LINE @@ -163,10 +170,14 @@ extern "C" { /** * @brief Defines the maximum length in bytes of a JSON representation of the IP information * assuming all ips are 4*3 digits, and all characters in the ssid require to be escaped. - * example: {"ssid":"abcdefghijklmnopqrstuvwxyz012345","ip":"192.168.1.119","netmask":"255.255.255.0","gw":"192.168.1.1","urc":0} + * example: {"ssid":"abcdefghijklmnopqrstuvwxyz012345","ip":"192.168.1.119","netmask":"255.255.255.0","gw":"192.168.1.1","urc":0, "ota_dsc":"Installing...", "ota_pct":100} */ +#if RECOVERY_APPLICATION +// recovery has more resources available. Let's use them to include more details about the OTA process +#define JSON_IP_INFO_SIZE 150+255 +#else #define JSON_IP_INFO_SIZE 150 - +#endif /** diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index d0ba9012..497df353 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -2,7 +2,7 @@ set(COMPONENT_ADD_INCLUDEDIRS . ) set(COMPONENT_SRCS "esp_app_main.c" "platform_esp32.c" "cmd_wifi.c" "console.c" "nvs_utilities.c" "cmd_squeezelite.c") set(REQUIRES esp_common) -set(REQUIRES_COMPONENTS freertos squeezelite nvs_flash esp32 spi_flash newlib log console ) +set(REQUIRES_COMPONENTS freertos squeezelite nvs_flash esp32 spi_flash newlib log console ota ) register_component() diff --git a/main/cmd_decl.h b/main/cmd_decl.h index dc88939e..eb91713a 100644 --- a/main/cmd_decl.h +++ b/main/cmd_decl.h @@ -16,7 +16,7 @@ extern "C" { #include "cmd_wifi.h" #include "cmd_nvs.h" #include "cmd_i2ctools.h" - +#include "cmd_ota.h" #ifdef __cplusplus } #endif diff --git a/main/console.c b/main/console.c index 96d1b1a6..f497fe80 100644 --- a/main/console.c +++ b/main/console.c @@ -33,6 +33,11 @@ static void * console_thread(); void console_start(); static const char * TAG = "console"; +#if (RECOVERY_APPLICATION ) +extern void start_ota(const char * bin_url); +#endif + + /* Prompt to be printed before each line. * This can be customized, made dynamic, etc. */ @@ -235,7 +240,13 @@ void console_start() { esp_console_register_help_command(); register_system(); register_nvs(); +#if !RECOVERY_APPLICATION +#pragma message "compiling for squeezelite"" register_squeezelite(); +#else +#pragma message "compiling for recovery" + register_ota_cmd(); +#endif register_i2ctools(); printf("\n" "Type 'help' to get the list of commands.\n" diff --git a/main/esp_app_main.c b/main/esp_app_main.c index 69b41705..32edc531 100644 --- a/main/esp_app_main.c +++ b/main/esp_app_main.c @@ -80,10 +80,7 @@ bool wait_for_wifi(){ ESP_LOGI(TAG,"WiFi Connected!"); } } - - return connected; - } void app_main() diff --git a/main/platform_esp32.h b/main/platform_esp32.h index 1e113d81..485a69b4 100644 --- a/main/platform_esp32.h +++ b/main/platform_esp32.h @@ -28,3 +28,10 @@ extern bool wait_for_wifi(); extern void console_start(); extern pthread_cond_t wifi_connect_suspend_cond; extern pthread_t wifi_connect_suspend_mutex; + +#ifndef RECOVERY_APPLICATION +#define RECOVERY_APPLICATION 0 +#else +#undef RECOVERY_APPLICATION +#define RECOVERY_APPLICATION 1 +#endif diff --git a/partitions.csv b/partitions.csv index 54c6f45d..81ae692d 100644 --- a/partitions.csv +++ b/partitions.csv @@ -1,7 +1,7 @@ # Name, Type, SubType, Offset, Size, Flags # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 3M, -storage, data, fat, , 819200, -coredump, data, coredump,, 64K \ No newline at end of file +nvs, data, nvs, 0x9000, 0x4000, +otadata, data, ota, 0xD000, 0x2000, +phy_init, data, phy, 0xF000, 0x1000, +recovery, app, factory, 0x10000, 0x100000, +ota_0, app, ota_0, 0x110000, 0x2F0000,