From a806910034f336c86880241846df807a95ebfc71 Mon Sep 17 00:00:00 2001 From: jedi Date: Sun, 8 Aug 2021 22:59:15 +0200 Subject: [PATCH] stash --- firmware/app.cpp | 261 ++++++++++++++++++++++++++++++++++ firmware/config.sample | 7 + firmware/fiatlux.c | 312 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 575 insertions(+), 5 deletions(-) create mode 100644 firmware/app.cpp create mode 100644 firmware/config.sample diff --git a/firmware/app.cpp b/firmware/app.cpp new file mode 100644 index 0000000..f7b2414 --- /dev/null +++ b/firmware/app.cpp @@ -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 +#include "esp/spi.h" +#include "math.h" +#include + +#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(xb) + 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); +} diff --git a/firmware/config.sample b/firmware/config.sample new file mode 100644 index 0000000..2673cac --- /dev/null +++ b/firmware/config.sample @@ -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 diff --git a/firmware/fiatlux.c b/firmware/fiatlux.c index 98d0efb..25573a6 100644 --- a/firmware/fiatlux.c +++ b/firmware/fiatlux.c @@ -1,16 +1,318 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + #include "system.h" #include "wifi.h" #include "web.h" #include "mqtt.h" #include "lux.h" -#include -#include -#include +#define LED_PIN 2 +#define SWITCH_PIN 2 -#include -#include +/* Add extras/sntp component to makefile for this include to work */ +#include +#include +#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) { uart_set_baud(0, 115200);