forked from j3d1/fiatlux
		
	Compare commits
	
		
			1 commit
		
	
	
		
			stable
			...
			jedi/timed
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a806910034 | 
					 13 changed files with 647 additions and 384 deletions
				
			
		|  | @ -57,7 +57,6 @@ steps: | |||
|     base_url: https://git.neulandlabor.de/ | ||||
|     files:  | ||||
|       - firmware/firmware/fiatlux.bin | ||||
|       - firmware/otaflash.py | ||||
|       - pcb/pcb.zip | ||||
|     checksum: | ||||
|       - sha512 | ||||
|  |  | |||
|  | @ -27,7 +27,6 @@ git submodule update --init --recursive | |||
|  - ncurses-dev libexpat-dev | ||||
|  - python3 python3-serial python-dev | ||||
|     | ||||
|  - pip install websocket-client (for otaflash.py, optional) | ||||
| 
 | ||||
| ### Build Steps | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										8
									
								
								firmware/.idea/firmware.iml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								firmware/.idea/firmware.iml
									
										
									
										generated
									
									
									
								
							|  | @ -1,8 +1,2 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module version="4"> | ||||
|   <component name="FacetManager"> | ||||
|     <facet type="Python" name="Python facet"> | ||||
|       <configuration sdkName="Python 3.9" /> | ||||
|     </facet> | ||||
|   </component> | ||||
| </module> | ||||
| <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" /> | ||||
							
								
								
									
										8
									
								
								firmware/.idea/modules.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								firmware/.idea/modules.xml
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | |||
| <?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/rboot-ota extras/mbedtls extras/httpd extras/sntp extras/cpp_support | ||||
| EXTRA_COMPONENTS=extras/i2s_dma extras/ws2812_i2s extras/dhcpserver extras/mbedtls extras/httpd extras/sntp extras/cpp_support | ||||
| 
 | ||||
| LIBS = hal m | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										261
									
								
								firmware/app.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								firmware/app.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,261 @@ | |||
| /* Example SPI transfert
 | ||||
|  * | ||||
|  * This sample code is in the public domain. | ||||
|  */ | ||||
| #include "espressif/esp_common.h" | ||||
| #include "esp/uart.h" | ||||
| #include "FreeRTOS.h" | ||||
| #include "task.h" | ||||
| #include "esp8266.h" | ||||
| #include <stdio.h> | ||||
| #include "esp/spi.h" | ||||
| #include "math.h" | ||||
| #include <time.h> | ||||
| 
 | ||||
| #define DAYTIME(h,m,s) (h*3600+m*60+s) | ||||
| 
 | ||||
| struct { | ||||
| 	time_t sunrise_start = DAYTIME(5,30,0); | ||||
| 	time_t sunrise_end = DAYTIME(6,0,0); | ||||
| 	time_t sunrise_shutdown = DAYTIME(7,0,0); | ||||
| 	time_t sunset_time = DAYTIME(22,0,0); | ||||
| } settings; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| struct { | ||||
| 	uint8_t r = 255; | ||||
| 	uint8_t g = 160; | ||||
| 	uint8_t b = 80; | ||||
| } white; | ||||
| 
 | ||||
| time_t day_seconds() { | ||||
|     time_t t1, t2; | ||||
|     struct tm tms; | ||||
|     time(&t1); | ||||
|     localtime_r(&t1, &tms); | ||||
|     tms.tm_hour = 0; | ||||
|     tms.tm_min = 0; | ||||
|     tms.tm_sec = 0; | ||||
|     t2 = mktime(&tms); | ||||
|     return t1 - t2; | ||||
| } | ||||
|   | ||||
| struct led_t{ | ||||
| 	struct { | ||||
| 		unsigned int mod : 5, marker : 3; | ||||
| 	} __attribute__((packed)) global = {0x1F, 0x7}; | ||||
| 	uint8_t b = 0; | ||||
| 	uint8_t g = 0; | ||||
| 	uint8_t r = 0; | ||||
| }; | ||||
| 
 | ||||
| led_t leds[4][10]; | ||||
| 
 | ||||
| float square(float x){ | ||||
| 	return x*x; | ||||
| } | ||||
| 
 | ||||
| float lerp(float a, float b, float x){ | ||||
| 	return (1.-x)*a + x *b; | ||||
| } | ||||
| 
 | ||||
| float clamp(float x, float a, float b){ | ||||
| 	if(x<a) | ||||
| 		return a; | ||||
| 	if(x>b) | ||||
| 		return b; | ||||
| 	return x; | ||||
| } | ||||
| 
 | ||||
