led.c (18077B)
1 /* 2 Function unicolor_rainbow based on 3 <https://www.esp32.com/viewtopic.php?t=589#> ((c) Lucas Bruder 4 <LBruder@me.com> 2016) 5 6 Everything else copyright © 2021 Gerd Beuster <gerd@frombelow.net> 7 8 Licensed under the Apache License, Version 2.0 (the "License"); 9 you may not use this file except in compliance with the License. 10 You may obtain a copy of the License at 11 12 http://www.apache.org/licenses/LICENSE-2.0 13 14 Unless required by applicable law or agreed to in writing, software 15 distributed under the License is distributed on an "AS IS" BASIS, 16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 See the License for the specific language governing permissions and 18 limitations under the License. 19 */ 20 21 /* {{{ Includes and definitions */ 22 23 #include "led.h" 24 #include "debug.h" 25 #include "freertos/task.h" 26 #include "nvs_flash.h" 27 #include "math.h" 28 29 /* }}} */ 30 31 void store_state_if_changed() { 32 if(blinkenstrip_state_has_changed) { 33 nvs_handle flash_handle; 34 nvs_open("blinkenstrip", NVS_READWRITE, &flash_handle); 35 nvs_set_u8(flash_handle, "blinkenstrip_state", blinkenstrip_state); 36 nvs_set_u8(flash_handle, "led_col_red", led_color_rgb.red); 37 nvs_set_u8(flash_handle, "led_col_green", led_color_rgb.green); 38 nvs_set_u8(flash_handle, "led_col_blue", led_color_rgb.blue); 39 nvs_commit(flash_handle); 40 nvs_close(flash_handle); 41 DEBUG_MSG(DEBUG, ("Wrote new state (%d / (%d, %d, %d)) to flash.\n", 42 blinkenstrip_state, led_color_rgb.red, led_color_rgb.green, led_color_rgb.blue)); 43 } 44 } 45 46 /* {{{ Unicolor patterns */ 47 48 void unicolor(struct led_color_t led_color){ 49 store_state_if_changed(); 50 for (uint32_t index = 0; index < led_strip_length; index++) { 51 led_strip_set_pixel_color(&led_strip, index, &led_color); 52 } 53 led_strip_show(&led_strip); 54 blinkenstrip_state_has_changed = false; 55 } 56 57 const uint8_t lights[360]={ 58 0, 0, 0, 0, 0, 1, 1, 2, 59 2, 3, 4, 5, 6, 7, 8, 9, 60 11, 12, 13, 15, 17, 18, 20, 22, 61 24, 26, 28, 30, 32, 35, 37, 39, 62 42, 44, 47, 49, 52, 55, 58, 60, 63 63, 66, 69, 72, 75, 78, 81, 85, 64 88, 91, 94, 97, 101, 104, 107, 111, 65 114, 117, 121, 124, 127, 131, 134, 137, 66 141, 144, 147, 150, 154, 157, 160, 163, 67 167, 170, 173, 176, 179, 182, 185, 188, 68 191, 194, 197, 200, 202, 205, 208, 210, 69 213, 215, 217, 220, 222, 224, 226, 229, 70 231, 232, 234, 236, 238, 239, 241, 242, 71 244, 245, 246, 248, 249, 250, 251, 251, 72 252, 253, 253, 254, 254, 255, 255, 255, 73 255, 255, 255, 255, 254, 254, 253, 253, 74 252, 251, 251, 250, 249, 248, 246, 245, 75 244, 242, 241, 239, 238, 236, 234, 232, 76 231, 229, 226, 224, 222, 220, 217, 215, 77 213, 210, 208, 205, 202, 200, 197, 194, 78 191, 188, 185, 182, 179, 176, 173, 170, 79 167, 163, 160, 157, 154, 150, 147, 144, 80 141, 137, 134, 131, 127, 124, 121, 117, 81 114, 111, 107, 104, 101, 97, 94, 91, 82 88, 85, 81, 78, 75, 72, 69, 66, 83 63, 60, 58, 55, 52, 49, 47, 44, 84 42, 39, 37, 35, 32, 30, 28, 26, 85 24, 22, 20, 18, 17, 15, 13, 12, 86 11, 9, 8, 7, 6, 5, 4, 3, 87 2, 2, 1, 1, 0, 0, 0, 0, 88 0, 0, 0, 0, 0, 0, 0, 0, 89 0, 0, 0, 0, 0, 0, 0, 0, 90 0, 0, 0, 0, 0, 0, 0, 0, 91 0, 0, 0, 0, 0, 0, 0, 0, 92 0, 0, 0, 0, 0, 0, 0, 0, 93 0, 0, 0, 0, 0, 0, 0, 0, 94 0, 0, 0, 0, 0, 0, 0, 0, 95 0, 0, 0, 0, 0, 0, 0, 0, 96 0, 0, 0, 0, 0, 0, 0, 0, 97 0, 0, 0, 0, 0, 0, 0, 0, 98 0, 0, 0, 0, 0, 0, 0, 0, 99 0, 0, 0, 0, 0, 0, 0, 0, 100 0, 0, 0, 0, 0, 0, 0, 0, 101 0, 0, 0, 0, 0, 0, 0, 0, 102 0, 0, 0, 0, 0, 0, 0, 0}; 103 104 void unicolor_rainbow(){ 105 106 static int rgb_red=0; 107 static int rgb_green=120; 108 static int rgb_blue=240; 109 110 static struct led_color_t led_color = { 111 .red = 5, 112 .green = 0, 113 .blue = 0, 114 }; 115 116 store_state_if_changed(); 117 for (uint32_t index = 0; index < led_strip_length; index++) { 118 led_strip_set_pixel_color(&led_strip, index, &led_color); 119 } 120 led_strip_show(&led_strip); 121 122 led_color.red = lights[rgb_red] * max_brightness / 0xFF; 123 led_color.green = lights[rgb_green] * max_brightness / 0xFF; 124 led_color.blue = lights[rgb_blue] * max_brightness / 0xFF; 125 126 rgb_red += 1; 127 rgb_green += 1; 128 rgb_blue += 1; 129 130 if (rgb_red >= 360) rgb_red=0; 131 if (rgb_green >= 360) rgb_green=0; 132 if (rgb_blue >= 360) rgb_blue=0; 133 134 blinkenstrip_state_has_changed = false; 135 } 136 137 /* }}} */ 138 139 /* {{{ Random on/off */ 140 141 uint32_t get_random_number(uint32_t n){ 142 uint32_t r; 143 do{ 144 r = esp_random(); 145 } while(r > (RAND_MAX - (RAND_MAX % n))); 146 return(r % n); 147 } 148 149 void random_on_off(){ 150 if(blinkenstrip_state_has_changed){ 151 // Initalize state 152 for (uint32_t index = 0; index < led_strip_length; index++) { 153 led_strip_set_pixel_color(&led_strip, index, 154 (struct led_color_t *) &led_color_blue); 155 } 156 store_state_if_changed(); 157 blinkenstrip_state_has_changed = false; 158 } 159 else{ 160 // Update state 161 // Copy over "old" strip 162 struct led_color_t c; 163 for (uint32_t index = 0; index < led_strip_length; index++) { 164 led_strip_get_pixel_color(&led_strip, index, &c); 165 led_strip_set_pixel_color(&led_strip, index, &c); 166 } 167 // Randomly switch an LED to green or blue. 168 uint32_t switch_led = get_random_number(led_strip_length); 169 if (get_random_number(2)){ 170 led_strip_set_pixel_color(&led_strip, switch_led, 171 (struct led_color_t *) &led_color_blue); 172 } 173 else{ 174 led_strip_set_pixel_color(&led_strip, switch_led, 175 (struct led_color_t *) &led_color_green); 176 } 177 } 178 led_strip_show(&led_strip); 179 } 180 181 /* }}} */ 182 183 /* {{{ Running dot */ 184 185 void running_dot(){ 186 static uint32_t position = 0; 187 store_state_if_changed(); 188 blinkenstrip_state_has_changed = false; 189 for (uint32_t index = 0; index < led_strip_length; index++) { 190 led_strip_set_pixel_color(&led_strip, index, 191 (struct led_color_t *) &led_color_blue); 192 } 193 led_strip_set_pixel_color(&led_strip, position, 194 (struct led_color_t *) &led_color_red); 195 position = (position + 1) % led_strip_length; 196 blinkenstrip_state_has_changed = false; 197 led_strip_show(&led_strip); 198 }; 199 200 /* }}} */ 201 202 /* {{{ Cellular Automaton */ 203 204 int32_t xy2i(int32_t x, int32_t y) { 205 int32_t i = y * led_strip_w; 206 if ((y % 2) && snake) { 207 i += led_strip_w - x - 1; 208 } 209 else { 210 i += x; 211 } 212 return(i); 213 } 214 215 216 bool cell_is_on(struct led_strip_t led_strip, int32_t x, int32_t y){ 217 static struct led_color_t c; 218 x = (x + led_strip_w) % led_strip_w; 219 y = (y + led_strip_h) % led_strip_h; 220 led_strip_get_pixel_color(&led_strip, xy2i(x, y), 221 &c); 222 return((c.red == cell_on.red) && 223 (c.green == cell_on.green) && 224 (c.blue == cell_on.blue)); 225 }; 226 227 void cellular_automaton(){ 228 static uint8_t number_of_updates = 0; 229 static bool stable_pattern = false; 230 // Since we get a stable pattern after sometime, we restart with a 231 // new random cellular automaton after a fixed number of steps or 232 // when we detect a stable situation. 233 store_state_if_changed(); 234 if(blinkenstrip_state_has_changed || stable_pattern || 235 (number_of_updates == 100)) { 236 // Initialize with random values 237 for (uint32_t index = 0; index < led_strip_length; index++) { 238 if (get_random_number(2)){ 239 led_strip_set_pixel_color(&led_strip, index, 240 (struct led_color_t *) &cell_on); 241 } 242 else{ 243 led_strip_set_pixel_color(&led_strip, index, 244 (struct led_color_t *) &cell_off); 245 } 246 } 247 led_strip_show(&led_strip); 248 number_of_updates = 0; 249 blinkenstrip_state_has_changed = false; 250 stable_pattern = false; 251 } 252 // Update automaton 253 if((!stable_pattern) && (number_of_updates++ < 100)){ 254 // Cycle over all cells 255 for (int32_t y = 0; y < led_strip_h; y++){ 256 for (int32_t x = 0; x < led_strip_w; x++){ 257 // Count neighbors 258 uint8_t neighbors = 0; 259 for (int32_t y_neighbor = 0; y_neighbor < 3; y_neighbor++){ 260 for (int32_t x_neighbor = 0; x_neighbor < 3; x_neighbor++){ 261 if (((x_neighbor != 1) || (y_neighbor != 1)) && 262 (cell_is_on(led_strip, x-x_neighbor+1, y-y_neighbor+1))){ 263 neighbors++; 264 } 265 } 266 } 267 // Update cell 268 uint32_t index = xy2i(x, y); 269 if (neighbors < 2) { 270 led_strip_set_pixel_color(&led_strip, index, 271 (struct led_color_t *) &cell_off); 272 } 273 else if (neighbors > 3) { 274 led_strip_set_pixel_color(&led_strip, index, 275 (struct led_color_t *) &cell_off); 276 } 277 else if (neighbors == 3) { 278 led_strip_set_pixel_color(&led_strip, index, 279 (struct led_color_t *) &cell_on); 280 } 281 else { 282 struct led_color_t c; 283 led_strip_get_pixel_color(&led_strip, index, &c); 284 led_strip_set_pixel_color(&led_strip, index, &c); 285 } 286 } 287 } 288 // Check if pattern has changed since last update. 289 // (We will exit the loop if the pattern becomes stable) 290 stable_pattern = true; 291 for(uint32_t i = 0; (i < led_strip_length) && stable_pattern; i++){ 292 stable_pattern = 293 (led_strip.led_strip_buf_1[i].red == 294 led_strip.led_strip_buf_2[i].red) && 295 (led_strip.led_strip_buf_1[i].green == 296 led_strip.led_strip_buf_2[i].green) && 297 (led_strip.led_strip_buf_1[i].blue == 298 led_strip.led_strip_buf_2[i].blue); 299 } 300 led_strip_show(&led_strip); 301 vTaskDelay(600 / portTICK_PERIOD_MS); 302 } 303 } 304 305 /* }}} */ 306 307 /* {{{ Line-by-line switch */ 308 309 void set_row_color(struct led_color_t led_color, uint32_t row) { 310 for (uint32_t index = 0; index < led_strip_w; index++) { 311 led_strip_set_pixel_color(&led_strip, xy2i(index, row), 312 &led_color); 313 } 314 } 315 316 void set_column_color(struct led_color_t led_color, uint32_t column) { 317 for (uint32_t index = 0; index < led_strip_h; index++) { 318 led_strip_set_pixel_color(&led_strip, xy2i(column, index), 319 &led_color); 320 } 321 } 322 323 void line_by_line() { 324 static bool change_row = true; // Switch in rows or in columns? 325 static void (*switch_command)(struct led_color_t led_color, uint32_t row); 326 const uint8_t color_cycle[] = { 327 max_brightness, max_brightness, 0 // Yellow 328 , 0, max_brightness, max_brightness // Turquoise 329 , 0, 0, max_brightness // Blue 330 }; 331 const uint8_t color_cycle_length = sizeof(color_cycle); 332 333 static uint8_t pos_in_color_cycle = 0; 334 static uint32_t switch_line = 0; 335 static struct led_color_t old_color; 336 static struct led_color_t new_color; 337 store_state_if_changed(); 338 if (switch_line == 0) { 339 old_color.red = color_cycle[pos_in_color_cycle]; 340 old_color.green = color_cycle[pos_in_color_cycle+1]; 341 old_color.blue = color_cycle[pos_in_color_cycle+2]; 342 pos_in_color_cycle = (pos_in_color_cycle + 3) % color_cycle_length; 343 new_color.red = color_cycle[pos_in_color_cycle]; 344 new_color.green = color_cycle[pos_in_color_cycle+1]; 345 new_color.blue = color_cycle[pos_in_color_cycle+2]; 346 change_row = !change_row; 347 if(change_row){ 348 switch_command = set_row_color; 349 } 350 else{ 351 switch_command = set_column_color; 352 } 353 } 354 for (uint8_t row = 0; row < led_strip_h; row++) { 355 if (row > switch_line) { 356 (*switch_command)(old_color, row); 357 } 358 else if (row == switch_line) { 359 (*switch_command)(led_color_red, row); 360 } 361 else { 362 (*switch_command)(new_color, row); 363 } 364 } 365 switch_line = (switch_line + 1) % led_strip_h; 366 led_strip_show(&led_strip); 367 blinkenstrip_state_has_changed = false; 368 } 369 370 /* }}} */ 371 372 /* {{{ Worm */ 373 374 // Positions and running directions of worms (allocated in main_led_task) 375 int8_t *worm_direction; 376 uint16_t *worm_pos; 377 378 void worm() { 379 // Percentage of max_brightness relative to full brightness 380 float mb_perc = ((float) max_brightness) / 0xFF; 381 /* const float mb_perc = 1; */ 382 struct led_color_t scanner_pattern[] = 383 {{round(0x00*mb_perc), round(0x00*mb_perc), round(0x00*mb_perc)}, 384 {round(0x00*mb_perc), round(0x20*mb_perc), round(0x00*mb_perc)}, 385 {round(0x20*mb_perc), round(0x20*mb_perc), round(0x00*mb_perc)}, 386 {round(0xFF*mb_perc), round(0x00*mb_perc), round(0x00*mb_perc)}, 387 {round(0xFF*mb_perc), round(0x00*mb_perc), round(0x00*mb_perc)}, 388 {round(0xFF*mb_perc), round(0x00*mb_perc), round(0x00*mb_perc)}, 389 {round(0xFF*mb_perc), round(0x00*mb_perc), round(0x00*mb_perc)}, 390 {round(0xFF*mb_perc), round(0x00*mb_perc), round(0x00*mb_perc)}, 391 {round(0x20*mb_perc), round(0x20*mb_perc), round(0x00*mb_perc)}, 392 {round(0x00*mb_perc), round(0x20*mb_perc), round(0x00*mb_perc)}, 393 {round(0x00*mb_perc), round(0x00*mb_perc), round(0x00*mb_perc)}}; 394 const uint8_t scanner_pattern_length = (sizeof(scanner_pattern) / 395 sizeof(struct led_color_t)); 396 store_state_if_changed(); 397 if(blinkenstrip_state_has_changed){ 398 // Initalize state 399 for (uint32_t index = 0; index < led_strip_length; index++) { 400 led_strip_set_pixel_color(&led_strip, index, 401 (struct led_color_t *) &led_color_black); 402 } 403 for (uint8_t w = 0; w < number_of_worms; w++) { 404 worm_direction[w] = get_random_number(2)*2-1; 405 worm_pos[w] = get_random_number(led_strip_length-scanner_pattern_length); 406 } 407 blinkenstrip_state_has_changed = false; 408 } 409 else{ 410 // Update state 411 // Copy over "old" strip 412 struct led_color_t c; 413 for (uint32_t index = 0; index < led_strip_length; index++) { 414 led_strip_get_pixel_color(&led_strip, index, &c); 415 led_strip_set_pixel_color(&led_strip, index, &c); 416 } 417 // Worms 418 for (uint8_t w = 0; w < number_of_worms; w++) { 419 for (uint8_t i = 0; i < scanner_pattern_length; i++) { 420 led_strip_set_pixel_color(&led_strip, 421 (worm_pos[w]+i) % led_strip_length, 422 &scanner_pattern[i]); 423 } 424 if (!get_random_number(50)) { 425 worm_direction[w] *= -1; 426 } 427 if (!wrap_around) { 428 // Switch direction if we hit the end of the strip 429 if (worm_pos[w] == 0){ 430 worm_direction[w] = 1; 431 } 432 if ((worm_pos[w] + scanner_pattern_length) == led_strip_length) { 433 worm_direction[w] = -1; 434 } 435 } 436 worm_pos[w] = (worm_pos[w] + worm_direction[w]) % led_strip_length; 437 }; 438 // Randomly switch an LED to blue 439 uint32_t switch_led = get_random_number(led_strip_length); 440 if (!get_random_number(4)){ 441 led_strip_set_pixel_color(&led_strip, switch_led, 442 (struct led_color_t *) &led_color_blue); 443 } 444 } 445 led_strip_show(&led_strip); 446 } 447 448 /* }}} */ 449 450 void experiment() { 451 /* empty */ 452 } 453 454 /* {{{ main */ 455 456 void led_controller(void *args) { 457 458 // Define standard colors 459 led_color_red.red = max_brightness; 460 led_color_red.green = 0x00; 461 led_color_red.blue = 0x00; 462 led_color_green.red = 0x00; 463 led_color_green.green = max_brightness; 464 led_color_green.blue = 0x00; 465 led_color_blue.red = 0x00; 466 led_color_blue.green = 0x00; 467 led_color_blue.blue = max_brightness; 468 led_color_yellow.red = max_brightness; 469 led_color_yellow.green = max_brightness; 470 led_color_yellow.blue = 0x00; 471 led_color_white.red = max_brightness; 472 led_color_white.green = max_brightness; 473 led_color_white.blue = max_brightness; 474 led_color_black.red = 0x00; 475 led_color_black.green = 0x00; 476 led_color_black.blue = 0x00; 477 478 // Define colors for cellular automaton 479 cell_on.red = 0x00; 480 cell_on.green = max_brightness; 481 cell_on.blue = 0x00; 482 cell_off.red = 0x00; 483 cell_off.green = 0x00; 484 cell_off.blue = max_brightness / 2; 485 486 // Initialize data structures for worm 487 worm_direction = calloc(number_of_worms, sizeof(int8_t)); // Yes, size is 1 byte ... 488 worm_pos = calloc(number_of_worms, sizeof(uint16_t)); // Yes, size is 2 bytes ... 489 490 // Initialize data structures for LED strip 491 led_strip_length = led_strip_w * led_strip_h; 492 led_strip_buf_1 = malloc(led_strip_length * sizeof(struct led_color_t)); 493 led_strip_buf_2 = malloc(led_strip_length * sizeof(struct led_color_t)); 494 led_strip.rgb_led_type = RGB_LED_TYPE_WS2812; 495 led_strip.rmt_channel = RMT_CHANNEL_1; 496 led_strip.rmt_interrupt_num = LED_STRIP_RMT_INTR_NUM; 497 led_strip.gpio = CONFIG_WS2811_DATA_PIN; 498 led_strip.led_strip_buf_1 = led_strip_buf_1; 499 led_strip.led_strip_buf_2 = led_strip_buf_2; 500 led_strip.led_strip_length = led_strip_length; 501 502 blinkenstrip_state_changed_semaphore = xSemaphoreCreateBinary(); 503 xSemaphoreGive(blinkenstrip_state_changed_semaphore); 504 assert(blinkenstrip_state_changed_semaphore); 505 led_strip.access_semaphore = xSemaphoreCreateBinary(); 506 assert(led_strip.access_semaphore); 507 bool led_init_ok = led_strip_init(&led_strip); 508 assert(led_init_ok); 509 510 blinkenstrip_state_has_changed = true; 511 while (true) { 512 xSemaphoreTake(blinkenstrip_state_changed_semaphore, portMAX_DELAY); 513 switch(blinkenstrip_state){ 514 case BLINKENSTRIP_STATE_UNICOLOR_RAINBOW: 515 unicolor_rainbow(); break; 516 case BLINKENSTRIP_STATE_RANDOM_ON_OFF: 517 random_on_off(); break; 518 case BLINKENSTRIP_STATE_RUNNING_DOT: 519 running_dot(); break; 520 case BLINKENSTRIP_STATE_CELLULAR_AUTOMATON: 521 cellular_automaton(); break; 522 case BLINKENSTRIP_STATE_LINE_BY_LINE: 523 line_by_line(); break; 524 case BLINKENSTRIP_STATE_WORM: 525 worm(); break; 526 case BLINKENSTRIP_STATE_UNICOLOR_RGB: 527 if(blinkenstrip_state_has_changed) 528 unicolor(led_color_rgb); 529 break; 530 case BLINKENSTRIP_STATE_EXPERIMENTAL: 531 experiment(); break; 532 } 533 xSemaphoreGive(blinkenstrip_state_changed_semaphore); 534 vTaskDelay(60 / portTICK_PERIOD_MS); 535 } 536 } 537 538 /* }}} */