main.c (15945B)
1 /* 2 Copyright © 2021 Gerd Beuster <gerd@frombelow.net> 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 16 17 */ 18 19 /* {{{ Includes and definitions */ 20 21 #include "main.h" 22 #include "debug.h" 23 #include "nvs_flash.h" 24 #include "led.h" 25 #include <string.h> 26 27 #define delay(ms) (vTaskDelay(ms/portTICK_RATE_MS)) 28 29 30 /* }}} */ 31 32 /* {{{ Processing of HTML pages */ 33 34 /* Page templates */ 35 36 const static char http_html_hdr[] = 37 "HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n"; 38 39 const static char http_index_template_html[] = 40 #include "html_templates/index.html" 41 42 const static char http_config_template_html[] = 43 #include "html_templates/config.html" 44 45 const static char http_reset_html[] = 46 #include "html_templates/reset.html" 47 48 /* Request handlers */ 49 50 static void handler_index_html(struct netconn *conn, char *base_url_begin, 51 char *base_url_end, char *parameters){ 52 /* Send the HTML header 53 * subtract 1 from the size, since we don't send the \0 in the string 54 * NETCONN_NOCOPY: our data is const static, so no need to copy it 55 */ 56 netconn_write(conn, http_html_hdr, sizeof(http_html_hdr)-1, NETCONN_NOCOPY); 57 // Process request (update state) 58 char *val_start, *val_end; 59 if(url_key_value(parameters, "mode", &val_start, &val_end)){ 60 uint8_t val_length = val_end-val_start; 61 xSemaphoreTake(blinkenstrip_state_changed_semaphore, portMAX_DELAY); 62 blinkenstrip_state_has_changed = true; 63 if(strncmp("sequence", val_start, val_length) == 0) 64 blinkenstrip_state = BLINKENSTRIP_STATE_UNICOLOR_RAINBOW; 65 else if(strncmp("blink", val_start, val_length) == 0) 66 blinkenstrip_state = BLINKENSTRIP_STATE_RANDOM_ON_OFF; 67 else if(strncmp("running", val_start, val_length) == 0) 68 blinkenstrip_state = BLINKENSTRIP_STATE_RUNNING_DOT; 69 else if(strncmp("cellular", val_start, val_length) == 0) 70 blinkenstrip_state = BLINKENSTRIP_STATE_CELLULAR_AUTOMATON; 71 else if(strncmp("lines", val_start, val_length) == 0) 72 blinkenstrip_state = BLINKENSTRIP_STATE_LINE_BY_LINE; 73 else if(strncmp("worm", val_start, val_length) == 0) 74 blinkenstrip_state = BLINKENSTRIP_STATE_WORM; 75 else if(strncmp("rgb", val_start, val_length) == 0){ 76 blinkenstrip_state = BLINKENSTRIP_STATE_UNICOLOR_RGB; 77 if(url_key_value(parameters, "red", &val_start, &val_end)){ 78 *val_end ='\0'; 79 led_color_rgb.red = max_brightness < atol(val_start) ? 80 max_brightness : atol(val_start); 81 *val_end ='&'; 82 } 83 if(url_key_value(parameters, "green", &val_start, &val_end)){ 84 *val_end ='\0'; 85 led_color_rgb.green = max_brightness < atol(val_start) ? 86 max_brightness : atol(val_start); 87 *val_end ='&'; 88 } 89 if(url_key_value(parameters, "blue", &val_start, &val_end)){ 90 *val_end ='\0'; 91 led_color_rgb.blue = max_brightness < atol(val_start) ? 92 max_brightness : atol(val_start); 93 *val_end ='&'; 94 } 95 } 96 else if(strncmp("experimental", val_start, val_length) == 0) 97 blinkenstrip_state = BLINKENSTRIP_STATE_EXPERIMENTAL; 98 xSemaphoreGive(blinkenstrip_state_changed_semaphore); 99 } 100 /* Return index page based on index_template_html. We have to fill 101 out the template. We have to set the maximal values for the RGB 102 sliders in the template. We allocating memory, we add 3 bytes to 103 the length of the buffer for this. (Numbers may have up to 3 104 digits, but two are already reserved because of the "%d"s in the 105 template). We also add space for the name of the app. */ 106 size_t http_index_html_len = (sizeof(http_index_template_html) + 107 sizeof(app_name)*3 + 3); 108 char *http_index_html = malloc(http_index_html_len); 109 snprintf(http_index_html, http_index_html_len, http_index_template_html, 110 app_name, app_name, 111 max_brightness, max_brightness, max_brightness); 112 netconn_write(conn, http_index_html, strlen(http_index_html)-1, 113 NETCONN_NOCOPY); 114 free(http_index_html); 115 } 116 117 static void handler_config_html(struct netconn *conn, char *base_url_begin, 118 char *base_url_end, char *parameters){ 119 /* Request for config.html */ 120 netconn_write(conn, http_html_hdr, sizeof(http_html_hdr)-1, NETCONN_NOCOPY); 121 /* Check if the user supplied values. If yes, we store the 122 values and reset. If not, we show config.html. */ 123 char *val_start, *val_end; 124 if(url_key_value(parameters, "app_name", &val_start, &val_end)){ 125 /* We got values. Store them and reset. */ 126 nvs_flash_init(); 127 nvs_handle flash_handle; 128 nvs_open("blinkenstrip", NVS_READWRITE, &flash_handle); 129 *(val_end) = '\0'; // Terminate string 130 nvs_set_str(flash_handle, "app_name", val_start); 131 *(val_end) = '&'; // Restore parameters 132 url_key_value(parameters, "essid", &val_start, &val_end); 133 *(val_end) = '\0'; // Terminate string 134 nvs_set_str(flash_handle, "wifi_essid", val_start); 135 *(val_end) = '&'; // Restore parameters 136 url_key_value(parameters, "password", &val_start, &val_end); 137 *(val_end) = '\0'; // Terminate string 138 if(strlen(val_start) >= 8) { 139 nvs_set_str(flash_handle, "wifi_password", val_start); 140 }; 141 *(val_end) = '&'; // Restore parameters 142 url_key_value(parameters, "ap", &val_start, &val_end); 143 *(val_end) = '\0'; // Terminate string 144 nvs_set_u8(flash_handle, "wifi_ap", !strcmp(val_start, "yes")); 145 *(val_end) = '&'; // Restore parameters 146 url_key_value(parameters, "led_strip_w", &val_start, &val_end); 147 *(val_end) = '\0'; // Terminate string 148 nvs_set_u16(flash_handle, "led_strip_w", atol(val_start)); 149 *(val_end) = '&'; // Restore parameters 150 url_key_value(parameters, "led_strip_h", &val_start, &val_end); 151 *(val_end) = '\0'; // Terminate string 152 nvs_set_u16(flash_handle, "led_strip_h", atol(val_start)); 153 *(val_end) = '&'; // Restore parameters 154 url_key_value(parameters, "snake", &val_start, &val_end); 155 *(val_end) = '\0'; // Terminate string 156 nvs_set_u8(flash_handle, "snake", !strcmp(val_start, "yes")); 157 *(val_end) = '&'; // Restore parameters 158 url_key_value(parameters, "wrap_around", &val_start, &val_end); 159 *(val_end) = '\0'; // Terminate string 160 nvs_set_u8(flash_handle, "wrap_around", !strcmp(val_start, "yes")); 161 *(val_end) = '&'; // Restore parameters 162 url_key_value(parameters, "number_of_worms", &val_start, &val_end); 163 *(val_end) = '\0'; // Terminate string 164 nvs_set_u8(flash_handle, "number_of_worms", (uint8_t) atol(val_start)); 165 *(val_end) = '&'; // Restore parameters 166 url_key_value(parameters, "max_brightness", &val_start, &val_end); 167 *(val_end) = '\0'; // Terminate string 168 nvs_set_u8(flash_handle, "max_brightness", (uint8_t) atol(val_start)); 169 *(val_end) = '&'; // Restore parameters 170 171 nvs_commit(flash_handle); 172 nvs_close(flash_handle); 173 /* Perform reset. Since we reset, it is not necessary to 174 update our internal configuration variables, since this 175 will be done once we start again after the reset. */ 176 netconn_write(conn, http_reset_html, strlen(http_reset_html)-1, 177 NETCONN_NOCOPY); 178 /* Since we want to perform a reset, we have to close the 179 connection right here. */ 180 netconn_close(conn); 181 delay(1000); 182 esp_restart(); 183 } 184 else { 185 /* Return config page based on config_template_html. We have to 186 fill out the template. This requires creation of a buffer of 187 apropriate size. The numbers in the size calculation are: 3 188 characters each for number of horizontal leds, vertical leds, 189 number of worms, and max brightness, plus length of string 190 "checked" *4 which will be a parameter for radio buttons. We 191 subtract 7*2 bytes for the format variables ("%s") in the 192 template file. */ 193 size_t http_config_html_len = (sizeof(http_config_template_html) + 194 sizeof(app_name)*3 + 195 sizeof(wifi_essid) + 196 sizeof(wifi_password) + 197 strlen("checked")*4 + 4*3 - 7*2); 198 char *http_config_html = malloc(http_config_html_len); 199 snprintf(http_config_html, http_config_html_len, 200 http_config_template_html, app_name, app_name, app_name, 201 wifi_essid, wifi_password, 202 wifi_ap ? "checked" : "", wifi_ap ? "" : "checked", 203 led_strip_w, led_strip_h, 204 snake ? "checked" : "", snake ? "" : "checked", 205 wrap_around ? "checked" : "", wrap_around ? "" : "checked", 206 number_of_worms, 207 max_brightness); 208 netconn_write(conn, http_config_html, strlen(http_config_html)-1, 209 NETCONN_NOCOPY); 210 free(http_config_html); 211 } 212 } 213 214 /* }}} */ 215 216 /* {{{ main */ 217 218 void read_configuration_from_flash(){ 219 // All global configuration variables are defined in led.h (some 220 // refactoring required ...) 221 nvs_flash_init(); 222 nvs_handle flash_handle; 223 nvs_open("blinkenstrip", NVS_READWRITE, &flash_handle); 224 size_t required_size; 225 bool flash_changed = false; 226 esp_err_t err = nvs_get_str(flash_handle, "app_name", NULL, &required_size); 227 if(err != ESP_OK){ 228 /* Not yet initialized. Use default. */ 229 nvs_set_str(flash_handle, "app_name", CONFIG_APP_NAME); 230 nvs_get_str(flash_handle, "app_name", NULL, &required_size); 231 flash_changed = true; 232 } 233 app_name = malloc(required_size); 234 nvs_get_str(flash_handle, "app_name", app_name, &required_size); 235 err = nvs_get_str(flash_handle, "wifi_essid", NULL, &required_size); 236 if(err != ESP_OK){ 237 /* Not yet initialized. Use default. */ 238 nvs_set_str(flash_handle, "wifi_essid", CONFIG_WIFI_SSID); 239 nvs_get_str(flash_handle, "wifi_essid", NULL, &required_size); 240 flash_changed = true; 241 } 242 wifi_essid = malloc(required_size); 243 nvs_get_str(flash_handle, "wifi_essid", wifi_essid, &required_size); 244 err = nvs_get_str(flash_handle, "wifi_password", NULL, &required_size); 245 wifi_password = malloc(required_size); 246 if(err != ESP_OK){ 247 /* Not yet initialized. Use default. */ 248 nvs_set_str(flash_handle, "wifi_password", CONFIG_WIFI_PASSWORD); 249 nvs_get_str(flash_handle, "wifi_password", NULL, &required_size); 250 flash_changed = true; 251 } 252 nvs_get_str(flash_handle, "wifi_password", wifi_password, &required_size); 253 if(nvs_get_u8(flash_handle, "wifi_ap", &wifi_ap) != ESP_OK){ 254 /* Not yet initialized. Use default. */ 255 #ifdef CONFIG_WIFI_AP 256 wifi_ap = true; 257 #else 258 wifi_ap = false; 259 #endif 260 nvs_set_u8(flash_handle, "wifi_ap", wifi_ap); 261 flash_changed = true; 262 } 263 if(nvs_get_u16(flash_handle, "led_strip_w", &led_strip_w) != ESP_OK){ 264 /* Not yet initialized. Use default. */ 265 led_strip_w = CONFIG_LED_STRIP_WIDTH; 266 nvs_set_u16(flash_handle, "led_strip_w", led_strip_w); 267 flash_changed = true; 268 } 269 if(nvs_get_u16(flash_handle, "led_strip_h", &led_strip_h) != ESP_OK){ 270 /* Not yet initialized. Use default. */ 271 led_strip_h = CONFIG_LED_STRIP_HEIGHT; 272 nvs_set_u16(flash_handle, "led_strip_h", led_strip_h); 273 flash_changed = true; 274 } 275 if(nvs_get_u8(flash_handle, "snake", &snake) != ESP_OK){ 276 /* Not yet initialized. Use default. */ 277 #ifdef CONFIG_LED_STRIP_SNAKE 278 snake = true; 279 #else 280 snake = false; 281 #endif 282 nvs_set_u8(flash_handle, "snake", snake); 283 flash_changed = true; 284 } 285 if(nvs_get_u8(flash_handle, "wrap_around", &wrap_around) != ESP_OK){ 286 /* Not yet initialized. Use default. */ 287 #ifdef CONFIG_LED_STRIP_WRAP_AROUND 288 wrap_around = true; 289 #else 290 wrap_around = false; 291 #endif 292 nvs_set_u8(flash_handle, "wrap_around", wrap_around); 293 flash_changed = true; 294 } 295 if(nvs_get_u8(flash_handle, "number_of_worms", &number_of_worms) != ESP_OK){ 296 /* Not yet initialized. Use default. */ 297 number_of_worms = CONFIG_NUMBER_OF_WORMS; 298 nvs_set_u8(flash_handle, "number_of_worms", number_of_worms); 299 flash_changed = true; 300 } 301 if(nvs_get_u8(flash_handle, "max_brightness", &max_brightness) != ESP_OK){ 302 /* Not yet initialized. Use default. */ 303 max_brightness = CONFIG_MAX_BRIGHTNESS; 304 nvs_set_u8(flash_handle, "max_brightness", max_brightness); 305 flash_changed = true; 306 } 307 if(nvs_get_u8(flash_handle, "blinkenstrip_state", (uint8_t *) &blinkenstrip_state) != ESP_OK){ 308 /* Not yet initialized. Use default. */ 309 blinkenstrip_state = BLINKENSTRIP_STATE_RANDOM_ON_OFF; 310 nvs_set_u8(flash_handle, "blinkenstrip_state", blinkenstrip_state); 311 flash_changed = true; 312 } 313 if(nvs_get_u8(flash_handle, "led_col_red", &led_color_rgb.red) != ESP_OK){ 314 /* Not yet initialized. Use default. */ 315 led_color_rgb.red = 0xFF; 316 nvs_set_u8(flash_handle, "led_col_red", led_color_rgb.red); 317 flash_changed = true; 318 } 319 if(nvs_get_u8(flash_handle, "led_col_green", &led_color_rgb.green) != ESP_OK){ 320 /* Not yet initialized. Use default. */ 321 led_color_rgb.green = 0xFF; 322 nvs_set_u8(flash_handle, "led_col_green", led_color_rgb.green); 323 flash_changed = true; 324 } 325 if(nvs_get_u8(flash_handle, "led_col_blue", &led_color_rgb.blue) != ESP_OK){ 326 /* Not yet initialized. Use default. */ 327 led_color_rgb.blue = 0xFF; 328 nvs_set_u8(flash_handle, "led_col_blue", led_color_rgb.blue); 329 flash_changed = true; 330 } 331 if(flash_changed){ 332 nvs_commit(flash_handle); 333 } 334 nvs_close(flash_handle); 335 DEBUG_MSG(DEBUG, ("app_name: %s\n", app_name)); 336 DEBUG_MSG(DEBUG, ("wifi_essid: %s\n", wifi_essid)); 337 DEBUG_MSG(DEBUG, ("wifi_password: %s\n", wifi_password)); 338 DEBUG_MSG(DEBUG, ("wifi_ap: %d\n", wifi_ap)); 339 DEBUG_MSG(DEBUG, ("led_strip_w: %d\n", led_strip_w)); 340 DEBUG_MSG(DEBUG, ("led_strip_h: %d\n", led_strip_h)); 341 DEBUG_MSG(DEBUG, ("snake: %d\n", snake)); 342 DEBUG_MSG(DEBUG, ("wrap_around: %d\n", wrap_around)); 343 DEBUG_MSG(DEBUG, ("number_of_worms: %d\n", number_of_worms)); 344 DEBUG_MSG(DEBUG, ("max_brightness: %d\n", max_brightness)); 345 DEBUG_MSG(DEBUG, ("blinkenstrip_state: %d (%d, %d, %d)\n", blinkenstrip_state, 346 led_color_rgb.red, led_color_rgb.green, led_color_rgb.blue)); 347 } 348 349 // Factory reset is triggered if factory reset pins are connected. 350 void check_for_factory_reset(){ 351 gpio_pad_select_gpio(CONFIG_PIN_FACTORY_RESET_A); 352 gpio_set_direction(CONFIG_PIN_FACTORY_RESET_A, GPIO_MODE_OUTPUT); 353 gpio_set_level(CONFIG_PIN_FACTORY_RESET_A, 1); 354 gpio_pad_select_gpio(CONFIG_PIN_FACTORY_RESET_B); 355 gpio_pullup_dis(CONFIG_PIN_FACTORY_RESET_B); 356 gpio_set_direction(CONFIG_PIN_FACTORY_RESET_B, GPIO_MODE_INPUT); 357 if(gpio_get_level(CONFIG_PIN_FACTORY_RESET_B)){ 358 DEBUG_MSG(DEBUG, ("Reset pin is HIGH!\n")); 359 /* RESET pins are connect. Clear NV memory! */ 360 nvs_flash_init(); 361 nvs_handle flash_handle; 362 nvs_open("blinkenstrip", NVS_READWRITE, &flash_handle); 363 nvs_erase_all(flash_handle); 364 nvs_commit(flash_handle); 365 nvs_close(flash_handle); 366 DEBUG_MSG(DEBUG, ("NV memory cleared!\n")); 367 delay(5000); 368 } 369 else{ 370 DEBUG_MSG(DEBUG, ("Reset pin is LOW!\n")); 371 gpio_set_level(CONFIG_PIN_FACTORY_RESET_A, 0); 372 } 373 } 374 375 int app_main(void) { 376 check_for_factory_reset(); 377 read_configuration_from_flash(); 378 379 initialise_wifi(wifi_ap, wifi_essid, wifi_password); 380 381 xTaskCreate(&http_server, "http_server", 4096, NULL, 5, &http_server_task); 382 vTaskSuspend(http_server_task); 383 xTaskCreate(&led_controller, "main_led_task", 4096, NULL, 5, NULL); 384 return 0; 385 } 386 387 /* }}} */