initial commit
authorDmitry Shalnoff <dev@shalnoff.com>
Mon, 9 Sep 2024 17:40:30 +0000 (19:40 +0200)
committerDmitry Shalnoff <dev@shalnoff.com>
Mon, 9 Sep 2024 17:40:30 +0000 (19:40 +0200)
.gitignore [new file with mode: 0644]
README.md [new file with mode: 0644]
Serial_RGB/Serial_RGB.ino [new file with mode: 0644]
Serial_RGB/version.c [new file with mode: 0644]
bidirectonal-mosfet-level-shifter.png [new file with mode: 0644]
make [new file with mode: 0755]
serial [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..dab8ca3
--- /dev/null
@@ -0,0 +1,60 @@
+memo
+info
+dev
+todo
+BKP/*
+Serial_RGB_test/*
+
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.bin
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..89a21a1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,102 @@
+# Serial RGB LED driver, Interplay Medium dendrite module (for ESP8266)
+
+This is IM denrite module (remote wifi network device) created for [Interplay Medium™](https://interplaymedium.org) project.
+
+![Interplay Medium RGB Serial LED Dendrite](https://repository.interplaymedium.org/RGBW%20Controller/IM_RGBW_LED_dendrite.jpeg)
+
+## Pinouts 
+
+For LEDs be sure to add MOSFETs and current limiting resistors appropriately. Wiring scheme will be added probably later. 
+
+![ESP8266 Pinout](https://repository.interplaymedium.org/RGBW%20Controller/esppinout_.png)
+![ESP8266 Programming](https://repository.interplaymedium.org/RGBW%20Controller/usbprogram_.png)
+
+## Preparing the building environment
+
+Make sure that you have the environment installed as described at
+
+1. [makeEspArduino.mk](https://github.com/plerup/makeEspArduino.git)
+2. [esp8266 Arduino SDK](https://github.com/esp8266/Arduino)
+
+3. In the *make* script, change path for each variable approprately: 
+    MAKE_FILE=....
+    ESP_SDK_ROOT=....
+
+## Change your IM AXOD microserver or AP (router) WIFI login and password
+
+create the file 
+    vim ../info
+
+assign SSID and PASSWORD of your local IM AXOD microserver or Access Point in there
+
+    WIFI_SSID="ssid"
+    WIFI_PASS="ssid password"
+
+You can change it later whenewer you want using HTTP interface
+
+## Building 
+
+initial buildong and flashing firmware at once 
+    ./make Serial_RGB upload
+
+after that you may just build the binary and uload it using remote HTTP interface
+
+    ./make Serial_RGB  
+    curl -F image=@Serial_RGB.bin -s im_<....>.lan/update
+
+## Usage 
+
+
+By default dendrite can be reached "im_serial_[last 4 digits of MAC address]" doman
+
+Change it with 
+
+    curl "im_serial_[last 4 digits of MAC address]?rename=NEWNAME"
+
+Turn on the color and effect 
+
+    curl "im_serial_[last 4 digits of MAC address]?gradient=ffffffccff44&slidez=-1&delay=100"
+
+Other options
+
+    curl "im_serial_[last 4 digits of MAC address]/help"
+
+## Todo 
+
+The development of this firmware is in progress. Here is a brief list of upcoming changes: 
+
+* features (save, reset....)
+* state return in 2 variants  
+    txt (default)  
+    JSON  
+    html UI
+* add commands  
+    dimm and soft state changing  
+    rgbwdef -- save default values in EEPROM, which is returning on reset command  
+  
+* switching AP/slave, AP by defuault
+    remote access setup (host name, AP/slave, SSID, passw)  
+    save in EEPROM  
+
+## License
+
+Copyright © 2016 Dmitry Shalnov [interplaymedium.org]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this files except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+
+
+
+
diff --git a/Serial_RGB/Serial_RGB.ino b/Serial_RGB/Serial_RGB.ino
new file mode 100644 (file)
index 0000000..8db32e9
--- /dev/null
@@ -0,0 +1,900 @@
+/*
+ * Serial LED Controller, IM Dendrite module (for ESP8266)
+ * Created for Interplaymedium™ project (https://interplaymedium.org)
+ * Copyright © 2016 Dmitry Shalnov [interplaymedium.org]
+ * Licensed under the Apache License, Version 2.0
+*/
+
+#define SERIAL                 0
+
+#include "../../info"
+#include "version.c"
+
+#include <NTPClient.h>
+
+#include <ESP8266WiFi.h>
+#include <WiFiClient.h>
+#include <ESP8266WebServer.h>
+#include <ESP8266mDNS.h>
+#include <EEPROM.h>
+
+#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
+
+// -------------------- FastLED settings  --------------
+
+#define FASTLED_ESP8266_RAW_PIN_ORDER
+#define FASTLED_INTERNAL                               // to remove #pragma diagnostics messages
+
+#include <FastLED.h>
+                                       // Serial       // Switch 4 channel
+#define DATA_PIN               3       // 3            // 2                    // DIn controller pin (GPIO2) TODO: switch to GPIO0 + pull down (to avoid blink on reset)
+#define ENABLE_GATE_PIN                2       // 2            // 3                    // common ping for SN74HC08 AND gate (to prevent blink on power on + pull up the level)
+#define MAX_NUM_LEDS           256                                             // maximum number of leds in stripe
+#define        DEF_NUM_LEDS            24                                              // default number of leds in stripe 
+#define DEF_MAIN_DELAY         1                                               // default FX delay (in loop)
+
+#define FULL_OUTPUT            2                       // default quiet level
+
+CRGB           LEDStripe_RGB[ MAX_NUM_LEDS ];          // This is an array of leds.  One item for each led in your stripe.
+CHSV           color_HSV;
+String                 param;
+
+uint8_t                paramCnt = 0;
+uint8_t                JSONon = 0;
+
+uint8_t                numberOfLEDs    = DEF_NUM_LEDS;
+
+uint16_t       mainDelay = 0, mainDelayNew = 0;
+
+unsigned char  FX_PhaseXDelay = 0, FX_PhaseYDelay = 0, FX_PhaseZDelay = 0;
+
+// uint8_t     EXP[ 256 ];     // lookup table 
+
+unsigned long  timeStamp = 0;
+uint8_t                changeColor = 0, quiet = FULL_OUTPUT, commandReboot = false;
+
+// -------------------- EEPROM addresing ---------------
+
+#define EEPROM_HOST            0
+
+#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)
+#define EEPROM_OTHER_MAX_LEN   13      // total size of other EEPROM parameters (summ of all EEPROM values below, included EEPROM_FLAG)
+
+
+#define EEPROM_FLAG            EEPROM_HOST_MAX_LEN + 1 // EEPROM has been changed (avoid overlap: EEPROM_HOST_MAX_LEN)
+
+#define EEPROM_numberOfLEDs    EEPROM_HOST_MAX_LEN + 2
+
+#define EEPROM_HgradBegin      EEPROM_HOST_MAX_LEN + 3 //  (the order of defines matter)
+#define EEPROM_SgradBegin      EEPROM_HOST_MAX_LEN + 4
+#define EEPROM_VgradBegin      EEPROM_HOST_MAX_LEN + 5
+
+#define EEPROM_HgradEnd                EEPROM_HOST_MAX_LEN + 6
+#define EEPROM_SgradEnd                EEPROM_HOST_MAX_LEN + 7
+#define EEPROM_VgradEnd                EEPROM_HOST_MAX_LEN + 8
+
+#define EEPROM_PERIOD          EEPROM_HOST_MAX_LEN + 9
+
+#define EEPROM_FX_PhaseX       EEPROM_HOST_MAX_LEN + 10
+#define EEPROM_FX_PhaseY       EEPROM_HOST_MAX_LEN + 11
+#define EEPROM_FX_PhaseZ       EEPROM_HOST_MAX_LEN + 12
+
+#define EEPROM_mainDelayNew    EEPROM_HOST_MAX_LEN + 13
+
+
+// ------------------- array of parameters ------------- (the order of defines matter)
+
+#define NUMBER_OF_PARAM                10
+
+#define H_GRAD_BEG             0 
+#define S_GRAD_BEG             1
+#define V_GRAD_BEG             2
+#define H_GRAD_END             3
+#define S_GRAD_END             4
+#define V_GRAD_END             5
+#define PERIOD                 6
+#define PHASE_X_INC            7
+#define PHASE_Y_INC            8
+#define PHASE_Z_INC            9
+
+float  FX_Arr[ NUMBER_OF_PARAM ]       = {};
+float  FX_ArrNew[ NUMBER_OF_PARAM ]    = {};
+float  FX_Coeff[ NUMBER_OF_PARAM ]     = {};
+float  Phase[ 3 ]                      = {};
+
+float  FX_CoeffMax                     = 1.0;
+
+#define PHASE_X                        0
+#define PHASE_Y                        1
+#define PHASE_Z                        2
+
+
+// ------------------- server settings -----------------
+
+#define HOST_DEFAULT           "im_serial_"
+#define NTP_SERVER             "europe.pool.ntp.org"
+
+char host[ EEPROM_HOST_MAX_LEN ];
+
+// const char* ssid    = "axod_ap";
+// const char* password        = "superstrongpassword"
+
+const char* ssid       = IM_WIFI_SSID;
+const char* password   = IM_WIFI_PASS;
+
+ESP8266WebServer       server(80);
+
+String                         hostDefURI;
+String                         HTTPOut;
+String                 HTTPErr;
+
+WiFiUDP                ntpUDP;
+NTPClient              timeClient(ntpUDP, NTP_SERVER , 0, 60000);
+
+// ---------------- EEPROM String r/w ------------------
+
+void EEPROMStrRead( uint8_t addr, char * str ){
+       for (uint8_t a = addr; a <  EEPROM_HOST_MAX_LEN; a++ ){
+               str[ a ] = EEPROM.read( a );
+               if (str[ a ] == 0) break;
+       }
+}
+
+void EEPROMStrWrite( uint8_t addr, char * str ){
+       uint8_t a = 0;
+       for (a = addr; a <  EEPROM_HOST_MAX_LEN; a++ ){
+               EEPROM.write( a, str[ a ] );
+               if (str[ a ] == 0) break;
+       }
+       EEPROM.write( a, 0 );
+}
+
+// ---------------- misc -------------------------------
+
+int str2HEX(const String str) {        
+       return strtol( str.c_str(), 0, 16 );
+}
+
+String printNum(unsigned long num, int base, char sign) {
+
+       char outbuf[12];
+
+       int i = 12;
+       int j = 0;
+       int n;
+
+       do {
+               outbuf[i] = "0123456789ABCDEF"[num % base];
+               i--;
+               num = num/base;
+       } while ( num > 0 );
+
+       if ( sign != ' ' ){
+               outbuf[0] = sign;
+               ++j;
+       }
+
+       while ( ++i < 13 ){
+               outbuf[j++] = outbuf[i];
+       }
+
+       if ( j == 1 ) { outbuf[ 1 ] = outbuf[ 0 ]; outbuf[ 0 ] = '0'; ++j; }
+
+       outbuf[j] = 0;
+
+       return outbuf;
+
+}
+
+uint8_t is_number( const char *s ){
+
+       while (*s) { 
+               Serial.printf("s: %c\n", *s);
+               if ( *s++ <48 || *s >57 ) return 0;
+       }
+       return true;
+}
+
+// ----------- explode for selected substring -----------
+
+String expld( String str, unsigned int numb, char delimiter ){
+       
+       unsigned int cnt = 0, a = 0, p2 = 0, p1 = 0, lng = 0;
+
+       lng = str.length();
+
+       for ( a = 0; a < lng; a++ ){
+               if ( str.charAt( a ) == delimiter ) { 
+                       p2 = p1;
+                       p1 = a;
+                       if ( cnt == numb ) break;
+                       cnt ++;
+               }
+       }
+
+       if ( cnt < numb ) return "";
+
+       if ( a == lng ) { 
+               p2 = p1;
+               p1 = lng;
+       }
+
+       if ( numb > 0 ) p2 ++;
+
+       return str.substring(p2, p1);
+       
+}
+
+// ---------------- FX ---------------------------------
+
+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 ) {
+
+       unsigned int    i = 0;
+       CHSV            color;
+
+       #define         Y_PHASE_SHIFT   128     // 128 // to avoid solid fill on phase = 0
+
+       // NOTE! phaseZ is for color component only since it is cyclic
+
+       for (i = 0; i < numberOfLEDs; i++) {
+
+//             newHueColor = pow( (float)sin( (float)i*PI / (period * 2) ) ,2 ) * (gradEnd - gradBegin) + gradBegin;           // MEMO! floating point
+
+               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;    
+               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;
+               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;
+
+//             LEDStripe_RGB[ i ] = CHSV( color.h + phaseZ , color.s, color.v );
+
+               color.h += phaseZ;
+               hsv2rgb_spectrum(color, LEDStripe_RGB[ i ]);
+
+       }
+}
+
+// -----------------------------------------------------
+// --------------- Init --------------------------------
+// -----------------------------------------------------
+
+void setup(void) {
+
+       HTTPOut.reserve(800); // lenght of Help message generally
+
+       hostDefURI.reserve( EEPROM_HOST_MAX_LEN );
+       hostDefURI = HOST_DEFAULT + WiFi.macAddress().substring(12, 14) + WiFi.macAddress().substring(15, 17); // 00:00:00:00:00:00
+
+       // calculate lookup array 
+//     for (unsigned int a = 1; a <= 255; a++) EXP[ a ] = round( pow( 255, (double)a / (255) ) ); // calculate exponential lookup array (for PWM etc..)
+
+       // reset FX_Coeff (maybe not required)
+
+       for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_Coeff[ a ] = 1.0;
+
+#if SERIAL == 1
+
+       // Serial init
+
+       Serial.begin(115200);
+       Serial.println();
+       Serial.println("Booting Sketch...");
+#endif
+
+       // read parameters from EEPROM (if it has been saved early)
+
+       EEPROM.begin( EEPROM_HOST_MAX_LEN + EEPROM_OTHER_MAX_LEN );
+       
+       if ( EEPROM.read( EEPROM_FLAG ) == 1 ){
+
+               EEPROMStrRead( EEPROM_HOST, host ); 
+
+               numberOfLEDs    = EEPROM.read( EEPROM_numberOfLEDs );
+               mainDelayNew    = EEPROM.read( EEPROM_mainDelayNew );
+
+               for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_ArrNew[ a ]  = EEPROM.read( EEPROM_HgradBegin + a );
+
+               changeColor = true;
+
+       } else {
+
+               FX_ArrNew[ PERIOD ] = DEF_NUM_LEDS;
+               strcpy( host, (char*)hostDefURI.c_str() ); // default URI and host name
+       }
+
+       // Fast LED RGB init
+
+       pinMode(ENABLE_GATE_PIN, OUTPUT);
+       digitalWrite( ENABLE_GATE_PIN, HIGH );
+
+       delay(1000);  // 2000 // sanity check delay - allows reprogramming if accidently blowing power w/leds
+
+       FastLED.addLeds<LED_STRIPE, DATA_PIN, GRB>( LEDStripe_RGB, numberOfLEDs );
+       fill_solid( LEDStripe_RGB, numberOfLEDs, CRGB(0, 0, 0) );
+       FastLED.show();
+
+       // WiFi init 
+
+       if ( host == "" ) strcpy( host, (char*)hostDefURI.c_str() );  // if it was changed without parameters by mistake
+
+       WiFi.hostname( host );
+
+//     WiFi.softAP(APssid, APpassword);
+//     WiFi.mode(WIFI_AP);
+//     WiFi.mode(WIFI_AP_STA); 
+
+       WiFi.mode(WIFI_STA);
+       WiFi.setAutoReconnect( true );
+       WiFi.begin(ssid, password);
+
+       // main events handlers
+
+       if (WiFi.waitForConnectResult() == WL_CONNECTED) {
+
+               MDNS.begin( host );
+
+               // get time 
+
+//             timeClient.setTimeOffset(0);
+               timeClient.begin();
+               timeClient.update();
+
+               // start server 
+
+               server.begin();
+               MDNS.addService("http", "tcp", 80);
+
+#if SERIAL == 1
+               Serial.printf("Ready. Try \"curl %s/help\" to see the list of commands and parameters.\n", host);
+#endif
+
+               server.onNotFound( []() {
+
+                       server.sendHeader("Connection", "close");
+
+                       param           = "";
+//                     HTTPOut         = "";
+
+                       // no command given (see handlers of commands below)
+
+                       if ( server.uri() != "/" )HTTPErr += "Command '" + server.uri() + "' not exist.\n";
+
+
+                       for ( uint8_t i=0; i < server.args(); i++ ){
+
+                               paramCnt = 0;
+
+                               // ------------- specific parameters -----------------
+
+                               // change number of leds in stripe 
+       
+                               if ( server.argName(i) == "number" ) {
+
+                                       // TODO make it in MAIN with dimming
+
+                                       fill_solid( LEDStripe_RGB, numberOfLEDs, CRGB(0, 0, 0) );
+                                       FastLED.show();
+
+                                       numberOfLEDs    = server.arg("number").toInt();
+                                       FastLED.addLeds<LED_STRIPE, DATA_PIN, GRB>( LEDStripe_RGB, numberOfLEDs );
+
+                                       paramCnt++;
+                               }
+
+                               // color gradient (hex)
+
+                               if ( server.argName(i) == "gradient" ) {
+
+                                       param = server.arg("gradient");
+
+                                       for ( uint8_t a = 0; a < 6; a++ ) FX_ArrNew[ H_GRAD_BEG + a ] = str2HEX( param.substring( a*2, a*2+2) );
+
+                                       changeColor = true;
+                                       paramCnt++;
+
+                               }
+
+                               // sin/cos period (dec)
+
+                               if ( server.argName(i) == "period" ) {
+
+                                       FX_ArrNew[ PERIOD ] = server.arg("period").toInt(); 
+
+                                       changeColor = true;
+                                       paramCnt++; 
+                               }
+
+
+                               // pattern motion parameters (dec)
+
+                               if ( server.argName(i) == "slidex" ) { 
+                                       FX_ArrNew [ PHASE_X_INC ] = (float)server.arg("slidex").toInt(); 
+                                       changeColor = true;
+                                       paramCnt++; 
+                               }
+
+                               if ( server.argName(i) == "slidey" ) { 
+                                       FX_ArrNew [ PHASE_Y_INC ] = (float)server.arg("slidey").toInt(); 
+                                       changeColor = true;
+                                       paramCnt++; 
+                               }
+
+                               if ( server.argName(i) == "slidez" ) { 
+                                       FX_ArrNew [ PHASE_Z_INC ] = (float)server.arg("slidez").toInt(); 
+                                       changeColor = true;
+                                       paramCnt++; 
+                               }
+
+
+                               // delay between frames
+
+                               if ( server.argName(i) == "delay" ) { 
+                                       mainDelayNew = server.arg("delay").toInt(); 
+//                                     changeColor = true;
+                                       paramCnt++; 
+                               }
+
+                               // ------------- common parameters -------------------
+
+                               // debug data {Number R G B H S V}
+
+                               if ( server.argName(i) == "debug" ){ // || param != 0
+
+                                       HTTPOut += "Stripe [debug]: ";
+
+                                       for (unsigned int i = 0; i < numberOfLEDs; i++) { 
+                                               color_HSV = rgb2hsv_approximate( LEDStripe_RGB[ i ] ); 
+                                               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"; 
+                                       } 
+
+                                       paramCnt++;
+                               }
+
+                               // set time 
+
+                               if ( server.argName(i) == "time" ){ // || param != 0
+
+                                       if ( is_number( server.arg("time").c_str() ) ) { 
+                                               timeClient.update();
+                                               timeStamp = strtol( server.arg("time").c_str(), 0, 10 );
+                                       } else {
+                                               HTTPErr += "Parameter 'time' must be specified in UNIX timestamp format.\n"; // 1631848103
+                                       }
+
+                                       paramCnt++;
+                               }
+
+                               // rename host 
+
+                               if ( server.argName(i) == "host" ) {
+
+                                       if ( server.arg("host") == "" ) {
+
+                                               HTTPErr += "Please specify new unit hostname.\n";
+
+                                       } else if ( server.arg("host").length() > EEPROM_HOST_MAX_LEN ) { 
+
+                                               HTTPErr += "Unit hostname can not exceed " + String( EEPROM_HOST_MAX_LEN ) + " symbols.\n";
+
+                                       } else {
+
+                                               strcpy( host, (char*)server.arg("host").c_str() ); // host = (char *)server.arg("host").c_str();
+                                               WiFi.hostname( host );
+                                               MDNS.begin( host );
+
+                                               HTTPOut +=  "The host name has been changed to '" + String( host ) + "'. Do not forget to save current settings using the '/save' command.\n";
+                                       }
+
+                                       paramCnt++;
+                               }
+
+                               // save all parameters to EEPROM 
+
+                               if (server.argName(i) == "save" ){
+
+                                       EEPROM.write( EEPROM_numberOfLEDs, numberOfLEDs );
+                                       EEPROM.write( EEPROM_mainDelayNew, mainDelayNew );
+
+                                       for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) EEPROM.write( EEPROM_HgradBegin + a, (uint8_t)roundf( FX_Arr[ a ] ) );
+
+                                       EEPROMStrWrite( EEPROM_HOST, host );
+                                       EEPROM.write( EEPROM_FLAG, 1 ); // EEPROM changed
+                                       EEPROM.commit();
+
+                                       HTTPOut +=  "The current settings are saved.\n";
+
+                                       paramCnt++;
+                               }
+
+                               // reset to default parameters
+
+                               if ( server.argName(i) == "reset" ){
+
+                                       numberOfLEDs            = DEF_NUM_LEDS;
+                                       mainDelayNew            = DEF_MAIN_DELAY;
+
+                                       for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) FX_ArrNew[ a ] = 0;
+
+                                       FX_Arr[ PERIOD ]        = DEF_NUM_LEDS;
+                                       FX_ArrNew[ PERIOD ]     = DEF_NUM_LEDS;
+
+                                       strcpy( host, (char*)hostDefURI.c_str() );
+                                       WiFi.hostname( host );
+
+                                       server.begin();
+                                               
+                                       if ( quiet > 1 ) HTTPOut += "All parameters are set to default values. Don't forget to /save them.\n";
+
+                                       paramCnt++;
+
+                                       changeColor = true;
+
+                               }
+
+                               // reboot
+
+                               if ( server.argName(i) == "reboot" ){
+
+                                       FX_ArrNew[ V_GRAD_BEG ] = 0;
+                                       FX_ArrNew[ V_GRAD_END ] = 0;
+
+                                       mainDelayNew = DEF_MAIN_DELAY;
+
+                                       if ( quiet > 1 ) {
+
+                                               server.send_P(200, "text/plain", PSTR("Unit rebooting...\n") );
+                                               server.begin();
+                                       }
+
+                                       paramCnt++;
+
+                                       changeColor = true;
+                                       commandReboot = true;
+                               }
+
+                               // quiet, level of output
+
+                               if ( server.argName(i) == "quiet" ) {
+
+                                       quiet = str2HEX( server.arg("quiet") );
+                                       paramCnt++;
+                               } else {
+                                       quiet = FULL_OUTPUT;
+                               }
+
+                               // json on
+
+                               if ( server.argName(i) == "json" ) {
+                                       JSONon = true;
+                                       paramCnt++;
+                               }
+
+                               if ( !paramCnt ) HTTPErr += "Parameter '" + server.argName(i) + "' not exist.\n";
+                       }
+
+
+                       // ---------------- host default return ------------------------
+
+                       if (HTTPErr != "" ) {   
+
+                               // list of errors 
+
+                               String Err = "";
+
+                               paramCnt = 0;
+
+                               if (quiet > 0) { 
+
+                                       if ( JSONon ) {
+
+                                               HTTPOut += "{[";
+
+                                               while ( 1 ){
+                                                       Err = expld( HTTPErr, paramCnt, '\n' );
+                                                       if ( Err != "" ) HTTPOut += "\"" + Err  + "\""; else break; 
+                                                       if ( expld( HTTPErr, paramCnt+1, '\n' ) != "" ) HTTPOut += ",";
+                                                       paramCnt ++;
+                                               }
+
+                                               HTTPOut += "]}";
+
+                                       } else {
+
+                                               while ( 1 ){
+                                                       Err = expld( HTTPErr, paramCnt, '\n' );
+                                                       if ( Err != "" ) HTTPOut += "Error: " + Err + "\n"; else break;
+                                                       paramCnt ++;
+                                               }
+//                                             HTTPOut += "See '/help' for details\n";
+                                       }
+                               }
+
+                       } else {
+
+                               // standart output 
+
+                               if ( quiet > 1 && HTTPOut == "" ) { 
+
+                                       if ( JSONon ){
+
+                                               HTTPOut =  "{";
+                                               HTTPOut += "\"MAC\":\""                 + String( WiFi.macAddress() ) + "\",\n";
+                                               HTTPOut += "\"Host\":\""                + String( host ) + "\",\n";
+                                               HTTPOut += "\"Timestamp\":\""           + String( timeClient.getEpochTime() ) + "\",\n";
+                                               HTTPOut += "\"Target_timestamp\":\""    + String( timeStamp ) + "\",\n";
+
+                                               HTTPOut += "\"TNumber_of_LEDs\":\""     + String( numberOfLEDs ) + "\",\n";
+
+                                               HTTPOut += "\"SlideX:\":\""             + String( (int)roundf( FX_ArrNew[ PHASE_X_INC ] ) ) + "\",\n";
+                                               HTTPOut += "\"SlideY:\":\""             + String( (int)roundf( FX_ArrNew[ PHASE_Y_INC ] ) ) + "\",\n";
+                                               HTTPOut += "\"SlideZ:\":\""             + String( (int)roundf( FX_ArrNew[ PHASE_Z_INC ] ) ) + "\",\n";
+
+                                               HTTPOut += "\"Period:\":\""             + String( (int)roundf( FX_ArrNew[ PERIOD ] ) ) + "\",\n";
+                                               HTTPOut += "\"Delay:\":\""              + String( mainDelayNew ) + "\",\n";
+
+                                               HTTPOut += "\"Gradient_HSV:\":\"";
+
+                                               for ( uint8_t a = 0; a < 6 ; a++ ) HTTPOut +=  String( printNum( FX_ArrNew[ a ], 16, ' ') );
+
+                                               HTTPOut += "\",\n";
+
+                                               HTTPOut += "}";
+
+                                       } else { 
+
+                                               HTTPOut =  "MAC:"               + String( WiFi.macAddress() ) + "\n";
+                                               HTTPOut += "Host:"              + String( host ) + "\n";
+                                               HTTPOut += "Timestamp:"         + String( timeClient.getEpochTime() ) + "\n";
+                                               HTTPOut += "Target timestamp:"  + String( timeStamp ) + "\n";
+
+                                               HTTPOut += "Number of LEDs:"    + String( numberOfLEDs ) + "\n";
+
+                                               HTTPOut += "Slide X:"           + String( (int)roundf( FX_ArrNew[ PHASE_X_INC ] ) ) + "\n";
+                                               HTTPOut += "Slide Y:"           + String( (int)roundf( FX_ArrNew[ PHASE_Y_INC ] ) ) + "\n";
+                                               HTTPOut += "Slide Z:"           + String( (int)roundf( FX_ArrNew[ PHASE_Z_INC ] ) ) + "\n";
+
+                                               HTTPOut += "Period:"            + String( (int)roundf( FX_ArrNew[ PERIOD ] ) ) + "\n";
+                                               HTTPOut += "Delay:"             + String( mainDelayNew ) + "\n";
+
+                                               HTTPOut += "Gradient (HSV):";
+
+                                               for ( uint8_t a = 0; a < 6 ; a++ ) HTTPOut += String( printNum( roundf( FX_ArrNew[ a ] ), 16, ' ') ) + " ";                             
+
+                                               HTTPOut += "\n";
+                                       
+                                       }
+                               } 
+                       }
+
+                       server.send(200, "text/plain", HTTPOut );
+
+                       HTTPOut         = "";
+                       HTTPErr         = "";
+                       JSONon          = 0;
+
+               });
+
+               // ui interface TODO xml inteface
+
+               server.on("/ui", HTTP_GET, []() {
+                       HTTPOut = "User interface. Not yet implemented.\n";
+                       server.send(200, "text/plain", HTTPOut);
+               });
+
+               // help
+
+               server.on("/help", HTTP_GET, []() {
+
+                       HTTPOut =       "Interplay Medium ESP8266 Serial LED Controller. Version: " + String(VERSION) + "\n";
+                       HTTPOut +=      "Created by Dmitry Shalnov (c) 2017. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>. \n\n";
+
+                       HTTPOut +=      "Parameters:\n";
+
+                       HTTPOut +=      "       ?number=        Number of LEDs in stripe (max MAX_NUM_LEDS)\n";
+                       HTTPOut +=      "       ?gradient=      Start and end colors of gradient (hex, 24bit/color in HLS format, e.g. 001122FFEECC)\n";
+                       HTTPOut +=      "       ?period=        Sine period (number of leds)\n";
+                       HTTPOut +=      "       ?delay=         Delay of state changing, integer\n";
+
+                       HTTPOut +=      "       ?slidex=        Step of X shift per cycle, signed integer\n";
+                       HTTPOut +=      "       ?slidey=        Step of Y shift per cycle, signed integer\n";
+                       HTTPOut +=      "       ?slidez=        Step of Z shift per cycle, signed integer\n";
+
+                       HTTPOut +=      "       ?time=          Target timestamp (when the changes will take effect, good for synch), decimal UNIX Timestamp\n\n";
+
+                       HTTPOut +=      "       ?host=          Rename the unit\n";
+                       HTTPOut +=      "       ?quiet=         Level of output (0/empty = no output, 1 = OK/Error)\n\n";
+
+                       HTTPOut +=      "Commands:\n";
+
+                       HTTPOut +=      "       ?save           Save current settings\n";
+                       HTTPOut +=      "       ?reset          Reset to initial settings\n";
+                       HTTPOut +=      "       ?reboot         Reboot unit\n";
+
+                       HTTPOut +=      "       \n";
+                       HTTPOut +=      "       /ui             Output in XML UI format\n";
+                       HTTPOut +=      "       /json           Output in JSON format\n";
+                       HTTPOut +=      "       /update         Wireless update of firmware (see example below)\n";
+                       HTTPOut +=      "       /help           This help\n\n";
+
+                       HTTPOut +=      "Usage:         curl " + String(host) + "?<parameter>=<value>\n";
+                       HTTPOut +=      "               curl " + String(host) + "/<command>\n";
+                       HTTPOut +=      "Examples:      curl \"" + String(host) + "?gradient=ffffffddff00&period=30\" \n";
+                       HTTPOut +=      "               curl -F image=@firmware.bin " + String(host) + "/update \n";
+
+                       server.sendHeader("Connection", "close");
+                       server.send( 200, "text/plain", HTTPOut );
+
+               });
+
+               // firmware update
+
+               server.on("/update", HTTP_POST, []() {
+
+                       server.sendHeader("Connection", "close");
+                       server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
+
+                       ESP.restart();
+
+                       }, []() {
+
+                               HTTPUpload& upload = server.upload();
+
+                               if (upload.status == UPLOAD_FILE_START) {
+                               
+#if SERIAL == 1
+                                       Serial.setDebugOutput(true);
+#endif
+                                       WiFiUDP::stopAll();
+
+#if SERIAL == 1
+                                       Serial.printf("Update: %s\n", upload.filename.c_str());
+#endif
+                                       uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
+
+                                       if (!Update.begin(maxSketchSpace)) { //start with max available size
+#if SERIAL == 1
+                                               Update.printError(Serial);
+#endif
+                                       }
+                               } else if (upload.status == UPLOAD_FILE_WRITE) {
+                                       if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
+#if SERIAL == 1
+                                               Update.printError(Serial);
+#endif
+                                       }
+                               } else if (upload.status == UPLOAD_FILE_END) {
+                                       if (Update.end(true)) { //true to set the size to the current progress
+
+                                               server.sendHeader("Connection", "close");
+                                               server.send_P(200, "text/plain", PSTR("Success. Please wait until device replace firmware and boot up...\n") );
+#if SERIAL == 1
+                                               Serial.printf("Update Success: %u\nRebooting....\n", upload.totalSize); 
+#endif
+                                       } else {
+                                               server.sendHeader("Connection", "close");
+                                               server.send_P(200, "text/plain", PSTR("Something went wrong. Please reset device and try again.\n") );
+#if SERIAL == 1
+                                               Update.printError(Serial);
+#endif
+                                       }
+#if SERIAL == 1
+                                       Serial.setDebugOutput(false);
+#endif
+                               }
+
+                               yield();
+               });
+
+       } else {
+#if SERIAL == 1
+               Serial.println("WiFi Failed");
+#endif
+       }
+}
+
+// -----------------------------------------------------
+// ------------------- MAIN ----------------------------
+// -----------------------------------------------------
+
+// uint8_t rebootColorsCnt = 0;
+
+void loop(void) {
+
+       server.handleClient();
+       MDNS.update();
+
+       // reconnect
+
+       if ( WiFi.status() != WL_CONNECTED ) {
+#if SERIAL == 1
+               Serial.printf(".");
+#endif
+               if (WiFi.waitForConnectResult() == WL_CONNECTED) {
+#if SERIAL == 1
+                       Serial.println("Reconnected");
+#endif
+                       MDNS.begin( host );
+
+                       timeClient.begin();
+                       timeClient.update();
+               }
+
+       } 
+
+       if ( changeColor ) {
+
+               // set float coefficents
+
+               for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) {
+                       FX_Coeff[ H_GRAD_BEG + a ] = (float)FX_ArrNew[ H_GRAD_BEG + a ] - FX_Arr[ H_GRAD_BEG + a ];
+                       if ( abs( FX_Coeff[ H_GRAD_BEG + a ] ) > FX_CoeffMax ) FX_CoeffMax = abs( FX_Coeff[ H_GRAD_BEG + a ] );
+               }
+
+               for ( uint8_t a = 0; a < 6; a++ ) FX_Coeff[ H_GRAD_BEG + a ] = FX_Coeff[ H_GRAD_BEG + a ] / FX_CoeffMax;
+
+               changeColor = false;
+
+#if SERIAL == 1
+               Serial.printf("FX_ArrNew: "); 
+               for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_ArrNew[ a ] );
+               Serial.printf("\n");
+               Serial.printf("FX_Coeff: "); 
+               for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_Coeff[ a ] );
+               Serial.printf("\n");
+#endif
+
+       } else {
+
+               if ( ++mainDelay == mainDelayNew /* && FX_Delay != 0 */ ) {
+
+#if SERIAL == 1
+//                     Serial.printf("FX_Arr: "); 
+//                     for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) Serial.printf("%f ", FX_Arr[ a ] );
+//                     Serial.printf("\n");
+//                     Serial.printf("Phase: %f %f %f\n", Phase[ PHASE_X ], Phase[ PHASE_Y ], Phase[ PHASE_Z ] );
+#endif
+
+                       mainDelay = 0;
+
+                       // fade out and reboot
+
+                       if ( commandReboot && roundf( FX_Arr[ V_GRAD_BEG ] ) == 0 && roundf( FX_Arr[ V_GRAD_END ]) == 0 ) {
+                               commandReboot = false;
+#if SERIAL == 1
+                               Serial.printf("Reboot......\n"); 
+#endif
+                               ESP.restart();
+//                             Serial.printf("Reboot.2...\n"); 
+                       }
+
+                       if ( timeClient.getEpochTime() > timeStamp ) {
+
+                               // change color 
+
+                               for ( uint8_t a = 0; a < NUMBER_OF_PARAM; a++ ) {
+                                       if ( FX_ArrNew [ a ] != roundf( FX_Arr[ a ] ) ) { FX_Arr[ a ] += FX_Coeff[ a ]; } else FX_Arr[ a ] = (float)FX_ArrNew [ a ];
+                               }
+               
+                               // change phase 
+
+                               for ( uint8_t a = 0; a < 3; a++ ) {
+
+                                       if ( FX_Arr[ PHASE_X_INC + a ] == 0 ) { 
+                                               // shift phase to 0 after slide. = 0
+                                               if ( (uint8_t)round( Phase[ a ] ) != 0 ) Phase[ a ] -= FX_Coeff[ PHASE_X_INC + a ]/100; else Phase[ a ] = 0; 
+                                       } else { 
+                                               // routine shift phase
+                                               Phase[ a ] += FX_Arr[ PHASE_X_INC + a ]/100;
+                                       }
+                               }
+
+                       }
+
+                       if ( FX_Arr[ PERIOD ] == 0 ) FX_Arr[ PERIOD ] = 1;
+
+                       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 ] ) );
+                       FastLED.show();
+                       delay(3);
+
+               }
+       }
+}
diff --git a/Serial_RGB/version.c b/Serial_RGB/version.c
new file mode 100644 (file)
index 0000000..1438826
--- /dev/null
@@ -0,0 +1 @@
+#define        VERSION                 "10.05.24"
diff --git a/bidirectonal-mosfet-level-shifter.png b/bidirectonal-mosfet-level-shifter.png
new file mode 100644 (file)
index 0000000..469ace3
Binary files /dev/null and b/bidirectonal-mosfet-level-shifter.png differ
diff --git a/make b/make
new file mode 100755 (executable)
index 0000000..856ac1c
--- /dev/null
+++ b/make
@@ -0,0 +1,45 @@
+#!/bin/bash 
+
+if [ $# -lt 1 ]; then
+       echo "Usage: $0 <sketch name> [upload]"
+       exit 0
+fi 
+
+# assign WIFI_SSID and WIFI_PASS in external file ../info
+# source "../info"
+
+SKETCH="$1/$1.ino"
+
+TMP="/tmp/ESPcompile.tmp"
+
+# MAKE_FILE="/home/dmitry/Bin/SDK/ESP/makeEspArduino.21.02.22/makeEspArduino.mk"
+MAKE_FILE="/home/dmitry/Bin/SDK/ESP/makeEspArduino/makeEspArduino.mk"
+# ESP_SDK_ROOT=/home/dmitry/Bin/SDK/ESP/esp8266                                # keep it without quotation marks
+
+# ESP_SDK_ROOT=~/Bin/SDK/ESP/Arduino
+# ESP_SDK_ROOT=~/Bin/SDK/ESP/esp8266_3.1.2
+# ESP_SDK_ROOT=~/Bin/SDK/ESP/esp8266_3.0.2
+ESP_SDK_ROOT=~/Bin/SDK/ESP/esp8266_2.5.0
+
+
+# LIBS=/home/dmitry/Bin/SDK/ESP/esp8266/libraries
+# USER_SRC_PATTERN="version.h"
+
+DATE=$(date +"%d.%m.%y")
+echo "#define  VERSION                 \"$DATE\"" > "$1/version.c"
+
+# nodemcuv2, generic, esp8285
+make -f "$MAKE_FILE" ESP_ROOT="$ESP_SDK_ROOT" F_CPU=160000000L CHIP=esp8266 BOARD=esp8285 SKETCH="$SKETCH" $2 2>&1 | tee "$TMP"
+
+if [ -s "$TMP" ]; then 
+       binSRC=$( cat "$TMP" | grep Linking | sed -e 's/Linking //g' )
+       cp "$binSRC" .
+
+       serialPort=$(cat "$TMP" | grep "opening port" | awk '{split($0,a," "); print a[3]}')
+else
+       serialPort=/dev/ttyUSB0
+fi
+
+
+
+
diff --git a/serial b/serial
new file mode 100755 (executable)
index 0000000..679915c
--- /dev/null
+++ b/serial
@@ -0,0 +1,32 @@
+#!/bin/bash 
+
+if [ -s "$TMP" ]; then 
+       binSRC=$( cat "$TMP" | grep Linking | sed -e 's/Linking //g' )
+       cp "$binSRC" .
+
+       serialPort=$(cat "$TMP" | grep "opening port" | awk '{split($0,a," "); print a[3]}')
+else
+       serialPort=/dev/ttyUSB0
+fi
+
+echo "Serial: $serialPort"
+
+
+if cat "$TMP" | grep -q 'error'; then 
+       echo "exit"
+else 
+       if [ "$1" != "noserial" ]; then
+
+               echo "Connecting $serialPort"
+
+               stty -F $serialPort cs8 cstopb -ixon raw speed 115200
+
+               while [ 1 ]; do
+                       cat $serialPort
+                       sleep 1
+               done
+       else 
+               echo "Serial terminal omited"
+       fi
+fi 
+
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717