add basic OTA Update fucionality
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				continuous-integration/drone/push Build is passing
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			This commit is contained in:
		
							parent
							
								
									495163060d
								
							
						
					
					
						commit
						8874fecf15
					
				
					 8 changed files with 333 additions and 72 deletions
				
			
		
							
								
								
									
										8
									
								
								firmware/.idea/firmware.iml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								firmware/.idea/firmware.iml
									
										
									
										generated
									
									
									
								
							|  | @ -1,2 +1,8 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module classpath="External" external.linked.project.id="firmware" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="CompDB" type="CPP_MODULE" version="4" /> | ||||
| <module version="4"> | ||||
|   <component name="FacetManager"> | ||||
|     <facet type="Python" name="Python facet"> | ||||
|       <configuration sdkName="Python 3.9" /> | ||||
|     </facet> | ||||
|   </component> | ||||
| </module> | ||||
							
								
								
									
										8
									
								
								firmware/.idea/modules.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								firmware/.idea/modules.xml
									
										
									
										generated
									
									
									
								
							|  | @ -1,8 +0,0 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/firmware.iml" filepath="$PROJECT_DIR$/.idea/firmware.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
|  | @ -2,7 +2,7 @@ PROGRAM=fiatlux | |||
| 
 | ||||
| EXTRA_CFLAGS=-O3 -Ifsdata | ||||
| 
 | ||||
| EXTRA_COMPONENTS=extras/i2s_dma extras/ws2812_i2s extras/dhcpserver extras/mbedtls extras/httpd extras/sntp extras/cpp_support | ||||
| EXTRA_COMPONENTS=extras/i2s_dma extras/ws2812_i2s extras/dhcpserver extras/rboot-ota extras/mbedtls extras/httpd extras/sntp extras/cpp_support | ||||
| 
 | ||||
| LIBS = hal m | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,9 +22,9 @@ void user_init(void) | |||
| 
 | ||||
|     wifi_available_semaphore = xSemaphoreCreateBinary(); | ||||
| 
 | ||||
|     xTaskCreate(wifi_task, "wifi_task", 512, NULL, 2, NULL); | ||||
|     xTaskCreate(wifi_task, "wifi_task", 1024, NULL, 1, NULL); | ||||
| 
 | ||||
|     xTaskCreate(&httpd_task, "httpd_task", 512, NULL, 2, NULL); | ||||
|     xTaskCreate(&httpd_task, "httpd_task", 1024, NULL, 2, NULL); | ||||
| 
 | ||||
|     xTaskCreate(&lux_task, "lux_task", 512, NULL, 1, NULL); | ||||
| } | ||||
|  |  | |||
|  | @ -16,9 +16,48 @@ | |||
|     <label for="bmenub" class="burger pseudo button">☰</label> | ||||
|     <div class="menu"> | ||||
|         <a href="/#" class="button icon-picture">Dashboard</a> | ||||
|         <a href="/#ota" class="button icon-picture">System</a> | ||||
|     </div> | ||||
| </nav> | ||||
| <main id="page"> | ||||
|     <section id="ota"> | ||||
|         <h2>System</h2> | ||||
|         <article class="card"> | ||||
|             <header> | ||||
|                 <h3>Firmware Update</h3> | ||||
|             </header> | ||||
|             <div class="table"> | ||||
|                 <div class="row"> | ||||
|                     <span><input id="firmware_file" type="file" onchange="load_firmware(event)"/></span> | ||||
|                 </div> | ||||
|                 <div class="row"> | ||||
|                     <span><input id="transmit_firmware" disabled type="submit" value="Upload" | ||||
|                                  onclick="transmit_firmware(event)"></span> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </article> | ||||
|         <article class="card"> | ||||
|             <header> | ||||
|                 <h3>Restart</h3> | ||||
|             </header> | ||||
|             <div class="table"> | ||||
|                 <div class="row"> | ||||
|                     <span><input type="submit" value="Restart" onclick="wsWrite('R')"></span> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </article> | ||||
|         <article class="card"> | ||||
|             <header> | ||||
|                 <h3>Reset Config</h3> | ||||
|             </header> | ||||
|             <div class="table"> | ||||
|                 <div class="row"> | ||||
|                     <span><input type="submit" class="warning" value="Reset" onclick="wsWrite('X')"></span> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </article> | ||||
| 
 | ||||