| float fade(float x, float offset, float factor){ | ||||
| 	float val = (x*factor-offset); | ||||
| 	return lerp(square(clamp(val,0,1)),square(1-clamp(1-val,0,1)),val); | ||||
| } | ||||
| 
 | ||||
| void write_leds(){ | ||||
|         spi_transfer_32(1, 0x00000000); | ||||
|         for(int i = 0; i < 10; i++) | ||||
| 			spi_transfer_32(1, *(uint32_t*)&leds[0][i]); | ||||
|         for(int i = 0; i < 10; i++) | ||||
| 			spi_transfer_32(1, *(uint32_t*)&leds[1][9-i]); | ||||
|         for(int i = 0; i < 10; i++) | ||||
| 			spi_transfer_32(1, *(uint32_t*)&leds[2][i]); | ||||
|         for(int i = 0; i < 10; i++) | ||||
| 			spi_transfer_32(1, *(uint32_t*)&leds[3][9-i]); | ||||
|         spi_transfer_32(1, 0xFFFFFFFF); | ||||
|         spi_transfer_32(1, 0xFFFFFFFF); | ||||
| } | ||||
| 
 | ||||
| enum state_t{ BOOT_S, SUNRISE_S, MORNING_S, DAY_S, NIGHT_S }; | ||||
| 
 | ||||
| state_t s = BOOT_S; | ||||
| 
 | ||||
| bool maual_mode = false; | ||||
| 
 | ||||
