├── ArtnetWifi_SDreadwrite.ino ├── README.md └── pics ├── Thumbs.db ├── back.JPG ├── blynk.JPG ├── blynk1.JPG ├── panels.JPG └── wall2.jpg /ArtnetWifi_SDreadwrite.ino: -------------------------------------------------------------------------------- 1 | //**********************************************************************************************************// 2 | // With this project it is possible to drive Ws2812 Leds with Artnet over Wifi (with Jinx! or Glediator) // 3 | // and it´s also possible to switch to the SD-card version where animations can be recorded from Artnet and // 4 | // than be played back. Although a Matrix-clock can be switched on and off over the animations. // 5 | // You will need the Fastled Library from Samguyer https://github.com/samguyer/FastLED // 6 | // and the Artnet-library of Yves Bazin: https://github.com/hpwit/artnet // 7 | // // 8 | // Created by Stefan Weickinger & Yves BAZIN 2018 // 9 | //**********************************************************************************************************// 10 | 11 | 12 | //-------------------Set RTC------------------------------------------------------- 13 | #include "time.h" 14 | 15 | 16 | const char* ntpServer = "pool.ntp.org"; 17 | const long gmtOffset_sec = 3600; 18 | const int daylightOffset_sec = 3600; 19 | 20 | //------------------------------ WiFi details------------------------------ 21 | 22 | const char *ssid = ""; //Setup your Wifi 23 | const char *pass = ""; 24 | 25 | IPAddress local_ip(192,168,137,10); 26 | IPAddress gateway_ip(192,168,137,1); 27 | IPAddress subnet_mask(255, 255, 255, 0); 28 | IPAddress dns_ip(8,8,8,8); //if needed 29 | 30 | //------------------------Wifi variables ------------------------------ 31 | bool isWifiConnected=false; 32 | bool isBlynkConnected=false; 33 | bool isTryingToConnect=false; 34 | static TaskHandle_t checkWifiTaskHandle; 35 | static TaskHandle_t userTaskHandle = 0; 36 | int WAIT_WIFI_MAX=100; 37 | int WAIT_WIFI = 0; 38 | //----------------------------------BLYNK------------------------------------------ 39 | 40 | #define BLYNK_PRINT Serial 41 | 42 | #include 43 | 44 | char auth[] = ""; //fill in the Auth-Token from your Blynk-setup 45 | 46 | //--------------------------------Clock display--------------------------------- 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | RtcDS1307 Rtc(Wire); 54 | 55 | #define mw 80 56 | #define mh 32 57 | #define NUMMATRIX (mw*mh) 58 | #define LED_RUN_LENGTH 16//number of leds per column 59 | 60 | CRGB matrixleds[NUMMATRIX]; 61 | 62 | FastLED_NeoMatrix *matrix = new FastLED_NeoMatrix(matrixleds,16,16,5,2, 63 | NEO_TILE_TOP + NEO_TILE_LEFT + NEO_TILE_ROWS +NEO_TILE_ZIGZAG+ 64 | NEO_MATRIX_TOP + NEO_MATRIX_LEFT + 65 | NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG ); 66 | 67 | const uint16_t colors[] = { 68 | matrix->Color(255, 0, 0), matrix->Color(0, 255, 0), matrix->Color(0, 0, 255) }; 69 | 70 | //--------------------------------------------------------------------------------- 71 | 72 | #include 73 | //#include 74 | 75 | #include 76 | #include 77 | 78 | #include "SD.h" 79 | #include "SPI.h" 80 | //#define FASTLED_ALLOW_INTERRUPTS 0 81 | //#define INTERRUPT_THRESHOLD 1 82 | #include "FastLED.h" 83 | FASTLED_USING_NAMESPACE 84 | #define FASTLED_SHOW_CORE 0 85 | 86 | 87 | #define LED_WIDTH 80 88 | #define LED_HEIGHT 32 89 | #define NUM_LEDS LED_WIDTH*LED_HEIGHT 90 | #define UNIVERSE_SIZE 170 91 | #define PIXEL_PER_PIN NUM_LEDS/5 92 | /*we will use 5 pins each pin will drive two 16x16 panel hence 512 pix it will take 15ms to refresh your entire panel instead of 76ms 93 | hence having a refresh rate of 65fps instead of 13fps*/ 94 | #define PIN1 15 95 | #define PIN2 4 96 | #define PIN3 27 97 | #define PIN4 25 98 | #define PIN5 13 99 | 100 | #define BUFFER_SIZE 10 101 | uint8_t readbuffer[NUM_LEDS*3]; 102 | char READ_NAME[]="savedata"; //the name of your save 103 | Artnet artnet; 104 | 105 | File root; 106 | static File myFile; 107 | 108 | int blynk_wait, blynk_wait_max=0; 109 | 110 | #define BRIGHTNESS bright 111 | #define MILLI_AMPS 35000 // if needed 35A max 112 | 113 | char datestring[20]; 114 | char blank[20]; 115 | 116 | int MatrixTime=0; 117 | int Screen=0; 118 | int bright=100; 119 | int nextFile=1; 120 | 121 | int color=0; 122 | 123 | 124 | //----------------------------RECORDING--------------------- 125 | char filename[256]; 126 | char SAVE_NAME[]="savedata"; //the name of your save 127 | 128 | static uint32_t deb=0; 129 | static uint32_t fin=0; 130 | 131 | static bool new_record=true; 132 | int recordingnumber=0; 133 | 134 | int record=0; 135 | 136 | bool stopped=false; 137 | 138 | static uint8_t frame[NUM_LEDS*3]; 139 | //----------------------------------------------------------- 140 | 141 | int status = WL_IDLE_STATUS; 142 | 143 | BLYNK_CONNECTED() { 144 | Blynk.syncAll(); 145 | } 146 | 147 | BLYNK_WRITE(V0) 148 | { Screen=param.asInt(); 149 | } 150 | 151 | BLYNK_WRITE(V1) // 152 | { MatrixTime=param.asInt(); 153 | } 154 | 155 | BLYNK_WRITE(V2) // Slider - Brightnes 156 | { 157 | bright = param.asLong(); 158 | } 159 | 160 | BLYNK_WRITE(V3) // switch to the new file 161 | { 162 | nextFile = param.asLong(); 163 | } 164 | 165 | BLYNK_WRITE(V4) // Slider - Clockcolor 166 | { 167 | color = param.asInt(); 168 | CRGB c=CHSV(color*128,255,color); 169 | matrix->setTextColor(matrix->Color(c.r, c.g, c.b)); 170 | } 171 | 172 | BLYNK_WRITE(V5) // switch to the new file 173 | { 174 | record = param.asInt(); 175 | } 176 | 177 | void checkWifi() 178 | { 179 | if (userTaskHandle == 0) { 180 | const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 200 ); 181 | // -- Store the handle of the current task, so that the show task can 182 | // notify it when it's done 183 | // noInterrupts(); 184 | userTaskHandle = xTaskGetCurrentTaskHandle(); 185 | 186 | // -- Trigger the show task 187 | xTaskNotifyGive(checkWifiTaskHandle); 188 | //to thge contrary to the other one we do not wait for the display task to come back 189 | } 190 | } 191 | 192 | void checkWifiTask(void *pvParameters) 193 | { 194 | //Serial.printf("djdj\n"); 195 | 196 | 197 | 198 | const TickType_t xMaxBlockTime = pdMS_TO_TICKS( 500 ); 199 | // -- Run forever... 200 | for(;;) { 201 | // -- Wait for the trigger 202 | ulTaskNotifyTake(pdTRUE,portMAX_DELAY); 203 | 204 | while(1) 205 | { 206 | 207 | /* if((WiFi.status() == WL_CONNECTED)) 208 | { 209 | artnet.begin(NUM_LEDS+160,UNIVERSE_SIZE,1); 210 | if(!Blynk.connected()){ 211 | isBlynkConnected=false; 212 | Blynk.config(auth); 213 | Blynk.connect(); 214 | delay(500); 215 | isTryingToConnect=true; 216 | Serial.println("Trying to connect Blynk"); 217 | while( (!Blynk.connected()) && (blynk_wait(matrixleds, 0*PIXEL_PER_PIN, PIXEL_PER_PIN); 407 | FastLED.addLeds(matrixleds, 1*PIXEL_PER_PIN, PIXEL_PER_PIN); 408 | FastLED.addLeds(matrixleds, 2*PIXEL_PER_PIN, PIXEL_PER_PIN); 409 | FastLED.addLeds(matrixleds, 3*PIXEL_PER_PIN, PIXEL_PER_PIN); 410 | FastLED.addLeds(matrixleds, 4*PIXEL_PER_PIN, PIXEL_PER_PIN); 411 | 412 | fill_solid(matrixleds,NUM_LEDS,CRGB::Black); 413 | FastLED.show(); 414 | 415 | FastLED.setMaxPowerInVoltsAndMilliamps(5, MILLI_AMPS); 416 | 417 | matrix->begin(); 418 | matrix->setTextWrap(true); 419 | //matrix->setBrightness(100); 420 | matrix->setTextColor(colors[color]); 421 | checkWifi(); //launching the process to check the wifi on core 0 422 | } 423 | 424 | bool artnetdisplay=false; 425 | 426 | 427 | ///----------------------------------------LOOP--------------------------------------------- 428 | 429 | void loop() { 430 | if(isBlynkConnected){ 431 | Blynk.run(); 432 | RTCtime(); 433 | } 434 | 435 | if(WiFi.status() != WL_CONNECTED) 436 | {Blynk.disconnect(); 437 | Screen=0; //if connection lost , switch back to sd-card and time on. 438 | MatrixTime=0; 439 | } 440 | 441 | FastLED.setBrightness(BRIGHTNESS); 442 | 443 | 444 | RtcDateTime now = Rtc.GetDateTime(); 445 | printDateTime(now); 446 | if( (Screen==0) && !isTryingToConnect) 447 | {//delay(10); 448 | 449 | Serial.println("SD-Card"); 450 | 451 | if (myFile.available()) 452 | { 453 | myFile.read(readbuffer,NUM_LEDS*sizeof(CRGB)); 454 | memcpy(matrixleds,readbuffer,NUM_LEDS*sizeof(CRGB)); 455 | 456 | if(record==1) 457 | { 458 | //myFile.close(); 459 | Serial.println(myFile.name()); 460 | SD.remove(myFile.name()); 461 | selectNextFile(); 462 | record=0; //otherwise you would erase all the files :) 463 | Serial.println("file deleted"); 464 | } 465 | } 466 | 467 | else 468 | { 469 | myFile.seek(0); 470 | } 471 | 472 | 473 | } 474 | else if(Screen==1 && artnet.running) 475 | { 476 | 477 | if((WiFi.status() == WL_CONNECTED)) 478 | { 479 | Serial.println("Artnet-Wifi"); 480 | 481 | 482 | 483 | 484 | //--------------Recordingpart----------------------------------------- 485 | 486 | while(record==1){ 487 | Blynk.run(); 488 | if(new_record) 489 | { 490 | artnet.nbframeread=0; 491 | artnet.frameslues=0; 492 | artnet.lostframes=0; 493 | if(myFile) 494 | myFile.close(); //if we had one open file it needs to be closed 495 | // memset(filename, 0, 255); 496 | //sprintf(filename,"/%s_%03d.dat",SAVE_NAME,recordingnumber); 497 | //myFile = SD.open(filename, FILE_WRITE); 498 | openNextFileForRecording(); //this will avoid to override existing files 499 | new_record=false; 500 | //checkWifi(); you do not need to do that as this function is a while loop 501 | Serial.println("start new recording"); 502 | } 503 | 504 | if(!new_record) 505 | { 506 | artnet.read(); 507 | if(artnet.nbframeread==0) //because we already did a getframe(); 508 | { 509 | Serial.println("Recording has started"); 510 | Serial.printf("Filename:%s\n",filename); 511 | } 512 | FastLED.show(); 513 | } 514 | } 515 | if(!new_record) 516 | { 517 | myFile.close(); 518 | Serial.println("Recording stopped ..."); 519 | Serial.printf("frames saved:%d frames lues:%d lost frames:%d\n",artnet.nbframeread,artnet.frameslues,artnet.lostframes) ; 520 | Serial.printf("time per frame %f\n",(float)deb/(artnet.nbframeread)); 521 | artnet.nbframeread=0; 522 | artnet.frameslues=0; 523 | artnet.lostframes=0; 524 | deb=0; 525 | stopped=true; 526 | new_record=true; 527 | recordingnumber++; 528 | } 529 | //--------------------------------------------------------- 530 | 531 | if(artnet.read()==1) 532 | memcpy(matrixleds,artnet.getframe(),NUM_LEDS*sizeof(CRGB)); 533 | 534 | artnet.resetsync(); 535 | } 536 | } 537 | 538 | 539 | 540 | 541 | if(MatrixTime==0){ 542 | Serial.println("on"); 543 | matrix->print (datestring); 544 | } 545 | else{ 546 | Serial.println("off"); 547 | matrix->print (blank); 548 | } 549 | 550 | FastLED.show(); 551 | 552 | 553 | 554 | if((WiFi.status() == WL_CONNECTED) && new_record) //to not check the wifi during recording 555 | { 556 | if(!artnet.running){ 557 | artnet.begin(NUM_LEDS+160,UNIVERSE_SIZE,BUFFER_SIZE); //2 558 | } 559 | if(!Blynk.connected()) 560 | { Blynk.disconnect(); 561 | isBlynkConnected=false; 562 | Blynk.config(auth); 563 | Blynk.connect(); 564 | delay(500); 565 | isTryingToConnect=true; 566 | Serial.println("Trying to connect Blynk"); 567 | while( (!Blynk.connected()) && (blynk_waitclear(); 600 | matrix->setCursor(1, 1); 601 | 602 | } 603 | 604 | 605 | void RTCtime(){ 606 | ///----------------------------------------RTC-Set--------------------------------------------- 607 | 608 | configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); 609 | struct tm timeinfo; 610 | 611 | Rtc.Begin(); 612 | 613 | RtcDateTime compiled; 614 | if(getLocalTime(&timeinfo)) 615 | { 616 | 617 | compiled =RtcDateTime( 618 | (uint16_t )timeinfo.tm_year, 619 | (uint8_t) timeinfo.tm_mon, 620 | (uint8_t) timeinfo.tm_mday, 621 | (uint8_t) timeinfo.tm_hour, 622 | (uint8_t) timeinfo.tm_min, 623 | (uint8_t) timeinfo.tm_sec) ; 624 | } 625 | else 626 | { 627 | Serial.println("error"); 628 | } 629 | 630 | 631 | if (!Rtc.IsDateTimeValid()) 632 | { 633 | Serial.println("RTC lost confidence in the DateTime!"); 634 | Rtc.SetDateTime(compiled); 635 | } 636 | 637 | if (!Rtc.GetIsRunning()) 638 | { 639 | Serial.println("RTC was not actively running, starting now"); 640 | Rtc.SetIsRunning(true); 641 | } 642 | 643 | RtcDateTime now = Rtc.GetDateTime(); 644 | if (now < compiled) 645 | { 646 | Serial.println("RTC is older than compile time! (Updating DateTime)"); 647 | Rtc.SetDateTime(compiled); 648 | } 649 | else if (now > compiled) 650 | { 651 | Serial.println("RTC is newer than compile time. (this is expected)"); 652 | } 653 | else if (now == compiled) 654 | { 655 | Serial.println("RTC is the same as compile time! (not expected but all is fine)"); 656 | } 657 | Rtc.SetSquareWavePin(DS1307SquareWaveOut_Low); 658 | 659 | } 660 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-ArtnetWifi-SDreadwrite-clock 2 | With this project it´s possible to drive Ws2812 Leds with Artnet over Wifi (with Jinx! or Glediator) and it´s also possible to switch to the SD-card version where animations can be recorded from Artnet and than be played back. Although a Matrix-clock can be switched on and off over the animations. 3 | 4 | Here you can watch the project in motion: https://youtu.be/k8Fe5WJFNhA 5 | 6 | I am a passionate technician and like to build and create works of art. With the great help of Yves Bazin i got this cool work of art real. For this project you will need the latest artnet library from Yves Bazin which you can find here: 7 | https://github.com/hpwit/artnet 8 | 9 | I bought a Wemos MINI D1 ESP32, ten 16x16 Matrixscreens, a powersupply with 5V/40A and a Real Time Clock (RTC DS1307) Data Log Logger Shield for the WeMos WIFI D1 Mini Board (because it fits perfect in the ESP32 without soldering). All this things you get very cheap on ebay or aliexpress. 10 | 11 | ![image](https://github.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/blob/master/pics/back.JPG) 12 | 13 | 14 | The pin assignment is like in the picture below: 15 | 16 | ![image](https://github.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/blob/master/pics/panels.JPG) 17 | 18 | 19 | If you want the wirelesscontroll with your phone, you have to install Blynk and also to create an account. Download the app of your choise (iOS/Android) https://www.blynk.cc/ The setup is very easy! Only a few buttens and 2 sliders as you can see in the pics: 20 | 21 | ![image](https://github.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/blob/master/pics/blynk1.JPG) 22 | ![image](https://github.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/blob/master/pics/blynk.JPG) 23 | 24 | To get enough space on my ESP32 (D1 Mini) i had to respace the partition. 25 | These are the settings which worked fine with my controller: 26 | in Arduino >> hardware >>espressif>>esp32>>partitions you have a file default.csv make a copy of that file 27 | then copy that instead inside the "default.csv" 28 | 29 | #Name, Type, SubType, Offset, Size, Flags 30 | 31 | nvs, data, nvs, 0x9000, 0x5000, 32 | 33 | otadata, data, ota, 0xe000, 0x2000, 34 | 35 | app0, app, ota_0, 0x10000, 0x200000, 36 | 37 | app1, app, ota_1, 0x210000, 0x101000, 38 | 39 | eeprom, data, 0x99, 0x311000, 0x1000, 40 | 41 | spiffs, data, spiffs, 0x312000, 0xEE000, 42 | 43 | //it will reduce the OTA and the SPIFF size and extend the flash size to 3mo when it will compile 44 | 45 | then we need to tell Arduino that you have increase that, open the file 46 | 47 | arduino>>hardware>>expressif>> board.txt 48 | 49 | find you board name for instance I am using "ESP32 Dev module" 50 | 51 | esp32.name=ESP32 Dev Module 52 | 53 | esp32.upload.tool=esptool 54 | 55 | esp32.upload.maximum_size=2097152 <= ,normally you have 1310720 56 | 57 | so instead of 1310720 put 2097152 58 | 59 | ------------------------------------------------------------------------------------------------------ 60 | And here it is, the mounted matrixscreen on the wall. Isn´t it beautiful? :) 61 | 62 | ![image](https://github.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/blob/master/pics/wall2.jpg) 63 | 64 | 65 | I am open for every idea or improvements to the code. 66 | If you wanna see more of my artobjects you are welcome to visit my homepage: https://kunstkraft.jimdo.com/ 67 | -------------------------------------------------------------------------------- /pics/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/46b1e3cfcda526dfcbac39a03c4ccee5b135bc32/pics/Thumbs.db -------------------------------------------------------------------------------- /pics/back.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/46b1e3cfcda526dfcbac39a03c4ccee5b135bc32/pics/back.JPG -------------------------------------------------------------------------------- /pics/blynk.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/46b1e3cfcda526dfcbac39a03c4ccee5b135bc32/pics/blynk.JPG -------------------------------------------------------------------------------- /pics/blynk1.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/46b1e3cfcda526dfcbac39a03c4ccee5b135bc32/pics/blynk1.JPG -------------------------------------------------------------------------------- /pics/panels.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/46b1e3cfcda526dfcbac39a03c4ccee5b135bc32/pics/panels.JPG -------------------------------------------------------------------------------- /pics/wall2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/weicks/ESP32-ArtnetWifi-SDreadwriteclock/46b1e3cfcda526dfcbac39a03c4ccee5b135bc32/pics/wall2.jpg --------------------------------------------------------------------------------