forked from j3d1/fiatlux
		
	add basic OTA Update fucionality
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"?> | <?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_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 | LIBS = hal m | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -22,9 +22,9 @@ void user_init(void) | ||||||
| 
 | 
 | ||||||
|     wifi_available_semaphore = xSemaphoreCreateBinary(); |     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); |     xTaskCreate(&lux_task, "lux_task", 512, NULL, 1, NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -16,9 +16,48 @@ | ||||||
|     <label for="bmenub" class="burger pseudo button">☰</label> |     <label for="bmenub" class="burger pseudo button">☰</label> | ||||||
|     <div class="menu"> |     <div class="menu"> | ||||||
|         <a href="/#" class="button icon-picture">Dashboard</a> |         <a href="/#" class="button icon-picture">Dashboard</a> | ||||||
|  |         <a href="/#ota" class="button icon-picture">System</a> | ||||||
|     </div> |     </div> | ||||||
| </nav> | </nav> | ||||||
| <main id="page"> | <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"> |     <section id="dashboard"> | ||||||
|         <h2>Status</h2> |         <h2>Status</h2> | ||||||
|         <div class="flex"> |         <div class="flex"> | ||||||
|  | @ -133,19 +172,18 @@ | ||||||
| <div id="unused_values" style="display:none;"></div> | <div id="unused_values" style="display:none;"></div> | ||||||
| <script type="text/javascript" src="js/smoothie_min.js"></script> | <script type="text/javascript" src="js/smoothie_min.js"></script> | ||||||
| <script> | <script> | ||||||
|     var section = document.getElementById("page"); |  | ||||||
|     var menu = document.getElementById("bmenub"); |     var menu = document.getElementById("bmenub"); | ||||||
|     var voltage = document.getElementById("out_voltage"); |     var voltage = document.getElementById("out_voltage"); | ||||||
| 
 | 
 | ||||||
|     var unused_values = {}; |     var unused_values = {}; | ||||||
| 
 | 
 | ||||||
|     DataView.prototype.setChar = function(pos, char) { |     DataView.prototype.setChar = function (pos, char) { | ||||||
|         this.setInt8(pos++, char.charCodeAt(0)); |         this.setInt8(pos++, char.charCodeAt(0)); | ||||||
|         return pos; |         return pos; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     DataView.prototype.setString = function(pos, str) { |     DataView.prototype.setString = function (pos, str) { | ||||||
|         for(var i = 0; i < str.length; i++) { |         for (var i = 0; i < str.length; i++) { | ||||||
|             this.setInt8(pos++, str.charCodeAt(i)); |             this.setInt8(pos++, str.charCodeAt(i)); | ||||||
|         } |         } | ||||||
|         this.setInt8(pos++, 0); |         this.setInt8(pos++, 0); | ||||||
|  | @ -175,6 +213,9 @@ | ||||||
|         chart.streamTo(canvas, 500); |         chart.streamTo(canvas, 500); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     var receive_chunk_confirmation = () => { | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     function onMessage(evt) { |     function onMessage(evt) { | ||||||
|         retries = 0; |         retries = 0; | ||||||
|         if (typeof evt.data == 'string') { |         if (typeof evt.data == 'string') { | ||||||
|  | @ -196,7 +237,9 @@ | ||||||
| 
 | 
 | ||||||
|             if (cmd === 'G') |             if (cmd === 'G') | ||||||
|                 console.log("LED switched", val); |                 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); |                 voltage.innerHTML = (val * 13 / 1024).toFixed(2); | ||||||
|                 series.append(new Date().getTime(), val); |                 series.append(new Date().getTime(), val); | ||||||
|             } else |             } else | ||||||
|  | @ -233,7 +276,7 @@ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function wsWrite(data) { |     function wsWrite(data) { | ||||||
|         console.info(buf2hex(data)); |         //console.info(buf2hex(data)); | ||||||
|         if (ws.readyState === 3 || retries++ > 5) |         if (ws.readyState === 3 || retries++ > 5) | ||||||
|             wsOpen(); |             wsOpen(); | ||||||
|         else if (ws.readyState === 1) |         else if (ws.readyState === 1) | ||||||
|  | @ -252,6 +295,105 @@ | ||||||
|         startPolling(); |         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> | </script> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|  |  | ||||||
|  | @ -3,35 +3,114 @@ | ||||||
| //
 | //
 | ||||||
| 
 | 
 | ||||||
| #include "system.h" | #include "system.h" | ||||||
|  | #include "crc32.h" | ||||||
| 
 | 
 | ||||||
| #include <FreeRTOS.h> | #include <FreeRTOS.h> | ||||||
| #include <task.h> |  | ||||||
| #include <sysparam.h> | #include <sysparam.h> | ||||||
| #include <spiflash.h> | #include <spiflash.h> | ||||||
| 
 | 
 | ||||||
| #include <espressif/esp_common.h> |  | ||||||
| #include <espressif/user_interface.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(); |     vPortEnterCritical(); | ||||||
|     uint32_t num_sectors = 5 + DEFAULT_SYSPARAM_SECTORS; |     uint32_t num_sectors = 0x2000 / sdk_flashchip.sector_size; | ||||||
|     uint32_t start = sdk_flashchip.chip_size - num_sectors * sdk_flashchip.sector_size; |     uint32_t start = 0x00100000; | ||||||
|     for (uint32_t i = 0; i < num_sectors; i++) { |     for (uint32_t i = 0; i < num_sectors; i++) { | ||||||
|         spiflash_erase_sector(start + i * sdk_flashchip.sector_size); |         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(); |     sdk_system_restart(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void system_init_config(){ | void system_init_config() { | ||||||
|     uint32_t base_addr; |     uint32_t base_addr = 0x00100000; | ||||||
|     uint32_t num_sectors; |     uint32_t num_sectors; | ||||||
|  |     sysparam_init(base_addr, 0); | ||||||
|     if(sysparam_get_info(&base_addr, &num_sectors) != SYSPARAM_OK) { |     if(sysparam_get_info(&base_addr, &num_sectors) != SYSPARAM_OK) { | ||||||
|         printf("Warning: WiFi config, sysparam not initialized\n"); |         printf("Warning: WiFi config, sysparam not initialized\n"); | ||||||
|         num_sectors = DEFAULT_SYSPARAM_SECTORS; |         num_sectors = 0x2000 / sdk_flashchip.sector_size; | ||||||
|         base_addr = sdk_flashchip.chip_size - (5 + num_sectors) * sdk_flashchip.sector_size; |  | ||||||
|         if(sysparam_create_area(base_addr, num_sectors, true) == SYSPARAM_OK) { |         if(sysparam_create_area(base_addr, num_sectors, true) == SYSPARAM_OK) { | ||||||
|             sysparam_init(base_addr, 0); |             sysparam_init(base_addr, 0); | ||||||
|         } |         } | ||||||
|         sdk_system_restart(); |         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 | #ifndef FIRMWARE_SYSTEM_H | ||||||
| #define FIRMWARE_SYSTEM_H | #define FIRMWARE_SYSTEM_H | ||||||
| 
 | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| extern "C" { | extern "C" { | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| void system_clear_config(); | void system_clear_config(); | ||||||
|  | 
 | ||||||
| void system_init_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 | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
							
								
								
									
										123
									
								
								firmware/web.cpp
									
										
									
									
									
								
							
							
						
						
									
										123
									
								
								firmware/web.cpp
									
										
									
									
									
								
							|  | @ -38,7 +38,7 @@ void websocket_task(void *pvParameter) { | ||||||
|     has_changed = {true, true, true}; |     has_changed = {true, true, true}; | ||||||
| 
 | 
 | ||||||
|     for (;;) { |     for (;;) { | ||||||
|         if(pcb == NULL || pcb->state != ESTABLISHED) { |         if(pcb == nullptr || pcb->state != ESTABLISHED) { | ||||||
|             printf("Connection closed, deleting task\n"); |             printf("Connection closed, deleting task\n"); | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|  | @ -46,14 +46,14 @@ void websocket_task(void *pvParameter) { | ||||||
|         //Global Info
 |         //Global Info
 | ||||||
|         if(has_changed.global) { |         if(has_changed.global) { | ||||||
|             has_changed.global = false; |             has_changed.global = false; | ||||||
|             timeval tv; |             timeval tv{}; | ||||||
|             gettimeofday(&tv, NULL); |             gettimeofday(&tv, nullptr); | ||||||
|             int uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; |             size_t uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; | ||||||
|             int heap = (int) xPortGetFreeHeapSize(); |             int heap = (int) xPortGetFreeHeapSize(); | ||||||
|             uint32_t chip_id = sdk_system_get_chip_id(); |             uint32_t chip_id = sdk_system_get_chip_id(); | ||||||
|             uint32_t flash_id = sdk_spi_flash_get_id(); |             uint32_t flash_id = sdk_spi_flash_get_id(); | ||||||
|             uint32_t flash_size = sdk_flashchip.chip_size >> 10; |             uint32_t flash_size = sdk_flashchip.chip_size >> 10; | ||||||
|             char *hostname = NULL; |             char *hostname = nullptr; | ||||||
| 
 | 
 | ||||||
|             sysparam_get_string("hostname", &hostname); |             sysparam_get_string("hostname", &hostname); | ||||||
|             /* Generate response in JSON format */ |             /* Generate response in JSON format */ | ||||||
|  | @ -80,21 +80,16 @@ void websocket_task(void *pvParameter) { | ||||||
|         //Connection Info
 |         //Connection Info
 | ||||||
|         if(has_changed.connection) { |         if(has_changed.connection) { | ||||||
|             has_changed.connection = false; |             has_changed.connection = false; | ||||||
|             timeval tv; |             timeval tv{}; | ||||||
|             gettimeofday(&tv, NULL); |             gettimeofday(&tv, nullptr); | ||||||
|             int connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000; |             size_t connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000; | ||||||
| 
 | 
 | ||||||
|             printf("conn %d: " |             printf("conn %d: " IPSTR " <-> " IPSTR " \n", pcb->netif_idx, IP2STR(&pcb->local_ip), | ||||||
|                    IPSTR |                    IP2STR(&pcb->remote_ip)); | ||||||
|                    " <-> " |  | ||||||
|                    IPSTR |  | ||||||
|                    " \n", pcb->netif_idx, IP2STR(&pcb->local_ip), IP2STR(&pcb->remote_ip)); |  | ||||||
|             char response[160]; |             char response[160]; | ||||||
|             size_t len = snprintf(response, sizeof(response), |             size_t len = snprintf(response, sizeof(response), | ||||||
|                                   "{\"connage\" : \"%d\"," |                                   "{\"connage\" : \"%d\"," | ||||||
|                                   "\"clientip\" : \"" |                                   "\"clientip\" : \"" IPSTR "\"" | ||||||
|                                   IPSTR |  | ||||||
|                                   "\"" |  | ||||||
|                                   "}", connuptime, IP2STR(&pcb->remote_ip)); |                                   "}", connuptime, IP2STR(&pcb->remote_ip)); | ||||||
|             if(len < sizeof(response)) { |             if(len < sizeof(response)) { | ||||||
|                 LOCK_TCPIP_CORE(); |                 LOCK_TCPIP_CORE(); | ||||||
|  | @ -137,10 +132,10 @@ void websocket_task(void *pvParameter) { | ||||||
|             if(opmode == SOFTAP_MODE || opmode == STATIONAP_MODE) { |             if(opmode == SOFTAP_MODE || opmode == STATIONAP_MODE) { | ||||||
|                 uint8_t hwaddr[6]; |                 uint8_t hwaddr[6]; | ||||||
|                 sdk_wifi_get_macaddr(SOFTAP_IF, hwaddr); |                 sdk_wifi_get_macaddr(SOFTAP_IF, hwaddr); | ||||||
|                 ip_info info; |                 ip_info info{}; | ||||||
|                 sdk_wifi_get_ip_info(SOFTAP_IF, &info); |                 sdk_wifi_get_ip_info(SOFTAP_IF, &info); | ||||||
| 
 | 
 | ||||||
|                 char *apssid = NULL; |                 char *apssid = nullptr; | ||||||
|                 sysparam_get_string("wifi_ap_ssid", &apssid); |                 sysparam_get_string("wifi_ap_ssid", &apssid); | ||||||
| 
 | 
 | ||||||
|                 /* Generate response in JSON format */ |                 /* Generate response in JSON format */ | ||||||
|  | @ -148,12 +143,8 @@ void websocket_task(void *pvParameter) { | ||||||
|                 size_t len = snprintf(response, sizeof(response), |                 size_t len = snprintf(response, sizeof(response), | ||||||
|                                       "{\"opmode\" : \"%s\"," |                                       "{\"opmode\" : \"%s\"," | ||||||
|                                       " \"apssid\" : \"%s\"," |                                       " \"apssid\" : \"%s\"," | ||||||
|                                       " \"apip\" : \"" |                                       " \"apip\" : \"" IPSTR "\"," | ||||||
|                                       IPSTR |                                       " \"apmac\" : \"" MACSTR "\"" | ||||||
|                                       "\"," |  | ||||||
|                                       " \"apmac\" : \"" |  | ||||||
|                                       MACSTR |  | ||||||
|                                       "\"" |  | ||||||
|                                       "}", opmode_str, apssid, IP2STR(&info.ip), MAC2STR(hwaddr)); |                                       "}", opmode_str, apssid, IP2STR(&info.ip), MAC2STR(hwaddr)); | ||||||
|                 free(apssid); |                 free(apssid); | ||||||
|                 if(len < sizeof(response)) { |                 if(len < sizeof(response)) { | ||||||
|  | @ -169,7 +160,7 @@ void websocket_task(void *pvParameter) { | ||||||
|             if(opmode == STATION_MODE || opmode == STATIONAP_MODE) { |             if(opmode == STATION_MODE || opmode == STATIONAP_MODE) { | ||||||
|                 uint8_t hwaddr[6]; |                 uint8_t hwaddr[6]; | ||||||
|                 sdk_wifi_get_macaddr(STATION_IF, hwaddr); |                 sdk_wifi_get_macaddr(STATION_IF, hwaddr); | ||||||
|                 ip_info info; |                 ip_info info{}; | ||||||
|                 sdk_wifi_get_ip_info(STATION_IF, &info); |                 sdk_wifi_get_ip_info(STATION_IF, &info); | ||||||
|                 char *stassid = nullptr; |                 char *stassid = nullptr; | ||||||
|                 sysparam_get_string("wifi_sta_ssid", &stassid); |                 sysparam_get_string("wifi_sta_ssid", &stassid); | ||||||
|  | @ -179,12 +170,8 @@ void websocket_task(void *pvParameter) { | ||||||
|                 size_t len = snprintf(response, sizeof(response), |                 size_t len = snprintf(response, sizeof(response), | ||||||
|                                       "{\"opmode\" : \"%s\"," |                                       "{\"opmode\" : \"%s\"," | ||||||
|                                       " \"stassid\" : \"%s\"," |                                       " \"stassid\" : \"%s\"," | ||||||
|                                       " \"staip\" : \"" |                                       " \"staip\" : \"" IPSTR "\"," | ||||||
|                                       IPSTR |                                       " \"stamac\" : \"" MACSTR "\"" | ||||||
|                                       "\"," |  | ||||||
|                                       " \"stamac\" : \"" |  | ||||||
|                                       MACSTR |  | ||||||
|                                       "\"" |  | ||||||
|                                       "}", opmode_str, stassid, IP2STR(&info.ip), MAC2STR(hwaddr)); |                                       "}", opmode_str, stassid, IP2STR(&info.ip), MAC2STR(hwaddr)); | ||||||
|                 free(stassid); |                 free(stassid); | ||||||
|                 if(len < sizeof(response)) { |                 if(len < sizeof(response)) { | ||||||
|  | @ -198,34 +185,49 @@ void websocket_task(void *pvParameter) { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         vTaskDelayMs(500); |         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. |  * This function is called when websocket frame is received. | ||||||
|  * |  * | ||||||
|  * Note: this function is executed on TCP thread and should return as soon |  * Note: this function is executed on TCP thread and should return as soon | ||||||
|  * as possible. |  * 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]; |     uint8_t response[3]; | ||||||
|     uint16_t val = 0; |     uint16_t val = 0; | ||||||
|     char cmd = '0'; |     char cmd = '0'; | ||||||
| 
 | 
 | ||||||
|  |     bool togl = 0; | ||||||
|  | 
 | ||||||
|     switch (data[0]) { |     switch (data[0]) { | ||||||
|  |         case 'R': // Restart
 | ||||||
|  |             cmd = 'R'; | ||||||
|  |             break; | ||||||
|  |         case 'X': // Clear Config
 | ||||||
|  |             cmd = 'X'; | ||||||
|  |             break; | ||||||
|         case 'D': // Disable LED
 |         case 'D': // Disable LED
 | ||||||
|             signal_led(false); |             signal_led(false); | ||||||
|             val = 1; |             val = 1; | ||||||
|  | @ -236,9 +238,29 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t mode) | ||||||
|             val = 0; |             val = 0; | ||||||
|             cmd = 'G'; |             cmd = 'G'; | ||||||
|             break; |             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: |         default: | ||||||
|             printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char *) data); |             printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char *) data); | ||||||
|             printf("Unknown command\n"); |             printf("Unknown command %c\n", data[0]); | ||||||
|             val = 0; |             val = 0; | ||||||
|             break; |             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[1] = val >> 8; | ||||||
|     response[0] = cmd; |     response[0] = cmd; | ||||||
| 
 | 
 | ||||||
|  |     LOCK_TCPIP_CORE(); | ||||||
|     websocket_write(pcb, response, 3, WS_BIN_MODE); |     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) { | extern "C" void httpd_task(void *pvParameters) { | ||||||
|  |     (void) pvParameters; | ||||||
| 
 | 
 | ||||||
|     while (!uxSemaphoreGetCount(wifi_available_semaphore)) |     while (!uxSemaphoreGetCount(wifi_available_semaphore)) | ||||||
|         vTaskDelay(500 / portTICK_PERIOD_MS); |         vTaskDelay(500 / portTICK_PERIOD_MS); | ||||||
| 
 |  | ||||||
|     websocket_register_callbacks((tWsOpenHandler) websocket_open_cb, (tWsHandler) websocket_cb); |     websocket_register_callbacks((tWsOpenHandler) websocket_open_cb, (tWsHandler) websocket_cb); | ||||||
|     httpd_init(); |     httpd_init(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue