|
1 /** |
|
2 * @file updates.c |
|
3 * @brief Updates management. It can download and install new firmware |
|
4 * downloaded from the internet. |
|
5 */ |
|
6 |
|
7 #include "config.h" |
|
8 |
|
9 |
|
10 #define BUFFSIZE 1024 ///< Download buffer size |
|
11 static char ota_write_data[BUFFSIZE + 1] = { 0 }; |
|
12 static const char *TAG = "update"; |
|
13 |
|
14 |
|
15 |
|
16 static void http_cleanup(esp_http_client_handle_t client) |
|
17 { |
|
18 esp_http_client_close(client); |
|
19 esp_http_client_cleanup(client); |
|
20 } |
|
21 |
|
22 |
|
23 |
|
24 /** |
|
25 * @brief Run binary update procedure |
|
26 */ |
|
27 void bin_update(void) |
|
28 { |
|
29 char temp[64]; |
|
30 esp_err_t err; |
|
31 const esp_partition_t *update_partition = NULL; |
|
32 esp_ota_handle_t update_handle = 0; |
|
33 |
|
34 // TFT_setFont(DEJAVU18_FONT, NULL); |
|
35 // _fg = TFT_CYAN; |
|
36 const esp_partition_t *running = esp_ota_get_running_partition(); |
|
37 |
|
38 /* |
|
39 * Don't use https because it costs more then 100K memory. |
|
40 */ |
|
41 esp_http_client_config_t update = { |
|
42 .url = "http://update.mbse.eu/ap2/fw/co2meter.bin", |
|
43 }; |
|
44 |
|
45 esp_http_client_handle_t client = esp_http_client_init(&update); |
|
46 if (client == NULL) { |
|
47 ESP_LOGI(TAG, "Failed to init HTTP connection"); |
|
48 goto updateerr; |
|
49 } |
|
50 |
|
51 err = esp_http_client_open(client, 0); |
|
52 if (err != ESP_OK) { |
|
53 ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); |
|
54 esp_http_client_cleanup(client); |
|
55 goto updateerr; |
|
56 } |
|
57 |
|
58 int content_length = esp_http_client_fetch_headers(client); |
|
59 int status_code = esp_http_client_get_status_code(client); |
|
60 if (status_code != 200) { |
|
61 ESP_LOGE(TAG, "GET %s error %d", update.url, status_code); |
|
62 esp_http_client_cleanup(client); |
|
63 goto updateerr; |
|
64 } |
|
65 update_partition = esp_ota_get_next_update_partition(NULL); |
|
66 if (update_partition == NULL) { |
|
67 ESP_LOGE(TAG, "No update partition"); |
|
68 esp_http_client_cleanup(client); |
|
69 goto updateerr; |
|
70 } |
|
71 ESP_LOGI(TAG, "Update to partition subtype %d at offset 0x%x", update_partition->subtype, update_partition->address); |
|
72 |
|
73 err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); |
|
74 if (err != ESP_OK) { |
|
75 ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); |
|
76 http_cleanup(client); |
|
77 goto updateerr; |
|
78 } |
|
79 |
|
80 // TFT_print((char *)"Begin firmware download.\r\n", 0, LASTY); |
|
81 ESP_LOGI(TAG, "Download update %s size %d", update.url, content_length); |
|
82 int binary_file_length = 0; |
|
83 /*deal with all receive packet*/ |
|
84 bool image_header_was_checked = false; |
|
85 while (1) { |
|
86 int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE); |
|
87 if (data_read < 0) { |
|
88 ESP_LOGE(TAG, "Error: data read error"); |
|
89 http_cleanup(client); |
|
90 goto updateerr;; |
|
91 } else if (data_read > 0) { |
|
92 if (image_header_was_checked == false) { |
|
93 esp_app_desc_t new_app_info; |
|
94 if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) { |
|
95 // check current version with downloading |
|
96 memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t)); |
|
97 |
|
98 esp_app_desc_t running_app_info; |
|
99 if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) { |
|
100 ESP_LOGI(TAG, "Running firmware version: %s, %s %s", running_app_info.version, running_app_info.date, running_app_info.time); |
|
101 } |
|
102 |
|
103 const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition(); |
|
104 esp_app_desc_t invalid_app_info; |
|
105 if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) { |
|
106 ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version); |
|
107 } |
|
108 |
|
109 // check current sha256 with last invalid partition |
|
110 if (last_invalid_app != NULL) { |
|
111 if (memcmp(invalid_app_info.app_elf_sha256, new_app_info.app_elf_sha256, 32) == 0) { |
|
112 ESP_LOGW(TAG, "New version is the same as invalid version."); |
|
113 ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version); |
|
114 ESP_LOGW(TAG, "The firmware has been rolled back to the previous version."); |
|
115 http_cleanup(client); |
|
116 goto updateok; |
|
117 } |
|
118 } |
|
119 |
|
120 if (memcmp(new_app_info.app_elf_sha256, running_app_info.app_elf_sha256, 32) == 0) { |
|
121 ESP_LOGI(TAG, "Current running version is the same as a new."); |
|
122 http_cleanup(client); |
|
123 goto updateok; |
|
124 } |
|
125 |
|
126 image_header_was_checked = true; |
|
127 |
|
128 err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); |
|
129 if (err != ESP_OK) { |
|
130 ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); |
|
131 http_cleanup(client); |
|
132 goto updateerr; |
|
133 } |
|
134 ESP_LOGI(TAG, "Continue upgrade application"); |
|
135 } else { |
|
136 ESP_LOGE(TAG, "Received package is not fit len"); |
|
137 http_cleanup(client); |
|
138 goto updateerr; |
|
139 } |
|
140 } |
|
141 err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read); |
|
142 if (err != ESP_OK) { |
|
143 http_cleanup(client); |
|
144 goto updateerr;; |
|
145 } |
|
146 binary_file_length += data_read; |
|
147 ESP_LOGD(TAG, "Written image length %d", binary_file_length); |
|
148 } else if (data_read == 0) { |
|
149 break; |
|
150 } |
|
151 } |
|
152 |
|
153 ESP_LOGI(TAG, "Download complete, binary data length: %d", binary_file_length); |
|
154 http_cleanup(client); |
|
155 |
|
156 if (binary_file_length != content_length) { |
|
157 ESP_LOGE(TAG, "Incomplete file"); |
|
158 goto updateerr; |
|
159 } |
|
160 if (esp_ota_end(update_handle) != ESP_OK) { |
|
161 ESP_LOGE(TAG, "esp_ota_end failed!"); |
|
162 goto updateerr; |
|
163 } |
|
164 snprintf(temp, 63, "Received image %d bytes\r\n", binary_file_length); |
|
165 // TFT_print(temp, 0, LASTY); |
|
166 |
|
167 /* |
|
168 * Here we have new version, install and boot it. |
|
169 */ |
|
170 err = esp_ota_set_boot_partition(update_partition); |
|
171 if (err != ESP_OK) { |
|
172 ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err)); |
|
173 goto updateerr; |
|
174 } |
|
175 |
|
176 ESP_LOGI(TAG, "Prepare to restart system!"); |
|
177 // TFT_print((char *)"Rebooting ...", 0, LASTY); |
|
178 vTaskDelay(1000 / portTICK_PERIOD_MS); |
|
179 esp_restart(); |
|
180 return; |
|
181 |
|
182 updateerr: |
|
183 // _fg = TFT_RED; |
|
184 // TFT_print((char *)"Error\r\n", 0, LASTY); |
|
185 |
|
186 updateok: |
|
187 vTaskDelay(3000 / portTICK_PERIOD_MS); |
|
188 } |
|
189 |
|
190 |
|
191 |
|
192 /** |
|
193 * @brief Download a file to /spiffs |
|
194 * @param filename The name and path of the file to download. |
|
195 * @return Return 0 if ok, negative if errors. |
|
196 */ |
|
197 int DownloadSpiffs(char *filename) |
|
198 { |
|
199 esp_err_t err; |
|
200 static char theurl[73], thefile[41]; |
|
201 FILE *f; |
|
202 |
|
203 // static char todel[41]; |
|
204 // snprintf(todel, 40, "/spiffs//%s", filename); |
|
205 // unlink(todel); |
|
206 // return 0; |
|
207 |
|
208 snprintf(theurl, 72, "http://update.mbse.eu/ap1/image/%s", filename); |
|
209 snprintf(thefile, 40, "/spiffs/%s", filename); |
|
210 |
|
211 esp_http_client_config_t update = { |
|
212 .url = theurl, |
|
213 }; |
|
214 |
|
215 esp_http_client_handle_t client = esp_http_client_init(&update); |
|
216 if (client == NULL) { |
|
217 ESP_LOGE(TAG, "Failed to init HTTP connection"); |
|
218 return -1; |
|
219 } |
|
220 |
|
221 err = esp_http_client_open(client, 0); |
|
222 if (err != ESP_OK) { |
|
223 ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err)); |
|
224 esp_http_client_cleanup(client); |
|
225 return -1; |
|
226 } |
|
227 |
|
228 int content_length = esp_http_client_fetch_headers(client); |
|
229 int status_code = esp_http_client_get_status_code(client); |
|
230 if (status_code != 200) { |
|
231 ESP_LOGE(TAG, "GET %s error %d", update.url, status_code); |
|
232 esp_http_client_cleanup(client); |
|
233 return -1; |
|
234 } |
|
235 |
|
236 /* |
|
237 * Remove a possible stale download. |
|
238 */ |
|
239 unlink("/spiffs/tmpfile"); |
|
240 f = fopen("/spiffs/tmpfile", "w"); |
|
241 if (f == NULL) { |
|
242 ESP_LOGE(TAG, "Cannot create /spiffs/tmpfile"); |
|
243 esp_http_client_cleanup(client); |
|
244 return -1; |
|
245 } |
|
246 |
|
247 int read_length = 0; |
|
248 int write_length = 0; |
|
249 while (1) { |
|
250 int data_read = esp_http_client_read(client, ota_write_data, BUFFSIZE); |
|
251 if (data_read < 0) { |
|
252 ESP_LOGE(TAG, "Error: data read error %s", theurl); |
|
253 http_cleanup(client); |
|
254 return -1; |
|
255 } else if (data_read > 0) { |
|
256 size_t bytes = fwrite(ota_write_data, 1, data_read, f); |
|
257 if (bytes != data_read) { |
|
258 ESP_LOGE(TAG, "fwrite %s %d/%d at %d", theurl, bytes, data_read, write_length); |
|
259 } |
|
260 write_length += bytes; |
|
261 read_length += data_read; |
|
262 } else if (data_read == 0) { |
|
263 break; |
|
264 } |
|
265 vTaskDelay(10 / portTICK_PERIOD_MS); |
|
266 } |
|
267 fclose(f); |
|
268 |
|
269 if (content_length != write_length) { |
|
270 ESP_LOGE(TAG, "Download %s size %d but got %d bytes", theurl, content_length, write_length); |
|
271 unlink("/spiffs/tmpfile"); |
|
272 return -1; |
|
273 } |
|
274 |
|
275 ESP_LOGI(TAG, "Download %s size %d Ok", theurl, content_length); |
|
276 unlink(thefile); |
|
277 rename("/spiffs/tmpfile", thefile); |
|
278 esp_http_client_cleanup(client); |
|
279 return 0; |
|
280 } |
|
281 |
|
282 |
|
283 |
|
284 /* |
|
285 * Files init function, only runs once a new screen is entered. |
|
286 */ |
|
287 void Updates_Init(void) |
|
288 { |
|
289 // _bg = TFT_BLACK; |
|
290 // TFT_fillScreen(_bg); |
|
291 // TopMessage((char *)"Update"); |
|
292 |
|
293 } |
|
294 |
|
295 |
|
296 |
|
297 /* |
|
298 * Updates management loop, non-blocking. |
|
299 */ |
|
300 void Updates_Loop(void) |
|
301 { |
|
302 bin_update(); |
|
303 } |
|
304 |
|
305 |