initial commit
[Serial_RGB_Controller] / Serial_RGB / Serial_RGB.ino
1 /*
2  * Serial LED Controller, IM Dendrite module (for ESP8266)
3  * Created for Interplaymediumâ„¢ project (https://interplaymedium.org)
4  * Copyright Â© 2016 Dmitry Shalnov [interplaymedium.org]
5  * Licensed under the Apache License, Version 2.0
6 */
7
8 #define SERIAL                  0
9
10 #include "../../info"
11 #include "version.c"
12
13 #include <NTPClient.h>
14
15 #include <ESP8266WiFi.h>
16 #include <WiFiClient.h>
17 #include <ESP8266WebServer.h>
18 #include <ESP8266mDNS.h>
19 #include <EEPROM.h>
20
21 #define LED_STRIPE              WS2811  // WS2812B // APA106 // type of LEDs            Supported LED chipsets: http://fastled.io/docs/3.1/md__r_e_a_d_m_e.html
22
23 // -------------------- FastLED settings  --------------
24
25 #define FASTLED_ESP8266_RAW_PIN_ORDER
26 #define FASTLED_INTERNAL                                // to remove #pragma diagnostics messages
27
28 #include <FastLED.h>
29                                         // Serial       // Switch 4 channel
30 #define DATA_PIN                3       // 3            // 2                    // DIn controller pin (GPIO2) TODO: switch to GPIO0 + pull down (to avoid blink on reset)
31 #define ENABLE_GATE_PIN         2       // 2            // 3                    // common ping for SN74HC08 AND gate (to prevent blink on power on + pull up the level)
32 #define MAX_NUM_LEDS            256                                             // maximum number of leds in stripe
33 #define DEF_NUM_LEDS            24                                              // default number of leds in stripe 
34 #define DEF_MAIN_DELAY          1                                               // default FX delay (in loop)
35
36 #define FULL_OUTPUT             2                       // default quiet level
37
38 CRGB            LEDStripe_RGB[ MAX_NUM_LEDS ];          // This is an array of leds.  One item for each led in your stripe.
39 CHSV            color_HSV;
40 String          param;
41
42 uint8_t         paramCnt = 0;
43 uint8_t         JSONon = 0;
44
45 uint8_t         numberOfLEDs    = DEF_NUM_LEDS;
46
47 uint16_t        mainDelay = 0, mainDelayNew = 0;
48
49 unsigned char   FX_PhaseXDelay = 0, FX_PhaseYDelay = 0, FX_PhaseZDelay = 0;
50
51 // uint8_t      EXP[ 256 ];     // lookup table 
52
53 unsigned long   timeStamp = 0;
54 uint8_t         changeColor = 0, quiet = FULL_OUTPUT, commandReboot = false;
55
56 // -------------------- EEPROM addresing ---------------
57
58 #define EEPROM_HOST             0
59
60 #define EEPROM_HOST_MAX_LEN     57      // Host name max length, can't be > 57 (could be 64, buth there is a bug in avahi https://github.com/lathiat/avahi/issues/273)
61 #define EEPROM_OTHER_MAX_LEN    13      // total size of other EEPROM parameters (summ of all EEPROM values below, included EEPROM_FLAG)
62
63
64 #define EEPROM_FLAG             EEPROM_HOST_MAX_LEN + 1 // EEPROM has been changed (avoid overlap: EEPROM_HOST_MAX_LEN)
65
66 #define EEPROM_numberOfLEDs     EEPROM_HOST_MAX_LEN + 2
67
68 #define EEPROM_HgradBegin       EEPROM_HOST_MAX_LEN + 3 //  (the order of defines matter)
69 #define EEPROM_SgradBegin       EEPROM_HOST_MAX_LEN + 4
70 #define EEPROM_VgradBegin       EEPROM_HOST_MAX_LEN + 5
71
72 #define EEPROM_HgradEnd         EEPROM_HOST_MAX_LEN + 6
73 #define EEPROM_SgradEnd         EEPROM_HOST_MAX_LEN + 7
74 #define EEPROM_VgradEnd         EEPROM_HOST_MAX_LEN + 8
75
76 #define EEPROM_PERIOD           EEPROM_HOST_MAX_LEN + 9
77
78 #define EEPROM_FX_PhaseX        EEPROM_HOST_MAX_LEN + 10
79 #define EEPROM_FX_PhaseY        EEPROM_HOST_MAX_LEN + 11
80 #define EEPROM_FX_PhaseZ        EEPROM_HOST_MAX_LEN + 12
81
82 #define EEPROM_mainDelayNew     EEPROM_HOST_MAX_LEN + 13
83
84
85 // ------------------- array of parameters ------------- (the order of defines matter)
86
87 #define NUMBER_OF_PARAM         10
88
89 #define H_GRAD_BEG              0 
90 #define S_GRAD_BEG              1
91 #define V_GRAD_BEG              2
92 #define H_GRAD_END              3
93 #define S_GRAD_END              4
94 #define V_GRAD_END              5
95 #define PERIOD                  6
96 #define PHASE_X_INC             7
97 #define PHASE_Y_INC             8
98 #define PHASE_Z_INC             9
99
100 float   FX_Arr[ NUMBER_OF_PARAM ]       = {};
101 float   FX_ArrNew[ NUMBER_OF_PARAM ]    = {};
102 float   FX_Coeff[ NUMBER_OF_PARAM ]     = {};
103 float   Phase[ 3 ]                      = {};
104
105 float   FX_CoeffMax                     = 1.0;
106
107 #define PHASE_X                 0
108 #define PHASE_Y                 1
109 #define PHASE_Z                 2
110
111
112 // ------------------- server settings -----------------
113
114 #define HOST_DEFAULT            "im_serial_"
115 #define NTP_SERVER              "europe.pool.ntp.org"
116
117 char host[ EEPROM_HOST_MAX_LEN ];
118
119 // const char* ssid     = "axod_ap";
120 // const char* password = "superstrongpassword"
121
122 const char* ssid        = IM_WIFI_SSID;
123 const char* password    = IM_WIFI_PASS;
124
125 ESP8266WebServer        server(80);
126
127 String                  hostDefURI;
128 String                  HTTPOut;
129 String                  HTTPErr;
130
131 WiFiUDP                 ntpUDP;
132 NTPClient               timeClient(ntpUDP, NTP_SERVER , 0, 60000);
133
134 // ---------------- EEPROM String r/w ------------------
135
136 void EEPROMStrRead( uint8_t addr, char * str ){
137         for (uint8_t a = addr; a <  EEPROM_HOST_MAX_LEN; a++ ){
138                 str[ a ] = EEPROM.read( a );
139                 if (str[ a ] == 0) break;
140         }
141 }
142
143 void EEPROMStrWrite( uint8_t addr, char * str ){
144         uint8_t a = 0;
145         for (a = addr; a <  EEPROM_HOST_MAX_LEN; a++ ){
146                 EEPROM.write( a, str[ a ] );
147                 if (str[ a ] == 0) break;
148         }
149         EEPROM.write( a, 0 );
150 }
151
152 // ---------------- misc -------------------------------
153
154 int str2HEX(const String str) { 
155         return strtol( str.c_str(), 0, 16 );
156 }
157
158 String printNum(unsigned long num, int base, char sign) {
159
160         char outbuf[12];
161
162         int i = 12;
163         int j = 0;
164         int n;
165
166         do {
167                 outbuf[i] = "0123456789ABCDEF"[num % base];
168                 i--;
169                 num = num/base;
170         } while ( num > 0 );
171
172         if ( sign != ' ' ){
173                 outbuf[0] = sign;
174                 ++j;
175         }
176
177         while ( ++i < 13 ){
178                 outbuf[j++] = outbuf[i];
179         }
180
181         if ( j == 1 ) { outbuf[ 1 ] = outbuf[ 0 ]; outbuf[ 0 ] = '0'; ++j; }
182
183         outbuf[j] = 0;
184
185         return outbuf;
186
187 }
188
189 uint8_t is_number( const char *s ){
190
191         while (*s) { 
192                 Serial.printf("s: %c\n", *s);
193                 if ( *s++ <48 || *s >57 ) return 0;
194         }
195         return true;
196 }
197
198 // ----------- explode for selected substring -----------
199
200 String expld( String str, unsigned int numb, char delimiter ){
201         
202         unsigned int cnt = 0, a = 0, p2 = 0, p1 = 0, lng = 0;
203
204         lng = str.length();
205
206         for ( a = 0; a < lng; a++ ){
207                 if ( str.charAt( a ) == delimiter ) { 
208                         p2 = p1;
209                         p1 = a;
210                         if ( cnt == numb ) break;
211                         cnt ++;
212                 }
213         }
214
215         if ( cnt < numb ) return "";
216
217         if ( a == lng ) { 
218                 p2 = p1;
219                 p1 = lng;
220         }
221
222         if ( numb > 0 ) p2 ++;
223
224         return str.substring(p2, p1);
225         
226 }
227
228 // ---------------- FX ---------------------------------
229
230 void FX_Gradient_RT( uint8_t HgradBegin, uint8_t SgradBegin, uint8_t VgradBegin, uint8_t HgradEnd, uint8_t SgradEnd, uint8_t VgradEnd, uint8_t period, uint8_t phaseX, uint8_t phaseY, uint8_t phaseZ ) {
231
232         unsigned int    i = 0;
233         CHSV            color;
234
235         #define         Y_PHASE_SHIFT   128     // 128 // to avoid solid fill on phase = 0
236
237         // NOTE! phaseZ is for color component only since it is cyclic
238
239         for (i = 0; i < numberOfLEDs; i++) {
240
241 //              newHueColor = pow( (float)sin( (float)i*PI / (period * 2) ) ,2 ) * (gradEnd - gradBegin) + gradBegin;           // MEMO! floating point
242
243                 if ( HgradEnd != HgradBegin ) color.h = ( ((int)cos8( (i + phaseX) * 128 / period ) - 128) * ((int)sin8( (phaseY + Y_PHASE_SHIFT) * 128 / period ) - 128) / 128 + 128 ) / ( 256 / ( HgradEnd - HgradBegin ) ) + HgradBegin; else color.h = HgradEnd;    
244                 if ( SgradEnd != SgradBegin ) color.s = ( ((int)cos8( (i + phaseX) * 128 / period ) - 128) * ((int)sin8( (phaseY + Y_PHASE_SHIFT) * 128 / period ) - 128) / 128 + 128 ) / ( 256 / ( SgradEnd - SgradBegin ) ) + SgradBegin; else color.s = SgradEnd;
245                 if ( VgradEnd != VgradBegin ) color.v = ( ((int)cos8( (i + phaseX) * 128 / period ) - 128) * ((int)sin8( (phaseY + Y_PHASE_SHIFT) * 128 / period ) - 128) / 128 + 128 ) / ( 256 / ( VgradEnd - VgradBegin ) ) + VgradBegin; else color.v = VgradEnd;
246
247 //              LEDStripe_RGB[ i ] = CHSV( color.h + phaseZ , color.s, color.v );
248
249                 color.h += phaseZ;
250                 hsv2rgb_spectrum(color, LEDStripe_RGB[ i ]);
251
252         }
253 }
254
255 // -----------------------------------------------------
256 // --------------- Init --------------------------------
257 // -----------------------------------------------------
258
259 void setup(void) {
260
261         HTTPOut.reserve(800); // lenght of Help message generally
262
263         hostDefURI.reserve( EEPROM_HOST_MAX_LEN );
264         hostDefURI = HOST_DEFAULT + WiFi.macAddress().substring(12, 14) + WiFi.macAddress().substring(15, 17); // 00:00:00:00:00:00
265
266         // calculate lookup array 
267  
268 //      for (unsigned int a = 1; a <= 255; a++) EXP[ a ] = round( pow( 255, (double)a / (255) ) ); // calculate exponential lookup array (for PWM etc..)
269
270         // reset FX_Coeff (maybe not required)
271
272         for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_Coeff[ a ] = 1.0;
273
274 #if SERIAL == 1
275
276         // Serial init
277
278         Serial.begin(115200);
279         Serial.println();
280         Serial.println("Booting Sketch...");
281 #endif
282
283         // read parameters from EEPROM (if it has been saved early)
284
285         EEPROM.begin( EEPROM_HOST_MAX_LEN + EEPROM_OTHER_MAX_LEN );
286         
287         if ( EEPROM.read( EEPROM_FLAG ) == 1 ){
288
289                 EEPROMStrRead( EEPROM_HOST, host ); 
290
291                 numberOfLEDs    = EEPROM.read( EEPROM_numberOfLEDs );
292                 mainDelayNew    = EEPROM.read( EEPROM_mainDelayNew );
293
294                 for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_ArrNew[ a ]  = EEPROM.read( EEPROM_HgradBegin + a );
295
296                 changeColor = true;
297
298         } else {
299
300                 FX_ArrNew[ PERIOD ] = DEF_NUM_LEDS;
301                 strcpy( host, (char*)hostDefURI.c_str() ); // default URI and host name
302         }
303
304         // Fast LED RGB init
305
306         pinMode(ENABLE_GATE_PIN, OUTPUT);
307         digitalWrite( ENABLE_GATE_PIN, HIGH );
308
309         delay(1000);  // 2000 // sanity check delay - allows reprogramming if accidently blowing power w/leds
310
311         FastLED.addLeds<LED_STRIPE, DATA_PIN, GRB>( LEDStripe_RGB, numberOfLEDs );
312         fill_solid( LEDStripe_RGB, numberOfLEDs, CRGB(0, 0, 0) );
313         FastLED.show();
314
315         // WiFi init 
316
317         if ( host == "" ) strcpy( host, (char*)hostDefURI.c_str() );  // if it was changed without parameters by mistake
318
319         WiFi.hostname( host );
320
321 //      WiFi.softAP(APssid, APpassword);
322 //      WiFi.mode(WIFI_AP);
323 //      WiFi.mode(WIFI_AP_STA); 
324
325         WiFi.mode(WIFI_STA);
326         WiFi.setAutoReconnect( true );
327         WiFi.begin(ssid, password);
328
329         // main events handlers
330
331         if (WiFi.waitForConnectResult() == WL_CONNECTED) {
332
333                 MDNS.begin( host );
334
335                 // get time 
336
337 //              timeClient.setTimeOffset(0);
338                 timeClient.begin();
339                 timeClient.update();
340
341                 // start server 
342
343                 server.begin();
344                 MDNS.addService("http", "tcp", 80);
345
346 #if SERIAL == 1
347                 Serial.printf("Ready. Try \"curl %s/help\" to see the list of commands and parameters.\n", host);
348 #endif
349
350                 server.onNotFound( []() {
351
352                         server.sendHeader("Connection", "close");
353
354                         param           = "";
355 //                      HTTPOut         = "";
356
357                         // no command given (see handlers of commands below)
358
359                         if ( server.uri() != "/" )HTTPErr += "Command '" + server.uri() + "' not exist.\n";
360
361
362                         for ( uint8_t i=0; i < server.args(); i++ ){
363
364                                 paramCnt = 0;
365
366                                 // ------------- specific parameters -----------------
367
368                                 // change number of leds in stripe 
369         
370                                 if ( server.argName(i) == "number" ) {
371
372                                         // TODO make it in MAIN with dimming
373
374                                         fill_solid( LEDStripe_RGB, numberOfLEDs, CRGB(0, 0, 0) );
375                                         FastLED.show();
376
377                                         numberOfLEDs    = server.arg("number").toInt();
378                                         FastLED.addLeds<LED_STRIPE, DATA_PIN, GRB>( LEDStripe_RGB, numberOfLEDs );
379
380                                         paramCnt++;
381                                 }
382
383                                 // color gradient (hex)
384
385                                 if ( server.argName(i) == "gradient" ) {
386
387                                         param = server.arg("gradient");
388
389                                         for ( uint8_t a = 0; a < 6; a++ ) FX_ArrNew[ H_GRAD_BEG + a ] = str2HEX( param.substring( a*2, a*2+2) );
390
391                                         changeColor = true;
392                                         paramCnt++;
393
394                                 }
395
396                                 // sin/cos period (dec)
397
398                                 if ( server.argName(i) == "period" ) {
399
400                                         FX_ArrNew[ PERIOD ] = server.arg("period").toInt(); 
401
402                                         changeColor = true;
403                                         paramCnt++; 
404                                 }
405
406
407                                 // pattern motion parameters (dec)
408
409                                 if ( server.argName(i) == "slidex" ) { 
410                                         FX_ArrNew [ PHASE_X_INC ] = (float)server.arg("slidex").toInt(); 
411                                         changeColor = true;
412                                         paramCnt++; 
413                                 }
414
415                                 if ( server.argName(i) == "slidey" ) { 
416                                         FX_ArrNew [ PHASE_Y_INC ] = (float)server.arg("slidey").toInt(); 
417                                         changeColor = true;
418                                         paramCnt++; 
419                                 }
420
421                                 if ( server.argName(i) == "slidez" ) { 
422                                         FX_ArrNew [ PHASE_Z_INC ] = (float)server.arg("slidez").toInt(); 
423                                         changeColor = true;
424                                         paramCnt++; 
425                                 }
426
427
428                                 // delay between frames
429
430                                 if ( server.argName(i) == "delay" ) { 
431                                         mainDelayNew = server.arg("delay").toInt(); 
432 //                                      changeColor = true;
433                                         paramCnt++; 
434                                 }
435
436                                 // ------------- common parameters -------------------
437
438                                 // debug data {Number R G B H S V}
439
440                                 if ( server.argName(i) == "debug" ){ // || param != 0
441
442                                         HTTPOut += "Stripe [debug]: ";
443
444                                         for (unsigned int i = 0; i < numberOfLEDs; i++) { 
445                                                 color_HSV = rgb2hsv_approximate( LEDStripe_RGB[ i ] ); 
446                                                 HTTPOut += String( i ) + "\t" + String( LEDStripe_RGB[ i ].r ) + "\t" + String( LEDStripe_RGB[ i ].g ) + "\t" + String( LEDStripe_RGB[ i ].b ) + "\t" + String( color_HSV.h ) + "\t" + String( color_HSV.s ) + "\t" + String( color_HSV.v ) + "\n"; 
447                                         } 
448
449                                         paramCnt++;
450                                 }
451
452                                 // set time 
453
454                                 if ( server.argName(i) == "time" ){ // || param != 0
455
456                                         if ( is_number( server.arg("time").c_str() ) ) { 
457                                                 timeClient.update();
458                                                 timeStamp = strtol( server.arg("time").c_str(), 0, 10 );
459                                         } else {
460                                                 HTTPErr += "Parameter 'time' must be specified in UNIX timestamp format.\n"; // 1631848103
461                                         }
462
463                                         paramCnt++;
464                                 }
465
466                                 // rename host 
467
468                                 if ( server.argName(i) == "host" ) {
469
470                                         if ( server.arg("host") == "" ) {
471
472                                                 HTTPErr += "Please specify new unit hostname.\n";
473
474                                         } else if ( server.arg("host").length() > EEPROM_HOST_MAX_LEN ) { 
475
476                                                 HTTPErr += "Unit hostname can not exceed " + String( EEPROM_HOST_MAX_LEN ) + " symbols.\n";
477
478                                         } else {
479
480                                                 strcpy( host, (char*)server.arg("host").c_str() ); // host = (char *)server.arg("host").c_str();
481                                                 WiFi.hostname( host );
482                                                 MDNS.begin( host );
483
484                                                 HTTPOut +=  "The host name has been changed to '" + String( host ) + "'. Do not forget to save current settings using the '/save' command.\n";
485                                         }
486
487                                         paramCnt++;
488                                 }
489
490                                 // save all parameters to EEPROM 
491
492                                 if (server.argName(i) == "save" ){
493
494                                         EEPROM.write( EEPROM_numberOfLEDs, numberOfLEDs );
495                                         EEPROM.write( EEPROM_mainDelayNew, mainDelayNew );
496
497                                         for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) EEPROM.write( EEPROM_HgradBegin + a, (uint8_t)roundf( FX_Arr[ a ] ) );
498
499                                         EEPROMStrWrite( EEPROM_HOST, host );
500                                         EEPROM.write( EEPROM_FLAG, 1 ); // EEPROM changed
501                                         EEPROM.commit();
502
503                                         HTTPOut +=  "The current settings are saved.\n";
504
505                                         paramCnt++;
506                                 }
507
508                                 // reset to default parameters
509
510                                 if ( server.argName(i) == "reset" ){
511
512                                         numberOfLEDs            = DEF_NUM_LEDS;
513                                         mainDelayNew            = DEF_MAIN_DELAY;
514
515                                         for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_ArrNew[ a ] = 0;
516
517                                         FX_Arr[ PERIOD ]        = DEF_NUM_LEDS;
518                                         FX_ArrNew[ PERIOD ]     = DEF_NUM_LEDS;
519
520                                         strcpy( host, (char*)hostDefURI.c_str() );
521                                         WiFi.hostname( host );
522
523                                         server.begin();
524                                                 
525                                         if ( quiet > 1 ) HTTPOut += "All parameters are set to default values. Don't forget to /save them.\n";
526
527                                         paramCnt++;
528
529                                         changeColor = true;
530
531                                 }
532
533                                 // reboot
534
535                                 if ( server.argName(i) == "reboot" ){
536
537                                         FX_ArrNew[ V_GRAD_BEG ] = 0;
538                                         FX_ArrNew[ V_GRAD_END ] = 0;
539
540                                         mainDelayNew = DEF_MAIN_DELAY;
541
542                                         if ( quiet > 1 ) {
543
544                                                 server.send_P(200, "text/plain", PSTR("Unit rebooting...\n") );
545                                                 server.begin();
546                                         }
547
548                                         paramCnt++;
549
550                                         changeColor = true;
551                                         commandReboot = true;
552                                 }
553
554                                 // quiet, level of output
555
556                                 if ( server.argName(i) == "quiet" ) {
557
558                                         quiet = str2HEX( server.arg("quiet") );
559                                         paramCnt++;
560                                 } else {
561                                         quiet = FULL_OUTPUT;
562                                 }
563
564                                 // json on
565
566                                 if ( server.argName(i) == "json" ) {
567                                         JSONon = true;
568                                         paramCnt++;
569                                 }
570
571                                 if ( !paramCnt ) HTTPErr += "Parameter '" + server.argName(i) + "' not exist.\n";
572                         }
573
574
575                         // ---------------- host default return ------------------------
576
577                         if (HTTPErr != "" ) {   
578
579                                 // list of errors 
580
581                                 String Err = "";
582
583                                 paramCnt = 0;
584
585                                 if (quiet > 0) { 
586
587                                         if ( JSONon ) {
588
589                                                 HTTPOut += "{[";
590
591                                                 while ( 1 ){
592                                                         Err = expld( HTTPErr, paramCnt, '\n' );
593                                                         if ( Err != "" ) HTTPOut += "\"" + Err  + "\""; else break; 
594                                                         if ( expld( HTTPErr, paramCnt+1, '\n' ) != "" ) HTTPOut += ",";
595                                                         paramCnt ++;
596                                                 }
597
598                                                 HTTPOut += "]}";
599
600                                         } else {
601
602                                                 while ( 1 ){
603                                                         Err = expld( HTTPErr, paramCnt, '\n' );
604                                                         if ( Err != "" ) HTTPOut += "Error: " + Err + "\n"; else break;
605                                                         paramCnt ++;
606                                                 }
607 //                                              HTTPOut += "See '/help' for details\n";
608                                         }
609                                 }
610
611                         } else {
612
613                                 // standart output 
614
615                                 if ( quiet > 1 && HTTPOut == "" ) { 
616
617                                         if ( JSONon ){
618
619                                                 HTTPOut =  "{";
620                                                 HTTPOut += "\"MAC\":\""                 + String( WiFi.macAddress() ) + "\",\n";
621                                                 HTTPOut += "\"Host\":\""                + String( host ) + "\",\n";
622                                                 HTTPOut += "\"Timestamp\":\""           + String( timeClient.getEpochTime() ) + "\",\n";
623                                                 HTTPOut += "\"Target_timestamp\":\""    + String( timeStamp ) + "\",\n";
624
625                                                 HTTPOut += "\"TNumber_of_LEDs\":\""     + String( numberOfLEDs ) + "\",\n";
626
627                                                 HTTPOut += "\"SlideX:\":\""             + String( (int)roundf( FX_ArrNew[ PHASE_X_INC ] ) ) + "\",\n";
628                                                 HTTPOut += "\"SlideY:\":\""             + String( (int)roundf( FX_ArrNew[ PHASE_Y_INC ] ) ) + "\",\n";
629                                                 HTTPOut += "\"SlideZ:\":\""             + String( (int)roundf( FX_ArrNew[ PHASE_Z_INC ] ) ) + "\",\n";
630
631                                                 HTTPOut += "\"Period:\":\""             + String( (int)roundf( FX_ArrNew[ PERIOD ] ) ) + "\",\n";
632                                                 HTTPOut += "\"Delay:\":\""              + String( mainDelayNew ) + "\",\n";
633
634                                                 HTTPOut += "\"Gradient_HSV:\":\"";
635
636                                                 for ( uint8_t a = 0; a < 6 ; a++ ) HTTPOut +=  String( printNum( FX_ArrNew[ a ], 16, ' ') );
637
638                                                 HTTPOut += "\",\n";
639
640                                                 HTTPOut += "}";
641
642                                         } else { 
643
644                                                 HTTPOut =  "MAC:"               + String( WiFi.macAddress() ) + "\n";
645                                                 HTTPOut += "Host:"              + String( host ) + "\n";
646                                                 HTTPOut += "Timestamp:"         + String( timeClient.getEpochTime() ) + "\n";
647                                                 HTTPOut += "Target timestamp:"  + String( timeStamp ) + "\n";
648
649                                                 HTTPOut += "Number of LEDs:"    + String( numberOfLEDs ) + "\n";
650
651                                                 HTTPOut += "Slide X:"           + String( (int)roundf( FX_ArrNew[ PHASE_X_INC ] ) ) + "\n";
652                                                 HTTPOut += "Slide Y:"           + String( (int)roundf( FX_ArrNew[ PHASE_Y_INC ] ) ) + "\n";
653                                                 HTTPOut += "Slide Z:"           + String( (int)roundf( FX_ArrNew[ PHASE_Z_INC ] ) ) + "\n";
654
655                                                 HTTPOut += "Period:"            + String( (int)roundf( FX_ArrNew[ PERIOD ] ) ) + "\n";
656                                                 HTTPOut += "Delay:"             + String( mainDelayNew ) + "\n";
657
658                                                 HTTPOut += "Gradient (HSV):";
659
660                                                 for ( uint8_t a = 0; a < 6 ; a++ ) HTTPOut += String( printNum( roundf( FX_ArrNew[ a ] ), 16, ' ') ) + " ";                             
661
662                                                 HTTPOut += "\n";
663                                         
664                                         }
665                                 } 
666                         }
667
668                         server.send(200, "text/plain", HTTPOut );
669
670                         HTTPOut         = "";
671                         HTTPErr         = "";
672                         JSONon          = 0;
673
674                 });
675
676                 // ui interface TODO xml inteface
677
678                 server.on("/ui", HTTP_GET, []() {
679                         HTTPOut = "User interface. Not yet implemented.\n";
680                         server.send(200, "text/plain", HTTPOut);
681                 });
682
683                 // help
684
685                 server.on("/help", HTTP_GET, []() {
686
687                         HTTPOut =       "Interplay Medium ESP8266 Serial LED Controller. Version: " + String(VERSION) + "\n";
688                         HTTPOut +=      "Created by Dmitry Shalnov (c) 2017. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. \n\n";
689
690                         HTTPOut +=      "Parameters:\n";
691
692                         HTTPOut +=      "       ?number=        Number of LEDs in stripe (max MAX_NUM_LEDS)\n";
693                         HTTPOut +=      "       ?gradient=      Start and end colors of gradient (hex, 24bit/color in HLS format, e.g. 001122FFEECC)\n";
694                         HTTPOut +=      "       ?period=        Sine period (number of leds)\n";
695                         HTTPOut +=      "       ?delay=         Delay of state changing, integer\n";
696
697                         HTTPOut +=      "       ?slidex=        Step of X shift per cycle, signed integer\n";
698                         HTTPOut +=      "       ?slidey=        Step of Y shift per cycle, signed integer\n";
699                         HTTPOut +=      "       ?slidez=        Step of Z shift per cycle, signed integer\n";
700
701                         HTTPOut +=      "       ?time=          Target timestamp (when the changes will take effect, good for synch), decimal UNIX Timestamp\n\n";
702
703                         HTTPOut +=      "       ?host=          Rename the unit\n";
704                         HTTPOut +=      "       ?quiet=         Level of output (0/empty = no output, 1 = OK/Error)\n\n";
705
706                         HTTPOut +=      "Commands:\n";
707
708                         HTTPOut +=      "       ?save           Save current settings\n";
709                         HTTPOut +=      "       ?reset          Reset to initial settings\n";
710                         HTTPOut +=      "       ?reboot         Reboot unit\n";
711
712                         HTTPOut +=      "       \n";
713                         HTTPOut +=      "       /ui             Output in XML UI format\n";
714                         HTTPOut +=      "       /json           Output in JSON format\n";
715                         HTTPOut +=      "       /update         Wireless update of firmware (see example below)\n";
716                         HTTPOut +=      "       /help           This help\n\n";
717
718                         HTTPOut +=      "Usage:         curl " + String(host) + "?<parameter>=<value>\n";
719                         HTTPOut +=      "               curl " + String(host) + "/<command>\n";
720                         HTTPOut +=      "Examples:      curl \"" + String(host) + "?gradient=ffffffddff00&period=30\" \n";
721                         HTTPOut +=      "               curl -F image=@firmware.bin " + String(host) + "/update \n";
722
723                         server.sendHeader("Connection", "close");
724                         server.send( 200, "text/plain", HTTPOut );
725
726                 });
727
728                 // firmware update
729
730                 server.on("/update", HTTP_POST, []() {
731
732                         server.sendHeader("Connection", "close");
733                         server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
734
735                         ESP.restart();
736
737                         }, []() {
738
739                                 HTTPUpload& upload = server.upload();
740
741                                 if (upload.status == UPLOAD_FILE_START) {
742                                 
743 #if SERIAL == 1
744                                         Serial.setDebugOutput(true);
745 #endif
746                                         WiFiUDP::stopAll();
747
748 #if SERIAL == 1
749                                         Serial.printf("Update: %s\n", upload.filename.c_str());
750 #endif
751                                         uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
752
753                                         if (!Update.begin(maxSketchSpace)) { //start with max available size
754 #if SERIAL == 1
755                                                 Update.printError(Serial);
756 #endif
757                                         }
758                                 } else if (upload.status == UPLOAD_FILE_WRITE) {
759                                         if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
760 #if SERIAL == 1
761                                                 Update.printError(Serial);
762 #endif
763                                         }
764                                 } else if (upload.status == UPLOAD_FILE_END) {
765                                         if (Update.end(true)) { //true to set the size to the current progress
766
767                                                 server.sendHeader("Connection", "close");
768                                                 server.send_P(200, "text/plain", PSTR("Success. Please wait until device replace firmware and boot up...\n") );
769 #if SERIAL == 1
770                                                 Serial.printf("Update Success: %u\nRebooting....\n", upload.totalSize); 
771 #endif
772                                         } else {
773                                                 server.sendHeader("Connection", "close");
774                                                 server.send_P(200, "text/plain", PSTR("Something went wrong. Please reset device and try again.\n") );
775 #if SERIAL == 1
776                                                 Update.printError(Serial);
777 #endif
778                                         }
779 #if SERIAL == 1
780                                         Serial.setDebugOutput(false);
781 #endif
782                                 }
783
784                                 yield();
785                 });
786
787         } else {
788 #if SERIAL == 1
789                 Serial.println("WiFi Failed");
790 #endif
791         }
792 }
793
794 // -----------------------------------------------------
795 // ------------------- MAIN ----------------------------
796 // -----------------------------------------------------
797
798 // uint8_t rebootColorsCnt = 0;
799
800 void loop(void) {
801
802         server.handleClient();
803         MDNS.update();
804
805         // reconnect
806
807         if ( WiFi.status() != WL_CONNECTED ) {
808 #if SERIAL == 1
809                 Serial.printf(".");
810 #endif
811                 if (WiFi.waitForConnectResult() == WL_CONNECTED) {
812 #if SERIAL == 1
813                         Serial.println("Reconnected");
814 #endif
815                         MDNS.begin( host );
816
817                         timeClient.begin();
818                         timeClient.update();
819                 }
820
821         } 
822
823         if ( changeColor ) {
824
825                 // set float coefficents
826
827                 for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) {
828                         FX_Coeff[ H_GRAD_BEG + a ] = (float)FX_ArrNew[ H_GRAD_BEG + a ] - FX_Arr[ H_GRAD_BEG + a ];
829                         if ( abs( FX_Coeff[ H_GRAD_BEG + a ] ) > FX_CoeffMax ) FX_CoeffMax = abs( FX_Coeff[ H_GRAD_BEG + a ] );
830                 }
831
832                 for ( uint8_t a = 0; a < 6; a++ ) FX_Coeff[ H_GRAD_BEG + a ] = FX_Coeff[ H_GRAD_BEG + a ] / FX_CoeffMax;
833
834                 changeColor = false;
835
836 #if SERIAL == 1
837                 Serial.printf("FX_ArrNew: "); 
838                 for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_ArrNew[ a ] );
839                 Serial.printf("\n");
840                 Serial.printf("FX_Coeff: "); 
841                 for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_Coeff[ a ] );
842                 Serial.printf("\n");
843 #endif
844
845         } else {
846
847                 if ( ++mainDelay == mainDelayNew /* && FX_Delay != 0 */ ) {
848
849 #if SERIAL == 1
850 //                      Serial.printf("FX_Arr: "); 
851 //                      for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_Arr[ a ] );
852 //                      Serial.printf("\n");
853 //                      Serial.printf("Phase: %f %f %f\n", Phase[ PHASE_X ], Phase[ PHASE_Y ], Phase[ PHASE_Z ] );
854 #endif
855
856                         mainDelay = 0;
857
858                         // fade out and reboot
859
860                         if ( commandReboot && roundf( FX_Arr[ V_GRAD_BEG ] ) == 0 && roundf( FX_Arr[ V_GRAD_END ]) == 0 ) {
861                                 commandReboot = false;
862 #if SERIAL == 1
863                                 Serial.printf("Reboot......\n"); 
864 #endif
865                                 ESP.restart();
866 //                              Serial.printf("Reboot.2...\n"); 
867                         }
868
869                         if ( timeClient.getEpochTime() > timeStamp ) {
870
871                                 // change color 
872
873                                 for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) {
874                                         if ( FX_ArrNew [ a ] != roundf( FX_Arr[ a ] ) ) { FX_Arr[ a ] += FX_Coeff[ a ]; } else FX_Arr[ a ] = (float)FX_ArrNew [ a ];
875                                 }
876                 
877                                 // change phase 
878
879                                 for ( uint8_t a = 0; a < 3; a++ ) {
880
881                                         if ( FX_Arr[ PHASE_X_INC + a ] == 0 ) { 
882                                                 // shift phase to 0 after slide. = 0
883                                                 if ( (uint8_t)round( Phase[ a ] ) != 0 ) Phase[ a ] -= FX_Coeff[ PHASE_X_INC + a ]/100; else Phase[ a ] = 0; 
884                                         } else { 
885                                                 // routine shift phase
886                                                 Phase[ a ] += FX_Arr[ PHASE_X_INC + a ]/100;
887                                         }
888                                 }
889
890                         }
891
892                         if ( FX_Arr[ PERIOD ] == 0 ) FX_Arr[ PERIOD ] = 1;
893
894                         FX_Gradient_RT( roundf( FX_Arr[ H_GRAD_BEG ] ), roundf( FX_Arr[ S_GRAD_BEG ] ), roundf( FX_Arr[ V_GRAD_BEG ] ), roundf( FX_Arr[ H_GRAD_END ] ), roundf( FX_Arr[ S_GRAD_END ] ), roundf( FX_Arr[ V_GRAD_END ] ), roundf( FX_Arr[ PERIOD ] ), roundf( Phase[ PHASE_X ] ), roundf( Phase[ PHASE_Y ] ), roundf( Phase[ PHASE_Z ] ) );
895                         FastLED.show();
896                         delay(3);
897
898                 }
899         }
900 }
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717