| extern "C" void manual_switch(){ | ||||
| 	 | ||||
| 	maual_mode = !maual_mode; | ||||
| 	 | ||||
| 	printf("%d\n", (int)maual_mode); | ||||
| 	 | ||||
| 	if(maual_mode){ | ||||
| 			 | ||||
| 		if(s == NIGHT_S){ | ||||
| 				 | ||||
| 			for(int j = 0; j < 4; j++) | ||||
| 				for(int i = 0; i < 8; i+=2){ | ||||
| 					leds[j][i].global.mod = 2; | ||||
| 					leds[j][i].r = 128; | ||||
| 					leds[j][i].g = 30; | ||||
| 					leds[j][i].b = 15; | ||||
| 				} | ||||
| 		}else{ | ||||
| 		 | ||||
| 			for(int j = 0; j < 4; j++) | ||||
| 				for(int i = 0; i < 10; i++){ | ||||
| 					leds[j][i].global.mod = 31; | ||||
| 					leds[j][i].r = white.r; | ||||
| 					leds[j][i].g = white.g; | ||||
| 					leds[j][i].b = white.b; | ||||
| 				} | ||||
| 		} | ||||
| 		 | ||||
| 		write_leds(); | ||||
| 		 | ||||
| 	}else{ | ||||
| 		for(int j = 0; j < 4; j++) | ||||
| 			for(int i = 0; i < 10; i++){ | ||||
| 				leds[j][i].global.mod = 0; | ||||
| 				leds[j][i].r = 0; | ||||
| 				leds[j][i].g = 0; | ||||
| 				leds[j][i].b = 0; | ||||
| 			} | ||||
| 		write_leds(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void loop(void *pvParameters) | ||||
| { | ||||
| 	spi_init(1, SPI_MODE0, SPI_FREQ_DIV_1M, 1, SPI_LITTLE_ENDIAN, false); | ||||
| 	 | ||||
| 	while(1){ | ||||
| 		 | ||||
| 		time_t rt = day_seconds(); | ||||
| 		 | ||||
| 		if(maual_mode){ | ||||
| 			 | ||||
| 			if(s == NIGHT_S){ | ||||
| 				 | ||||
| 				for(int j = 0; j < 4; j++) | ||||
| 					for(int i = 0; i < 8; i+=2){ | ||||
| 						leds[j][i].global.mod = 2; | ||||
| 						leds[j][i].r = 128; | ||||
| 						leds[j][i].g = 30; | ||||
| 						leds[j][i].b = 15; | ||||
| 					} | ||||
| 			}else{ | ||||
| 			 | ||||
| 				for(int j = 0; j < 4; j++) | ||||
| 					for(int i = 0; i < 10; i++){ | ||||
| 						leds[j][i].global.mod = 31; | ||||
| 						leds[j][i].r = white.r; | ||||
| 						leds[j][i].g = white.g; | ||||
| 						leds[j][i].b = white.b; | ||||
| 					} | ||||
| 			} | ||||
| 				 | ||||
| 			write_leds(); | ||||
| 			 | ||||
| 		}else{ | ||||
| 			 | ||||
| 			if( s == BOOT_S ) { | ||||
| 				if( rt >= settings.sunrise_start ) { | ||||
| 					s = SUNRISE_S; | ||||
| 					printf("SUNRISE_S\n"); | ||||
| 				} | ||||
| 				if( rt >= settings.sunrise_end ) { | ||||
| 					s = MORNING_S; | ||||
| 					printf("MORNING_S\n"); | ||||
| 				} | ||||
| 			} else if( s == SUNRISE_S ){ | ||||
| 				if( rt >= settings.sunrise_end ) { | ||||
| 					s = MORNING_S; | ||||
| 					printf("MORNING_S\n"); | ||||
| 				}else{ | ||||
| 					int steps = (settings.sunrise_end - settings.sunrise_start)*(1000/50); | ||||
| 					int t = (rt - settings.sunrise_start)*(1000/50); | ||||
| 					for(; t < steps && !maual_mode; t++) { | ||||
| 						 | ||||
| 						for(int j = 0; j < 4; j++) | ||||
| 							for(int i = 0; i < 10; i++){ | ||||
| 								float val = (-i*30.+t*3.*(4./5.))/(float)steps; | ||||
| 								leds[j][i].global.mod = 8 + fade(val,0.,0.5)*23; | ||||
| 								leds[j][i].r = fade(val,0.01,1.) * white.r; | ||||
| 								leds[j][i].g = fade(val,0.1,0.5) * white.g; | ||||
| 								leds[j][i].b = fade(val,0.6,0.7) * white.b; | ||||
| 							} | ||||
| 						 | ||||
| 						write_leds(); | ||||
| 						 | ||||
| 						if ((t%25)==0) { | ||||
| 							 | ||||
| 							printf("Time: %d%% %d %d/%d\n", (t*100)/steps, (int)day_seconds(), t, steps); | ||||
| 							printf("%d %d %d %d\n", | ||||
| 								leds[0][0].global.mod, | ||||
| 								leds[0][0].r, | ||||
| 								leds[0][0].g, | ||||
| 								leds[0][0].b); | ||||
| 						} | ||||
| 						 | ||||
| 						vTaskDelay(50/portTICK_PERIOD_MS); | ||||
| 					} | ||||
| 				} | ||||
| 			} else if( s == MORNING_S ) { | ||||
| 				if( rt >= settings.sunrise_shutdown ) { | ||||
| 					s = DAY_S; | ||||
| 					printf("DAY_S\n"); | ||||
| 				}else{ | ||||
| 					for(int j = 0; j < 4; j++) | ||||
| 						for(int i = 0; i < 10; i++){ | ||||
| 							leds[j][i].global.mod = 31; | ||||
| 							leds[j][i].r = white.r; | ||||
| 							leds[j][i].g = white.g; | ||||
| 							leds[j][i].b = white.b; | ||||
| 						} | ||||
| 					write_leds(); | ||||
| 				} | ||||
| 			} else if( s == DAY_S ) { | ||||
| 				if( rt >= settings.sunset_time ) { | ||||
| 					s = NIGHT_S; | ||||
| 					printf("NIGHT_S\n"); | ||||
| 				}else{ | ||||
| 					for(int j = 0; j < 4; j++) | ||||
| 						for(int i = 0; i < 10; i++){ | ||||
| 							leds[j][i].global.mod = 0; | ||||
| 							leds[j][i].r = 0; | ||||
| 							leds[j][i].g = 0; | ||||
| 							leds[j][i].b = 0; | ||||
| 						} | ||||
| 					 | ||||
| 					write_leds(); | ||||
| 				} | ||||
| 			} else if( s == NIGHT_S ) { | ||||
| 				if( rt >= settings.sunrise_start && rt < settings.sunset_time) { | ||||
| 					s = SUNRISE_S; | ||||
| 					printf("SUNRISE_S\n"); | ||||
| 				} | ||||
| 			} | ||||
| 			 | ||||
| 		} | ||||
| 		 | ||||
| 		vTaskDelay(1000/portTICK_PERIOD_MS); | ||||
| 		 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| extern "C" void register_app(void) | ||||
| { | ||||
|     xTaskCreate(loop, "loop", 1024, NULL, 2, NULL); | ||||
| } | ||||
							
								
								
									
										7
									
								
								firmware/config.sample
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								firmware/config.sample
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| sunrise_start: time | ||||
| sunrise_end: time | ||||
| sunrise_shutdown: time | ||||
| sunrise_color: color | ||||
| sunrise_fade: linear, ease, ease_in, ease_out | ||||
| sunset_time: time | ||||
| night_color: color | ||||
|  | @ -1,15 +1,317 @@ | |||
| #include <espressif/esp_common.h> | ||||
| #include <esp8266.h> | ||||
| #include <esp/uart.h> | ||||
| #include <string.h> | ||||
| #include <stdio.h> | ||||
| #include <FreeRTOS.h> | ||||
| #include <task.h> | ||||
| #include <ssid_config.h> | ||||
| #include <httpd/httpd.h> | ||||
| #include <sys/time.h> | ||||
| 
 | ||||
| #include <lwip/err.h> | ||||
| #include <lwip/sockets.h> | ||||
| #include <lwip/sys.h> | ||||
| #include <lwip/netdb.h> | ||||
| #include <lwip/dns.h> | ||||
| 
 | ||||
| #include "system.h" | ||||
| #include "wifi.h" | ||||
| #include "web.h" | ||||
| #include "mqtt.h" | ||||
| #include "lux.h" | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <FreeRTOS.h> | ||||
| #include <task.h> | ||||
| #define LED_PIN 2 | ||||
| #define SWITCH_PIN 2 | ||||
| 
 | ||||
| #include <espressif/esp_common.h> | ||||
| #include <esp/uart.h> | ||||
| /* Add extras/sntp component to makefile for this include to work */ | ||||
| #include <sntp.h> | ||||
| #include <time.h> | ||||
| 
 | ||||
| #define SNTP_SERVERS 	"0.pool.ntp.org", "1.pool.ntp.org", \ | ||||
| 						"2.pool.ntp.org", "3.pool.ntp.org" | ||||
| 
 | ||||
| #define vTaskDelayMs(ms)	vTaskDelay((ms)/portTICK_PERIOD_MS) | ||||
| #define UNUSED_ARG(x)	(void)x | ||||
| 
 | ||||
| 
 | ||||
| const gpio_inttype_t int_type = GPIO_INTTYPE_EDGE_NEG; | ||||
| 
 | ||||
| enum { | ||||
|     SSI_WALLTIME, | ||||
|     SSI_UPTIME, | ||||
|     SSI_FREE_HEAP, | ||||
|     SSI_LED_STATE | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| int32_t ssi_handler(int32_t iIndex, char *pcInsert, int32_t iInsertLen) | ||||
| { | ||||
| 	struct timeval tv; | ||||
| 			 | ||||
|     switch (iIndex) { | ||||
|         case SSI_WALLTIME: | ||||
| 			gettimeofday(&tv, NULL); | ||||
|             snprintf(pcInsert, iInsertLen, "%d", | ||||
|                     (int)tv.tv_sec); | ||||
|             break; | ||||
|         case SSI_UPTIME: | ||||
|             snprintf(pcInsert, iInsertLen, "%d", | ||||
|                     xTaskGetTickCount() * portTICK_PERIOD_MS / 1000); | ||||
|             break; | ||||
|         case SSI_FREE_HEAP: | ||||
|             snprintf(pcInsert, iInsertLen, "%d", (int) xPortGetFreeHeapSize()); | ||||
|             break; | ||||
|         case SSI_LED_STATE: | ||||
|             snprintf(pcInsert, iInsertLen, (GPIO.OUT & BIT(LED_PIN)) ? "Off" : "On"); | ||||
|             break; | ||||
|         default: | ||||
|             snprintf(pcInsert, iInsertLen, "N/A"); | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     /* Tell the server how many characters to insert */ | ||||
|     return (strlen(pcInsert)); | ||||
| } | ||||
| 
 | ||||
| const char *gpio_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) | ||||
| { | ||||
|     for (int i = 0; i < iNumParams; i++) { | ||||
|         if (strcmp(pcParam[i], "on") == 0) { | ||||
|             uint8_t gpio_num = atoi(pcValue[i]); | ||||
|             gpio_enable(gpio_num, GPIO_OUTPUT); | ||||
|             gpio_write(gpio_num, true); | ||||
|         } else if (strcmp(pcParam[i], "off") == 0) { | ||||
|             uint8_t gpio_num = atoi(pcValue[i]); | ||||
|             gpio_enable(gpio_num, GPIO_OUTPUT); | ||||
|             gpio_write(gpio_num, false); | ||||
|         } else if (strcmp(pcParam[i], "toggle") == 0) { | ||||
|             uint8_t gpio_num = atoi(pcValue[i]); | ||||
|             gpio_enable(gpio_num, GPIO_OUTPUT); | ||||
|             gpio_toggle(gpio_num); | ||||
|         } | ||||
|     } | ||||
|     return "/index.ssi"; | ||||
| } | ||||
| 
 | ||||
| const char *about_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) | ||||
| { | ||||
|     return "/about.html"; | ||||
| } | ||||
| 
 | ||||
| const char *websocket_cgi_handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) | ||||
| { | ||||
|     return "/websockets.html"; | ||||
| } | ||||
| 
 | ||||
| void websocket_task(void *pvParameter) | ||||
| { | ||||
|     struct tcp_pcb *pcb = (struct tcp_pcb *) pvParameter; | ||||
| 
 | ||||
|     for (;;) { | ||||
|         if (pcb == NULL || pcb->state != ESTABLISHED) { | ||||
|             printf("Connection closed, deleting task\n"); | ||||
|             break; | ||||
|         } | ||||
|          | ||||
| 		struct timeval tv; | ||||
| 		gettimeofday(&tv, NULL); | ||||
| 
 | ||||
|         int uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; | ||||
|         int heap = (int) xPortGetFreeHeapSize(); | ||||
|         int led = !gpio_read(LED_PIN); | ||||
| 
 | ||||
|         /* Generate response in JSON format */ | ||||
|         char response[64]; | ||||
|         int len = snprintf(response, sizeof (response), | ||||
|                 "{\"walltime\" : \"%d\"," | ||||
|                 "\"uptime\" : \"%d\"," | ||||
|                 " \"heap\" : \"%d\"," | ||||
|                 " \"led\" : \"%d\"}", (int)tv.tv_sec, uptime, heap, led); | ||||
|         if (len < sizeof (response)) | ||||
|             websocket_write(pcb, (unsigned char *) response, len, WS_TEXT_MODE); | ||||
| 
 | ||||
|         vTaskDelay(2000 / portTICK_PERIOD_MS); | ||||
|     } | ||||
| 
 | ||||
|     vTaskDelete(NULL); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * 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, uint8_t *data, u16_t data_len, uint8_t mode) | ||||
| { | ||||
|     printf("[websocket_callback]:\n%.*s\n", (int) data_len, (char*) data); | ||||
| 
 | ||||
|     uint8_t response[2]; | ||||
|     uint16_t val; | ||||
| 
 | ||||
|     switch (data[0]) { | ||||
|         case 'A': // ADC
 | ||||
|             /* This should be done on a separate thread in 'real' applications */ | ||||
|             val = sdk_system_adc_read(); | ||||
|             break; | ||||
|         case 'D': // Disable LED
 | ||||
|             gpio_write(LED_PIN, true); | ||||
|             val = 0xDEAD; | ||||
|             break; | ||||
|         case 'E': // Enable LED
 | ||||
|             gpio_write(LED_PIN, false); | ||||
|             val = 0xBEEF; | ||||
|             break; | ||||
|         default: | ||||
|             printf("Unknown command\n"); | ||||
|             val = 0; | ||||
|             break; | ||||
|     } | ||||
| 
 | ||||
|     response[1] = (uint8_t) val; | ||||
|     response[0] = val >> 8; | ||||
| 
 | ||||
|     websocket_write(pcb, response, 2, WS_BIN_MODE); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * This function is called when new websocket is open and | ||||
|  * creates a new websocket_task if requested URI equals '/stream'. | ||||
|  */ | ||||
| void websocket_open_cb(struct tcp_pcb *pcb, const char *uri) | ||||
| { | ||||
|     printf("WS URI: %s\n", uri); | ||||
|     if (!strcmp(uri, "/stream")) { | ||||
|         printf("request for streaming\n"); | ||||
|         xTaskCreate(&websocket_task, "websocket_task", 256, (void *) pcb, 2, NULL); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void httpd_task(void *pvParameters) | ||||
| { | ||||
|     tCGI pCGIs[] = { | ||||
|         {"/gpio", (tCGIHandler) gpio_cgi_handler}, | ||||
|         {"/about", (tCGIHandler) about_cgi_handler}, | ||||
|         {"/websockets", (tCGIHandler) websocket_cgi_handler}, | ||||
|     }; | ||||
| 
 | ||||
|     const char *pcConfigSSITags[] = { | ||||
|         "walltime", // SSI_WALLTIME
 | ||||
|         "uptime", // SSI_UPTIME
 | ||||
|         "heap",   // SSI_FREE_HEAP
 | ||||
|         "led"     // SSI_LED_STATE
 | ||||
|     }; | ||||
| 
 | ||||
|     /* register handlers and start the server */ | ||||
|     http_set_cgi_handlers(pCGIs, sizeof (pCGIs) / sizeof (pCGIs[0])); | ||||
|     http_set_ssi_handler((tSSIHandler) ssi_handler, pcConfigSSITags, | ||||
|             sizeof (pcConfigSSITags) / sizeof (pcConfigSSITags[0])); | ||||
|     websocket_register_callbacks((tWsOpenHandler) websocket_open_cb, | ||||
|             (tWsHandler) websocket_cb); | ||||
|     httpd_init(); | ||||
| 
 | ||||
|     for (;;); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void sntp_tsk(void *pvParameters) | ||||
| { | ||||
| 	const char *servers[] = {SNTP_SERVERS}; | ||||
| 	UNUSED_ARG(pvParameters); | ||||
| 
 | ||||
| 	/* Wait until we have joined AP and are assigned an IP */ | ||||
| 	while (sdk_wifi_station_get_connect_status() != STATION_GOT_IP) { | ||||
| 		vTaskDelayMs(100); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Start SNTP */ | ||||
| 	printf("Starting SNTP... "); | ||||
| 	/* SNTP will request an update each 5 minutes */ | ||||
| 	sntp_set_update_delay(5*60000); | ||||
| 	/* Set GMT+1 zone, daylight savings off */ | ||||
| 	const struct timezone tz = {1*60, 1}; | ||||
| 	/* SNTP initialization */ | ||||
| 	sntp_initialize(&tz); | ||||
| 	/* Servers must be configured right after initialization */ | ||||
| 	sntp_set_servers(servers, sizeof(servers) / sizeof(char*)); | ||||
| 	printf("DONE!\n"); | ||||
| 
 | ||||
| 	/* Print date and time each 5 seconds */ | ||||
| 	while(1) { | ||||
| 		vTaskDelayMs(5000); | ||||
| 		//time_t ts = time(NULL);
 | ||||
| 		//int t = ts;
 | ||||
| 		//printf("TIME: %d %d %s", t,(int) day_seconds(), ctime(&ts));
 | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void gpio_intr_handler(uint8_t gpio_num); | ||||
| 
 | ||||
| void manual_switch(void); | ||||
| 
 | ||||
| void buttonIntTask(void *pvParameters) | ||||
| { | ||||
|     printf("Waiting for button press interrupt on gpio %d...\r\n", SWITCH_PIN); | ||||
|     QueueHandle_t *tsqueue = (QueueHandle_t *)pvParameters; | ||||
|     gpio_set_interrupt(SWITCH_PIN, int_type, gpio_intr_handler); | ||||
| 
 | ||||
|     uint32_t last = 0; | ||||
|     while(1) { | ||||
|         uint32_t button_ts; | ||||
|         xQueueReceive(*tsqueue, &button_ts, portMAX_DELAY); | ||||
|         button_ts *= portTICK_PERIOD_MS; | ||||
|         if(last < button_ts-200) { | ||||
| 			manual_switch(); | ||||
|             //printf("Button interrupt fired at %dms\r\n", button_ts);
 | ||||
|             last = button_ts; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static QueueHandle_t tsqueue; | ||||
| 
 | ||||
| void gpio_intr_handler(uint8_t gpio_num) | ||||
| { | ||||
|     uint32_t now = xTaskGetTickCountFromISR(); | ||||
|     xQueueSendToBackFromISR(tsqueue, &now, NULL); | ||||
| } | ||||
| 
 | ||||
| void register_app(void); | ||||
| 
 | ||||
| void user_init(void) | ||||
| { | ||||
|     uart_set_baud(0, 115200); | ||||
|     printf("SDK version:%s\n", sdk_system_get_sdk_version()); | ||||
| 
 | ||||
|     struct sdk_station_config config = { | ||||
|         .ssid = WIFI_SSID, | ||||
|         .password = WIFI_PASS, | ||||
|     }; | ||||
| 
 | ||||
|     /* required to call wifi_set_opmode before station_set_config */ | ||||
|     sdk_wifi_set_opmode(STATION_MODE); | ||||
|     sdk_wifi_station_set_config(&config); | ||||
|     //netif_set_hostname(netif_default, "nachtlicht");
 | ||||
|     sdk_wifi_station_connect(); | ||||
| 
 | ||||
|     /* turn off LED */ | ||||
|     gpio_enable(LED_PIN, GPIO_OUTPUT); | ||||
|     gpio_write(LED_PIN, true); | ||||
|      | ||||
|      | ||||
|     gpio_enable(SWITCH_PIN, GPIO_INPUT); | ||||
|      | ||||
|     tsqueue = xQueueCreate(2, sizeof(uint32_t)); | ||||
|     //xTaskCreate(buttonIntTask, "buttonIntTask", 256, &tsqueue, 2, NULL);
 | ||||
| 
 | ||||
|     /* initialize tasks */ | ||||
|     xTaskCreate(&httpd_task, "HTTP Daemon", 2048, NULL, 2, NULL); | ||||
|      | ||||
|     xTaskCreate(&sntp_tsk, "SNTP", 512, NULL, 1, NULL); | ||||
|     register_app(); | ||||
| } | ||||
|   | ||||
| void user_init(void) | ||||
| { | ||||
|  | @ -22,9 +324,9 @@ void user_init(void) | |||
| 
 | ||||
|     wifi_available_semaphore = xSemaphoreCreateBinary(); | ||||
| 
 | ||||
|     xTaskCreate(wifi_task, "wifi_task", 1024, NULL, 1, NULL); | ||||
|     xTaskCreate(wifi_task, "wifi_task", 512, NULL, 2, NULL); | ||||
| 
 | ||||
|     xTaskCreate(&httpd_task, "httpd_task", 1024, NULL, 2, NULL); | ||||
|     xTaskCreate(&httpd_task, "httpd_task", 512, NULL, 2, NULL); | ||||
| 
 | ||||
|     xTaskCreate(&lux_task, "lux_task", 512, NULL, 1, NULL); | ||||
| } | ||||
|  |  | |||
|  | @ -16,48 +16,9 @@ | |||
|     <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"> | ||||
|  | @ -172,18 +133,19 @@ | |||
| <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); | ||||
|  | @ -213,9 +175,6 @@ | |||
|         chart.streamTo(canvas, 500); | ||||
|     } | ||||
| 
 | ||||
|     var receive_chunk_confirmation = () => { | ||||
|     }; | ||||
| 
 | ||||
|     function onMessage(evt) { | ||||
|         retries = 0; | ||||
|         if (typeof evt.data == 'string') { | ||||
|  | @ -237,9 +196,7 @@ | |||
| 
 | ||||
|             if (cmd === 'G') | ||||
|                 console.log("LED switched", val); | ||||
|             else if (cmd === 'F') { | ||||
|                 receive_chunk_confirmation(dv); | ||||
|             } else if (cmd === 'V') { | ||||
|             else if (cmd === 'V') { | ||||
|                 voltage.innerHTML = (val * 13 / 1024).toFixed(2); | ||||
|                 series.append(new Date().getTime(), val); | ||||
|             } else | ||||
|  | @ -276,7 +233,7 @@ | |||
|     } | ||||
| 
 | ||||
|     function wsWrite(data) { | ||||
|         //console.info(buf2hex(data)); | ||||
|         console.info(buf2hex(data)); | ||||
|         if (ws.readyState === 3 || retries++ > 5) | ||||
|             wsOpen(); | ||||
|         else if (ws.readyState === 1) | ||||
|  | @ -295,105 +252,6 @@ | |||
|         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> | ||||
|  |  | |||
|  | @ -1,44 +0,0 @@ | |||
| #!/usr/bin/env python3 | ||||
| 
 | ||||
| import time | ||||
| import websocket | ||||
| import argparse | ||||
| import zlib | ||||
| 
 | ||||
| parser = argparse.ArgumentParser(description='Update fiatlux firmware via websocket.') | ||||
| parser.add_argument("binfile") | ||||
| 
 | ||||
| args = parser.parse_args() | ||||
| 
 | ||||
| with open(args.binfile, "rb") as f: | ||||
|     try: | ||||
|         ws = websocket.WebSocket() | ||||
|         ws.connect("ws://172.16.0.1") | ||||
|         i = 0 | ||||
|         rolling = 0 | ||||
|         total = 0 | ||||
|         while True: | ||||
|             bytes = f.read(512) | ||||
|             rolling = zlib.crc32(bytes, rolling) | ||||
|             total += len(bytes) | ||||
|             msg = b'F\x00\x00\x00' | ||||
|             msg += i.to_bytes(2, 'big') | ||||
|             msg += len(bytes).to_bytes(2, 'big') | ||||
|             msg += (zlib.crc32(bytes) & 0xffffffff).to_bytes(4, 'big') | ||||
|             msg += bytes | ||||
|             ws.send(msg) | ||||
|             reply = ws.recv() | ||||
|             time.sleep(0.05) | ||||
|             i += 1 | ||||
|             if len(bytes) != 512: | ||||
|                 break | ||||
|         msg = b'C\x00\x00\x00' | ||||
|         msg += total.to_bytes(4, 'big') | ||||
|         msg += rolling.to_bytes(4, 'big') | ||||
|         ws.send(msg) | ||||
|         print(ws.recv()) | ||||
|         ws.close() | ||||
|     except ConnectionResetError: | ||||
|         pass | ||||
|     except KeyboardInterrupt: | ||||
|         pass | ||||
|  | @ -3,114 +3,35 @@ | |||
| //
 | ||||
| 
 | ||||
| #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> | ||||
| 
 | ||||
| #define min(a, b) \ | ||||
|  ({ __typeof__ (a) _a = (a); \ | ||||
|  __typeof__ (b) _b = (b); \ | ||||
|  _a < _b ? _a : _b; }) | ||||
| 
 | ||||
| void system_clear_config() { | ||||
| void system_clear_config(){ | ||||
|     vPortEnterCritical(); | ||||
|     uint32_t num_sectors = 0x2000 / sdk_flashchip.sector_size; | ||||
|     uint32_t start = 0x00100000; | ||||
|     uint32_t num_sectors = 5 + DEFAULT_SYSPARAM_SECTORS; | ||||
|     uint32_t start = sdk_flashchip.chip_size - num_sectors * sdk_flashchip.sector_size; | ||||
|     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 = 0x00100000; | ||||
| void system_init_config(){ | ||||
|     uint32_t base_addr; | ||||
|     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 = 0x2000 / sdk_flashchip.sector_size; | ||||
|         num_sectors = DEFAULT_SYSPARAM_SECTORS; | ||||
|         base_addr = sdk_flashchip.chip_size - (5 + num_sectors) * 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,22 +5,13 @@ | |||
| #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 == nullptr || pcb->state != ESTABLISHED) { | ||||
|         if(pcb == NULL || 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, nullptr); | ||||
|             size_t uptime = xTaskGetTickCount() * portTICK_PERIOD_MS / 1000; | ||||
|             timeval tv; | ||||
|             gettimeofday(&tv, NULL); | ||||
|             int 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 = nullptr; | ||||
|             char *hostname = NULL; | ||||
| 
 | ||||
|             sysparam_get_string("hostname", &hostname); | ||||
|             /* Generate response in JSON format */ | ||||
|  | @ -80,16 +80,21 @@ void websocket_task(void *pvParameter) { | |||
|         //Connection Info
 | ||||
|         if(has_changed.connection) { | ||||
|             has_changed.connection = false; | ||||
|             timeval tv{}; | ||||
|             gettimeofday(&tv, nullptr); | ||||
|             size_t connuptime = (xTaskGetTickCount() - connstarttime) * portTICK_PERIOD_MS / 1000; | ||||
|             timeval tv; | ||||
|             gettimeofday(&tv, NULL); | ||||
|             int 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(); | ||||
|  | @ -132,10 +137,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 = nullptr; | ||||
|                 char *apssid = NULL; | ||||
|                 sysparam_get_string("wifi_ap_ssid", &apssid); | ||||
| 
 | ||||
|                 /* Generate response in JSON format */ | ||||
|  | @ -143,8 +148,12 @@ 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)) { | ||||
|  | @ -160,7 +169,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); | ||||
|  | @ -170,8 +179,12 @@ 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)) { | ||||
|  | @ -185,49 +198,34 @@ 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(nullptr); | ||||
|     vTaskDelete(NULL); | ||||
| } | ||||
| 
 | ||||
| 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*/) { //mode should be WS_BIN_MODE or WS_TEXT_MODE
 | ||||
| void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, uint8_t 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; | ||||
|  | @ -238,29 +236,9 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, | |||
|             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 %c\n", data[0]); | ||||
|             printf("Unknown command\n"); | ||||
|             val = 0; | ||||
|             break; | ||||
|     } | ||||
|  | @ -269,18 +247,7 @@ void websocket_cb(struct tcp_pcb *pcb, char *data, u16_t data_len, | |||
|     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(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -295,10 +262,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