1 /* OTA example
2
3 This example code is in the Public Domain (or CC0 licensed, at your option.)
4
5 Unless required by applicable law or agreed to in writing, this
6 software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
7 CONDITIONS OF ANY KIND, either express or implied.
8 */
9 #include <string.h>
10 #include "freertos/FreeRTOS.h"
11 #include "freertos/task.h"
12 #include "esp_system.h"
13 #include "esp_event.h"
14 #include "esp_log.h"
15 #include "esp_ota_ops.h"
16 #include "esp_http_client.h"
17 #include "esp_flash_partitions.h"
18 #include "esp_partition.h"
19 #include "nvs.h"
20 #include "nvs_flash.h"
21 #include "driver/gpio.h"
22 #include "protocol_examples_common.h"
23 #include "errno.h"
24
25 #if CONFIG_EXAMPLE_CONNECT_WIFI
26 #include "esp_wifi.h"
27 #endif
28
29 #define BUFFSIZE 1024
30 #define HASH_LEN 32 /* SHA-256 digest length */
31
32 static const char *TAG = "native_ota_example";
33 /*an ota data write buffer ready to write to the flash*/
34 static char ota_write_data[BUFFSIZE + 1] = { 0 };
35 extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
36 extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");
37
38 #define OTA_URL_SIZE 256
39
http_cleanup(esp_http_client_handle_t client)40 static void http_cleanup(esp_http_client_handle_t client)
41 {
42 esp_http_client_close(client);
43 esp_http_client_cleanup(client);
44 }
45
task_fatal_error(void)46 static void __attribute__((noreturn)) task_fatal_error(void)
47 {
48 ESP_LOGE(TAG, "Exiting task due to fatal error...");
49 (void)vTaskDelete(NULL);
50
51 while (1) {
52 ;
53 }
54 }
55
print_sha256(const uint8_t * image_hash,const char * label)56 static void print_sha256 (const uint8_t *image_hash, const char *label)
57 {
58 char hash_print[HASH_LEN * 2 + 1];
59 hash_print[HASH_LEN * 2] = 0;
60 for (int i = 0; i < HASH_LEN; ++i) {
61 sprintf(&hash_print[i * 2], "%02x", image_hash[i]);
62 }
63 ESP_LOGI(TAG, "%s: %s", label, hash_print);
64 }
65
infinite_loop(void)66 static void infinite_loop(void)
67 {
68 int i = 0;
69 ESP_LOGI(TAG, "When a new firmware is available on the server, press the reset button to download it");
70 while(1) {
71 ESP_LOGI(TAG, "Waiting for a new firmware ... %d", ++i);
72 vTaskDelay(2000 / portTICK_PERIOD_MS);
73 }
74 }
75
ota_example_task(void * pvParameter)76 static void ota_example_task(void *pvParameter)
77 {
78 esp_err_t err;
79 /* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
80 esp_ota_handle_t update_handle = 0 ;
81 const esp_partition_t *update_partition = NULL;
82
83 ESP_LOGI(TAG, "Starting OTA example");
84
85 const esp_partition_t *configured = esp_ota_get_boot_partition();
86 const esp_partition_t *running = esp_ota_get_running_partition();
87
88 if (configured != running) {
89 ESP_LOGW(TAG, "Configured OTA boot partition at offset 0x%08x, but running from offset 0x%08x",
90 configured->address, running->address);
91 ESP_LOGW(TAG, "(This can happen if either the OTA boot data or preferred boot image become corrupted somehow.)");
92 }
93 ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
94 running->type, running->subtype, running->address);
95
96 esp_http_client_config_t config = {
97 .url = CONFIG_EXAMPLE_FIRMWARE_UPG_URL,
98 .cert_pem = (char *)server_cert_pem_start,
99 .timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT,
100 .keep_alive_enable = true,
101 };
102
103 #ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_URL_FROM_STDIN
104 char url_buf[OTA_URL_SIZE];
105 if (strcmp(config.url, "FROM_STDIN") == 0) {
106 example_configure_stdin_stdout();
107 fgets(url_buf, OTA_URL_SIZE, stdin);
108 int len = strlen(url_buf);
109 url_buf[len - 1] = '\0';
110 config.url = url_buf;
111 } else {
112 ESP_LOGE(TAG, "Configuration mismatch: wrong firmware upgrade image url");
113 abort();
114 }
115 #endif
116
117 #ifdef CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK
118 config.skip_cert_common_name_check = true;
119 #endif
120
121 esp_http_client_handle_t client = esp_http_client_init(&config);
122 if (client == NULL) {
123 ESP_LOGE(TAG, "Failed to initialise HTTP connection");
124 task_fatal_error();
125 }
126 err = esp_http_client_open(client, 0);
127 if (err != ESP_OK) {
128 ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
129 esp_http_client_cleanup(client);
130 task_fatal_error();
131 }
132 esp_http_client_fetch_headers(client);
133
134 update_partition = esp_ota_get_next_update_partition(NULL);
135 assert(update_partition != NULL);
136 ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
137 update_partition->subtype, update_partition->address);
138
139 int binary_file_length = 0;
140 /*deal with all receive packet*/
141 bool image_header_was_checked = false;
142 while (1) {
143 int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE);
144 if (data_read < 0) {
145 ESP_LOGE(TAG, "Error: SSL data read error");
146 http_cleanup(client);
147 task_fatal_error();
148 } else if (data_read > 0) {
149 if (image_header_was_checked == false) {
150 esp_app_desc_t new_app_info;
151 if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
152 // check current version with downloading
153 memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
154 ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
155
156 esp_app_desc_t running_app_info;
157 if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
158 ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
159 }
160
161 const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
162 esp_app_desc_t invalid_app_info;
163 if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
164 ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
165 }
166
167 // check current version with last invalid partition
168 if (last_invalid_app != NULL) {
169 if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
170 ESP_LOGW(TAG, "New version is the same as invalid version.");
171 ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
172 ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
173 http_cleanup(client);
174 infinite_loop();
175 }
176 }
177 #ifndef CONFIG_EXAMPLE_SKIP_VERSION_CHECK
178 if (memcmp(new_app_info.version, running_app_info.version, sizeof(new_app_info.version)) == 0) {
179 ESP_LOGW(TAG, "Current running version is the same as a new. We will not continue the update.");
180 http_cleanup(client);
181 infinite_loop();
182 }
183 #endif
184
185 image_header_was_checked = true;
186
187 err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle);
188 if (err != ESP_OK) {
189 ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
190 http_cleanup(client);
191 esp_ota_abort(update_handle);
192 task_fatal_error();
193 }
194 ESP_LOGI(TAG, "esp_ota_begin succeeded");
195 } else {
196 ESP_LOGE(TAG, "received package is not fit len");
197 http_cleanup(client);
198 esp_ota_abort(update_handle);
199 task_fatal_error();
200 }
201 }
202 err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
203 if (err != ESP_OK) {
204 http_cleanup(client);
205 esp_ota_abort(update_handle);
206 task_fatal_error();
207 }
208 binary_file_length += data_read;
209 ESP_LOGD(TAG, "Written image length %d", binary_file_length);
210 } else if (data_read == 0) {
211 /*
212 * As esp_http_client_read never returns negative error code, we rely on
213 * `errno` to check for underlying transport connectivity closure if any
214 */
215 if (errno == ECONNRESET || errno == ENOTCONN) {
216 ESP_LOGE(TAG, "Connection closed, errno = %d", errno);
217 break;
218 }
219 if (esp_http_client_is_complete_data_received(client) == true) {
220 ESP_LOGI(TAG, "Connection closed");
221 break;
222 }
223 }
224 }
225 ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length);
226 if (esp_http_client_is_complete_data_received(client) != true) {
227 ESP_LOGE(TAG, "Error in receiving complete file");
228 http_cleanup(client);
229 esp_ota_abort(update_handle);
230 task_fatal_error();
231 }
232
233 err = esp_ota_end(update_handle);
234 if (err != ESP_OK) {
235 if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
236 ESP_LOGE(TAG, "Image validation failed, image is corrupted");
237 } else {
238 ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
239 }
240 http_cleanup(client);
241 task_fatal_error();
242 }
243
244 err = esp_ota_set_boot_partition(update_partition);
245 if (err != ESP_OK) {
246 ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
247 http_cleanup(client);
248 task_fatal_error();
249 }
250 ESP_LOGI(TAG, "Prepare to restart system!");
251 esp_restart();
252 return ;
253 }
254
diagnostic(void)255 static bool diagnostic(void)
256 {
257 gpio_config_t io_conf;
258 io_conf.intr_type = GPIO_INTR_DISABLE;
259 io_conf.mode = GPIO_MODE_INPUT;
260 io_conf.pin_bit_mask = (1ULL << CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
261 io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
262 io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
263 gpio_config(&io_conf);
264
265 ESP_LOGI(TAG, "Diagnostics (5 sec)...");
266 vTaskDelay(5000 / portTICK_PERIOD_MS);
267
268 bool diagnostic_is_ok = gpio_get_level(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
269
270 gpio_reset_pin(CONFIG_EXAMPLE_GPIO_DIAGNOSTIC);
271 return diagnostic_is_ok;
272 }
273
app_main(void)274 void app_main(void)
275 {
276 uint8_t sha_256[HASH_LEN] = { 0 };
277 esp_partition_t partition;
278
279 // get sha256 digest for the partition table
280 partition.address = ESP_PARTITION_TABLE_OFFSET;
281 partition.size = ESP_PARTITION_TABLE_MAX_LEN;
282 partition.type = ESP_PARTITION_TYPE_DATA;
283 esp_partition_get_sha256(&partition, sha_256);
284 print_sha256(sha_256, "SHA-256 for the partition table: ");
285
286 // get sha256 digest for bootloader
287 partition.address = ESP_BOOTLOADER_OFFSET;
288 partition.size = ESP_PARTITION_TABLE_OFFSET;
289 partition.type = ESP_PARTITION_TYPE_APP;
290 esp_partition_get_sha256(&partition, sha_256);
291 print_sha256(sha_256, "SHA-256 for bootloader: ");
292
293 // get sha256 digest for running partition
294 esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256);
295 print_sha256(sha_256, "SHA-256 for current firmware: ");
296
297 const esp_partition_t *running = esp_ota_get_running_partition();
298 esp_ota_img_states_t ota_state;
299 if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
300 if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
301 // run diagnostic function ...
302 bool diagnostic_is_ok = diagnostic();
303 if (diagnostic_is_ok) {
304 ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
305 esp_ota_mark_app_valid_cancel_rollback();
306 } else {
307 ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
308 esp_ota_mark_app_invalid_rollback_and_reboot();
309 }
310 }
311 }
312
313 // Initialize NVS.
314 esp_err_t err = nvs_flash_init();
315 if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
316 // OTA app partition table has a smaller NVS partition size than the non-OTA
317 // partition table. This size mismatch may cause NVS initialization to fail.
318 // If this happens, we erase NVS partition and initialize NVS again.
319 ESP_ERROR_CHECK(nvs_flash_erase());
320 err = nvs_flash_init();
321 }
322 ESP_ERROR_CHECK( err );
323
324 ESP_ERROR_CHECK(esp_netif_init());
325 ESP_ERROR_CHECK(esp_event_loop_create_default());
326
327 /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
328 * Read "Establishing Wi-Fi or Ethernet Connection" section in
329 * examples/protocols/README.md for more information about this function.
330 */
331 ESP_ERROR_CHECK(example_connect());
332
333 #if CONFIG_EXAMPLE_CONNECT_WIFI
334 /* Ensure to disable any WiFi power save mode, this allows best throughput
335 * and hence timings for overall OTA operation.
336 */
337 esp_wifi_set_ps(WIFI_PS_NONE);
338 #endif // CONFIG_EXAMPLE_CONNECT_WIFI
339
340 xTaskCreate(&ota_example_task, "ota_example_task", 8192, NULL, 5, NULL);
341 }
342