|     </section> | ||||
|     <section id="dashboard"> | ||||
|         <h2>Status</h2> | ||||
|         <div class="flex"> | ||||
|  | @ -133,19 +172,18 @@ | |||
| <div id="unused_values" style="display:none;"></div> | ||||
| <script type="text/javascript" src="js/smoothie_min.js"></script> | ||||
| <script> | ||||
|     var section = document.getElementById("page"); | ||||
|     var menu = document.getElementById("bmenub"); | ||||
|     var voltage = document.getElementById("out_voltage"); | ||||
| 
 | ||||
|     var unused_values = {}; | ||||
| 
 | ||||
|     DataView.prototype.setChar = function(pos, char) { | ||||
|     DataView.prototype.setChar = function (pos, char) { | ||||
|         this.setInt8(pos++, char.charCodeAt(0)); | ||||
|         return pos; | ||||
|     }; | ||||
| 
 | ||||
|     DataView.prototype.setString = function(pos, str) { | ||||
|         for(var i = 0; i < str.length; i++) { | ||||
|     DataView.prototype.setString = function (pos, str) { | ||||
|         for (var i = 0; i < str.length; i++) { | ||||
|             this.setInt8(pos++, str.charCodeAt(i)); | ||||
|         } | ||||
|         this.setInt8(pos++, 0); | ||||
|  | @ -175,6 +213,9 @@ | |||
|         chart.streamTo(canvas, 500); | ||||
|     } | ||||
| 
 | ||||
|     var receive_chunk_confirmation = () => { | ||||
|     }; | ||||
| 
 | ||||
|     function onMessage(evt) { | ||||
|         retries = 0; | ||||
|         if (typeof evt.data == 'string') { | ||||
|  | @ -196,7 +237,9 @@ | |||
| 
 | ||||
|             if (cmd === 'G') | ||||
|                 console.log("LED switched", val); | ||||
|             else if (cmd === 'V') { | ||||
|             else if (cmd === 'F') { | ||||
|                 receive_chunk_confirmation(dv); | ||||
|             } else if (cmd === 'V') { | ||||
|                 voltage.innerHTML = (val * 13 / 1024).toFixed(2); | ||||
|                 series.append(new Date().getTime(), val); | ||||
|             } else | ||||
|  | @ -233,7 +276,7 @@ | |||
|     } | ||||
| 
 | ||||
|     function wsWrite(data) { | ||||
|         console.info(buf2hex(data)); | ||||
|         //console.info(buf2hex(data)); | ||||
|         if (ws.readyState === 3 || retries++ > 5) | ||||
|             wsOpen(); | ||||
|         else if (ws.readyState === 1) | ||||
|  | @ -252,6 +295,105 @@ | |||
|         startPolling(); | ||||
|     } | ||||
| 
 | ||||
|     var makeCRCTable = function () { | ||||
|         var c; | ||||
|         var crcTable = []; | ||||
|         for (var n = 0; n < 256; n++) { | ||||
|             c = n; | ||||
|             for (var k = 0; k < 8; k++) { | ||||
|                 c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); | ||||
|             } | ||||
|             crcTable[n] = c; | ||||
|         } | ||||
|         return crcTable; | ||||
|     } | ||||
| 
 | ||||
|     var crc32 = function (buf) { | ||||
|         const bufview = new DataView(buf); | ||||
|         var crcTable = window.crcTable || (window.crcTable = makeCRCTable()); | ||||
|         var crc = 0 ^ (-1); | ||||
| 
 | ||||
|         for (var i = 0; i < bufview.byteLength; i++) { | ||||
|             crc = (crc >>> 8) ^ crcTable[(crc ^ bufview.getInt8(i)) & 0xFF]; | ||||
|         } | ||||
| 
 | ||||
|         return (crc ^ (-1)) >>> 0; | ||||
|     }; | ||||
| 
 | ||||
|     var firmware_file; | ||||
| 
 | ||||
|     function load_firmware(evt) { | ||||
|         var file = evt.target.files[0]; | ||||
|         if (!file) { | ||||
|             return; | ||||
|         } | ||||
|         var reader = new FileReader(); | ||||
|         reader.onload = function (e) { | ||||
|             firmware_file = e.target.result; | ||||
|             document.getElementById("transmit_firmware").disabled = false; | ||||
|         }; | ||||
|         reader.readAsArrayBuffer(file) | ||||
|     } | ||||
| 
 | ||||
|     const chunk_size = 512; | ||||
| 
 | ||||
|     function transmit_firmware_chunk(buf, i) { | ||||
|         return new Promise((resolve, reject) => { | ||||
|             const begin = i * chunk_size; | ||||
|             const end = Math.min((i + 1) * chunk_size, buf.byteLength); | ||||
|             const slice = buf.slice(begin, end) | ||||
|             var header = new ArrayBuffer(12); | ||||
|             var headerview = new DataView(header); | ||||
|             headerview.setChar(0, 'F'); | ||||
|             headerview.setInt16(4, i); | ||||
|             headerview.setInt16(6, (end - begin)); | ||||
|             headerview.setInt32(8, crc32(slice)); | ||||
|             var frame = new Uint8Array(12 + slice.byteLength); | ||||
|             frame.set(new Uint8Array(header), 0); | ||||
|             frame.set(new Uint8Array(slice), 12); | ||||
|             receive_chunk_confirmation = (dv) => { | ||||
|                 setTimeout(() => { | ||||
|                     resolve(i); | ||||
|                 }, 50); | ||||
|             } | ||||
|             setTimeout(() => { | ||||
|                 reject(i); | ||||
|             }, 2000); | ||||
|             wsWrite(frame.buffer); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     function transmit_firmware_final(buf, hash) { | ||||
|         return new Promise((resolve, reject) => { | ||||
|             var frame = new ArrayBuffer(12); | ||||
|             var headerview = new DataView(frame); | ||||
|             headerview.setChar(0, 'C'); | ||||
|             headerview.setInt32(4, buf.byteLength); | ||||
|             headerview.setInt32(8, hash); | ||||
|             receive_chunk_confirmation = (dv) => { | ||||
|                 resolve(i); | ||||
|             } | ||||
|             setTimeout(() => { | ||||
|                 reject(i); | ||||
|             }, 500); | ||||
|             wsWrite(frame); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     function transmit_firmware(evt) { | ||||
|         console.log("transmit_firmware begin"); | ||||
|         if (firmware_file) { | ||||
|             (async () => { | ||||
|                 const ash = crc32(firmware_file); | ||||
|                 for (var i = 0; i * chunk_size < firmware_file.byteLength; i++) { | ||||
|                     await transmit_firmware_chunk(firmware_file, i); | ||||
|                 } | ||||
|                 await transmit_firmware_final(firmware_file, crc32(firmware_file)); | ||||
|             })().then(() => { | ||||
|                 console.log("transmit_firmware done"); | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
|  |  | |||
|  | @ -3,35 +3,114 @@ | |||
| //
 | ||||
| 
 | ||||
| #include "system.h" | ||||
| #include "crc32.h" | ||||
| 
 | ||||
| #include <FreeRTOS.h> | ||||
| #include <task.h> | ||||
| #include <sysparam.h> | ||||
| #include <spiflash.h> | ||||
| 
 | ||||
| #include <espressif/esp_common.h> | ||||
| #include <espressif/user_interface.h> | ||||
| #include <espressif/esp_system.h> | ||||
| #include <rboot/rboot.h> | ||||
| #include <rboot-ota/rboot-api.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| void system_clear_config(){ | ||||
| #define min(a, b) \ | ||||
|  ({ __typeof__ (a) _a = (a); \ | ||||
|  __typeof__ (b) _b = (b); \ | ||||
|  _a < _b ? _a : _b; }) | ||||
| 
 | ||||
| void system_clear_config() { | ||||
|     vPortEnterCritical(); | ||||
|     uint32_t num_sectors = 5 + DEFAULT_SYSPARAM_SECTORS; | ||||
|     uint32_t start = sdk_flashchip.chip_size - num_sectors * sdk_flashchip.sector_size; | ||||
|     uint32_t num_sectors = 0x2000 / sdk_flashchip.sector_size; | ||||
|     uint32_t start = 0x00100000; | ||||
|     for (uint32_t i = 0; i < num_sectors; i++) { | ||||
|         spiflash_erase_sector(start + i * sdk_flashchip.sector_size); | ||||
|     } | ||||
|     if(sysparam_create_area(start, num_sectors, true) == SYSPARAM_OK) { | ||||
|         sysparam_init(start, 0); | ||||
|     } | ||||
|     sysparam_init(start, start + 0x2000); | ||||
|     sdk_system_restart(); | ||||
| } | ||||
| 
 | ||||
| void system_init_config(){ | ||||
|     uint32_t base_addr; | ||||
| void system_init_config() { | ||||
|     uint32_t base_addr = 0x00100000; | ||||
|     uint32_t num_sectors; | ||||
|     sysparam_init(base_addr, 0); | ||||
|     if(sysparam_get_info(&base_addr, &num_sectors) != SYSPARAM_OK) { | ||||
|         printf("Warning: WiFi config, sysparam not initialized\n"); | ||||
|         num_sectors = DEFAULT_SYSPARAM_SECTORS; | ||||
|         base_addr = sdk_flashchip.chip_size - (5 + num_sectors) * sdk_flashchip.sector_size; | ||||
|         num_sectors = 0x2000 / sdk_flashchip.sector_size; | ||||
|         if(sysparam_create_area(base_addr, num_sectors, true) == SYSPARAM_OK) { | ||||
|             sysparam_init(base_addr, 0); | ||||
|         } | ||||
|         sdk_system_restart(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #define MAX_IMAGE_SIZE 0x100000 | ||||
| 
 | ||||
| struct { | ||||
|     rboot_write_status status; | ||||
|     uint32_t head; | ||||
|     uint32_t base; | ||||
|     uint16_t seq; | ||||
|     uint8_t slot; | ||||
| } otaflash_context; | ||||
| 
 | ||||
| void system_otaflash_init() { | ||||
|     rboot_config conf; | ||||
|     conf = rboot_get_config(); | ||||
|     otaflash_context.slot = (conf.current_rom + 1) % conf.count; | ||||
|     otaflash_context.base = rboot_get_slot_offset(otaflash_context.slot); | ||||
|     otaflash_context.status = rboot_write_init(otaflash_context.base); | ||||
|     otaflash_context.head = otaflash_context.base; | ||||
|     otaflash_context.seq = 0; | ||||
| } | ||||
| 
 | ||||
| int system_otaflash_chunk(uint8_t *data, uint16_t len, uint16_t seq, uint32_t hash) { | ||||
|     uint32_t local_hash = crc32(data, len); | ||||
|     if(hash == local_hash && otaflash_context.seq == seq) { | ||||
|         if(otaflash_context.head % SECTOR_SIZE == 0) { | ||||
|             sdk_spi_flash_erase_sector(otaflash_context.head / SECTOR_SIZE); | ||||
|         } | ||||
|         if(((uint32_t) data) % 4) { | ||||
|             uint32 buf[len / 4]; | ||||
|             memcpy(buf, data, len); | ||||
|             sdk_spi_flash_write(otaflash_context.head, buf, len); | ||||
|         } else { | ||||
|             sdk_spi_flash_write(otaflash_context.head, (uint32_t *) data, len); | ||||
|         } | ||||
|         otaflash_context.head += len; | ||||
|         otaflash_context.seq++; | ||||
|         return 0x88; | ||||
|     } else { | ||||
|         return 0xff; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void system_otaflash_verify_chunk(void *ctx, void *data, size_t len) { | ||||
|     uint32_t digest = *(uint32_t *) ctx; | ||||
|     digest = crc32_partial(digest, data, len); | ||||
|     *(uint32_t *) ctx = digest; | ||||
| } | ||||
| 
 | ||||
| int system_otaflash_verify_and_switch(uint32_t len, uint32_t hash) { | ||||
| 
 | ||||
|     uint32_t digest = 0; | ||||
|     rboot_digest_image(otaflash_context.base, min(len, MAX_IMAGE_SIZE), system_otaflash_verify_chunk, &digest); | ||||
| 
 | ||||
|     if(hash != digest) { | ||||
|         printf("OTA failed to verify firmware\r\n"); | ||||
|         return 0x99; | ||||
|     } | ||||
| 
 | ||||
|     vPortEnterCritical(); | ||||
|     if(!rboot_set_current_rom(otaflash_context.slot)) { | ||||
|         printf("OTA failed to set new rboot slot\r\n"); | ||||
|     } | ||||
|     sdk_system_restart(); | ||||
|     vPortExitCritical(); // | should not be reached
 | ||||
|     return 0x77;         // |
 | ||||
| } | ||||
|  | @ -5,13 +5,22 @@ | |||
| #ifndef FIRMWARE_SYSTEM_H | ||||
| #define FIRMWARE_SYSTEM_H | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| void system_clear_config(); | ||||
| 
 | ||||
| void system_init_config(); | ||||
| 
 | ||||
| void system_otaflash_init(); | ||||
| 
 | ||||
| int system_otaflash_chunk(uint8_t *data, uint16_t len, uint16_t seq, uint32_t hash); | ||||
| 
 | ||||
| int system_otaflash_verify_and_switch(uint32_t len, uint32_t hash); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										123
									
								
								firmware/web.cpp
									
										
									
									
									
								
							
							
						
						
									
										123
									
								
								firmware/web.cpp
									
										
									
									
									
								
							|  | @ -38,7 +38,7 @@ void websocket_task(void *pvParameter) { | |||
|     has_changed = {true, true, true}; | ||||
| 
 | ||||
|     for (;;) { | ||||
|         if(pcb == NULL || pcb->state != ESTABLISHED) { | ||||
|         if(pcb == nullptr || pcb->state != ESTABLISHED) { | ||||
|             printf("Connection closed, deleting task\n"); | ||||
|             break; | ||||
|         } | ||||
|  | @ -46,14 +46,14 @@ void websocket_task(void *pvParameter) { | |||
|         //Global Info
 | ||||
|         if(has_changed.global) { | ||||
|             has_changed.global = false; | ||||
|             timeval tv; | ||||
|             gettimeofday(&tv, NULL); | ||||
|             int uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; | ||||
|             timeval tv{}; | ||||
|             gettimeofday(&tv, nullptr); | ||||
|             size_t uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; | ||||
|             int heap = (int) xPortGetFreeHeapSize(); | ||||
|             uint32_t chip_id = sdk_system_get_chip_id(); | ||||
|             uint32_t flash_id = sdk_spi_flash_get_id(); | ||||
|             uint32_t flash_size = sdk_flashchip.chip_size >> 10; | ||||
|             char *hostname = NULL; | ||||
|             char *hostname = nullptr; | ||||
| 
 | ||||
|             sysparam_get_string("hostname", &hostname); | ||||
|             /* Generate response in JSON format */ | ||||
|  | @ -80,21 +80,16 @@ void websocket_task(void *pvParameter) { | |||
|         //Connection Info
 | ||||
|         if(has_changed.connection) { | ||||
|             has_changed.connection = false; | ||||
|             timeval tv; | ||||
|             gettimeofday(&tv, NULL); | ||||
|             int connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000; | ||||
|             timeval tv{}; | ||||
|             gettimeofday(&tv, nullptr); | ||||
|             size_t connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000; | ||||
| 
 | ||||
|             printf("conn %d: " | ||||
|                    IPSTR | ||||
|                    " <-> " | ||||
|                    IPSTR | ||||
|                    " \n", pcb->netif_idx, IP2STR(&pcb->local_ip), IP2STR(&pcb->remote_ip)); | ||||
|             printf("conn %d: " IPSTR " <-> " IPSTR " \n", pcb->netif_idx, IP2STR(&pcb->local_ip), | ||||
|                    IP2STR(&pcb->remote_ip)); | ||||
|             char response[160]; | ||||
|             size_t len = snprintf(response, sizeof(response), | ||||
|                                   "{\"connage\" : \"%d\"," | ||||
|                                   "\"clientip\" : \"" | ||||
|                                   IPSTR | ||||
|                                   "\"" | ||||
|                                   "\"clientip\" : \"" IPSTR "\"" | ||||
|                                   "}", connuptime, IP2STR(&pcb->remote_ip)); | ||||
|             if(len < sizeof(response)) { | ||||
|                 LOCK_TCPIP_CORE(); | ||||
|  | @ -137,10 +132,10 @@ void websocket_task(void *pvParameter) { | |||
|             if(opmode == SOFTAP_MODE || opmode == STATIONAP_MODE) { | ||||
|                 uint8_t hwaddr[6]; | ||||
|                 sdk_wifi_get_macaddr(SOFTAP_IF, hwaddr); | ||||
|                 ip_info info; | ||||
|                 ip_info info{}; | ||||
|                 sdk_wifi_get_ip_info(SOFTAP_IF, &info); | ||||
| 
 | ||||
|                 char *apssid = NULL; | ||||
|                 char *apssid = nullptr; | ||||
|                 sysparam_get_string("wifi_ap_ssid", &apssid); | ||||
| 
 | ||||
|                 /* Generate response in JSON format */ | ||||
|  | @ -148,12 +143,8 @@ void websocket_task(void *pvParameter) { | |||
|                 size_t len = snprintf(response, sizeof(response), | ||||
|                                       "{\"opmode\" : \"%s\"," | ||||
|                                       " \"apssid\" : \"%s\"," | ||||
|                                       " \"apip\" : \"" | ||||
|                                       IPSTR | ||||
|                                       "\"," | ||||
|                                       " \"apmac\" : \"" | ||||
|                                       MACSTR | ||||
|                                       "\"" | ||||
|                                       " \"apip\" : \"" IPSTR "\"," | ||||
|                                       " \"apmac\" : \"" MACSTR "\"" | ||||
|                                       "}", opmode_str, apssid, IP2STR(&info.ip), MAC2STR(hwaddr)); | ||||
|                 free(apssid); | ||||
|                 if(len < sizeof(response)) { | ||||
|  | @ -169,7 +160,7 @@ void websocket_task(void *pvParameter) { | |||
|             if(opmode == STATION_MODE || opmode == STATIONAP_MODE) { | ||||
|                 uint8_t hwaddr[6]; | ||||
|                 sdk_wifi_get_macaddr(STATION_IF, hwaddr); | ||||
|                 ip_info info; | ||||
|                 ip_info info{}; | ||||
|                 sdk_wifi_get_ip_info(STATION_IF, &info); | ||||
|                 char *stassid = nullptr; | ||||
|                 sysparam_get_string("wifi_sta_ssid", &stassid); | ||||
|  | @ -179,12 +170,8 @@ void websocket_task(void *pvParameter) { | |||
|                 size_t len = snprintf(response, sizeof(response), | ||||
|                                       "{\"opmode\" : \"%s\"," | ||||
|                                       " \"stassid\" : \"%s\"," | ||||
|                                       " \"staip\" : \"" | ||||
|                                       IPSTR | ||||
|                                       "\"," | ||||
|                                       " \"stamac\" : \"" | ||||
|                                       MACSTR | ||||
|                                       "\"" | ||||
|                                       " \"staip\" : \"" IPSTR "\"," | ||||
|                                       " \"stamac\" : \"" MACSTR "\"" | ||||
|                                       "}", opmode_str, stassid, IP2STR(&info.ip), MAC2STR(hwaddr)); | ||||
|                 free(stassid); | ||||
|                 if(len < sizeof(response)) { | ||||
|  | @ -198,34 +185,49 @@ void websocket_task(void *pvParameter) { | |||
|         } | ||||
| 
 | ||||
|         vTaskDelayMs(500); | ||||
|         { | ||||
|             uint8_t response[3]; | ||||
|             uint16_t val = 0; | ||||
|             val = sdk_system_adc_read(); | ||||
|             response[2] = (uint8_t) val; | ||||
|             response[1] = val >> 8; | ||||
|             response[0] = 'V'; | ||||
|             websocket_write(pcb, response, 3, WS_BIN_MODE); | ||||
|         } | ||||
|         vTaskDelayMs(500); | ||||
|     } | ||||
| 
 | ||||
|     vTaskDelete(NULL); | ||||
|     vTaskDelete(nullptr); | ||||
| } | ||||
| 
 | ||||
| struct fw_frame { | ||||
|     char t; | ||||
|     uint8_t reserved[3]; | ||||
|     uint16_t seq; | ||||
|     uint16_t len; | ||||
|     uint32_t hash; | ||||
|     uint8_t data[]; | ||||
| } __attribute__((packed)); | ||||
| 
 | ||||
| struct fw_check { | ||||
|     char t; | ||||
|     uint8_t reserved[3]; | ||||
|     uint32_t len; | ||||
|     uint32_t hash; | ||||
| } __attribute__((packed)); | ||||
| 
 | ||||
| /**
 | ||||
|  * This function is called when websocket frame is received. | ||||
|  * | ||||
|  * Note: this function is executed on TCP thread and should return as soon | ||||
|  * as possible. | ||||
|  */ | ||||
| void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode) { | ||||
| void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, | ||||
|                   uint8_t /*mode*/) { //mode should be WS_BIN_MODE or WS_TEXT_MODE
 | ||||
| 
 | ||||
|     uint8_t response[3]; | ||||
|     uint16_t val = 0; | ||||
|     char cmd = '0'; | ||||
| 
 | ||||
|     bool togl = 0; | ||||
| 
 | ||||
|     switch (data[0]) { | ||||
|         case 'R': // Restart
 | ||||
|             cmd = 'R'; | ||||
|             break; | ||||
|         case 'X': // Clear Config
 | ||||
|             cmd = 'X'; | ||||
|             break; | ||||
|         case 'D': // Disable LED
 | ||||
|             signal_led(false); | ||||
|             val = 1; | ||||
|  | @ -236,9 +238,29 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode) | |||
|             val = 0; | ||||
|             cmd = 'G'; | ||||
|             break; | ||||
|         case 'F': | ||||
|             togl = ~togl; | ||||
|             signal_led(togl); | ||||
|             { | ||||
|                 auto *f = (fw_frame *) data; | ||||
|                 if(f->seq == 0) { | ||||
|                     system_otaflash_init(); | ||||
|                 } | ||||
|                 val = system_otaflash_chunk(f->data, ntohs(f->len), ntohs(f->seq), ntohl(f->hash)); | ||||
|             } | ||||
|             cmd = 'F'; | ||||
|             break; | ||||
|         case 'C': | ||||
|             signal_led(false); | ||||
|             { | ||||
|                 auto *f = (fw_check *) data; | ||||
|                 val = system_otaflash_verify_and_switch(ntohl(f->len), ntohl(f->hash)); | ||||
|             } | ||||
|             cmd = 'C'; | ||||
|             break; | ||||
|         default: | ||||
|             printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char *) data); | ||||
|             printf("Unknown command\n"); | ||||
|             printf("Unknown command %c\n", data[0]); | ||||
|             val = 0; | ||||
|             break; | ||||
|     } | ||||
|  | @ -247,7 +269,18 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode) | |||
|     response[1] = val >> 8; | ||||
|     response[0] = cmd; | ||||
| 
 | ||||
|     LOCK_TCPIP_CORE(); | ||||
|     websocket_write(pcb, response, 3, WS_BIN_MODE); | ||||
|     UNLOCK_TCPIP_CORE(); | ||||
| 
 | ||||
|     if(data[0] == 'R') { // Restart
 | ||||
|         vTaskDelay(500 / portTICK_PERIOD_MS); | ||||
|         vPortEnterCritical(); | ||||
|         sdk_system_restart(); | ||||
|     } else if(data[0] == 'X') { // Clear Config
 | ||||
|         vTaskDelay(500 / portTICK_PERIOD_MS); | ||||
|         system_clear_config(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -262,10 +295,10 @@ void websocket_open_cb(struct tcp_pcb *pcb, const char *uri) { | |||
| } | ||||
| 
 | ||||
| extern "C" void httpd_task(void *pvParameters) { | ||||
|     (void) pvParameters; | ||||
| 
 | ||||
|     while (!uxSemaphoreGetCount(wifi_available_semaphore)) | ||||
|         vTaskDelay(500 / portTICK_PERIOD_MS); | ||||
| 
 | ||||
|     websocket_register_callbacks((tWsOpenHandler) websocket_open_cb, (tWsHandler) websocket_cb); | ||||
|     httpd_init(); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue