4 Channel Switcher
authorDmitry Shalnoff <dev@shalnoff.com>
Tue, 10 Sep 2024 14:13:22 +0000 (16:13 +0200)
committerDmitry Shalnoff <dev@shalnoff.com>
Tue, 10 Sep 2024 14:13:22 +0000 (16:13 +0200)
.gitignore [new file with mode: 0644]
4_Channel_Switcher/4_Channel_Switcher.ino [new file with mode: 0644]
4_Channel_Switcher/version.c [new file with mode: 0644]
4_Channel_Switcher/version.h [new file with mode: 0644]
README.md [new file with mode: 0644]
make [new file with mode: 0755]
serial [new file with mode: 0755]
todo [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..dfa73bd
--- /dev/null
@@ -0,0 +1,57 @@
+memo
+info
+BKP/*
+
+# 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/4_Channel_Switcher/4_Channel_Switcher.ino b/4_Channel_Switcher/4_Channel_Switcher.ino
new file mode 100644 (file)
index 0000000..3fa87a5
--- /dev/null
@@ -0,0 +1,682 @@
+/*
+ * 4 Channel Switcher, 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"
+
+#include <NTPClient.h>
+
+#include <ESP8266WiFi.h>
+#include <WiFiClient.h>
+#include <ESP8266WebServer.h>
+#include <ESP8266mDNS.h>
+#include <EEPROM.h>
+
+#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_LEN       9                       // length of EEPROM excepting EEPROM_HOST_MAX_LEN (count below)
+
+#define EEPROM_HOST            0
+#define EEPROM_FLAG            EEPROM_HOST_MAX_LEN + 1 
+#define EEPROM_R               EEPROM_HOST_MAX_LEN + 2
+#define EEPROM_G               EEPROM_HOST_MAX_LEN + 3
+#define EEPROM_B               EEPROM_HOST_MAX_LEN + 4
+#define EEPROM_W               EEPROM_HOST_MAX_LEN + 5
+#define EEPROM_ROTATE_DELAY    EEPROM_HOST_MAX_LEN + 6         // uint16_t
+#define EEPROM_MAIN_DELAY      EEPROM_HOST_MAX_LEN + 8         // uint16_t
+
+#define FULL_OUTPUT            2                       // default quiet level
+
+// -------------------- main settings  --------------------
+
+#define        LED1            0
+#define        LED2            2
+#define        LED3            3
+#define        LED4            1
+
+#define        DEF_MAIN_DELAY  600;
+
+uint8_t                switchState = 0;
+uint8_t                quiet = FULL_OUTPUT;
+uint8_t                paramCnt = 0;
+uint8_t                JSONon = 0;
+
+int            R=0, G=0, B=0, W=0;
+uint16_t       rotateTimer = 0, mainTimer = 0;
+uint16_t       rotateDelay = 0;
+uint16_t       mainDelay = DEF_MAIN_DELAY;
+String                 rgbw = "";
+
+unsigned long  timeStamp = 0;
+
+// ------------------- server settings -------------------
+
+#define HOST_DEFAULT                   "im_switch_"
+#define NTP_SERVER                     "europe.pool.ntp.org"
+
+char host[ EEPROM_HOST_MAX_LEN ];
+
+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( unsigned char addr, char * str ){
+       for (unsigned char a = addr; a < addr + EEPROM_HOST_MAX_LEN; a++ ){
+               str[ a ] = EEPROM.read( a );
+               if (str[ a ] == 0) break;
+       }
+}
+
+void EEPROMStrWrite( unsigned char addr, char * str ){
+       unsigned char a = 0;
+       for (a = addr; a < addr + 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];
+       }
+
+       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);
+       
+}
+
+// --------------- Init ---------------------------------
+
+void setup(void) {
+
+       HTTPOut.reserve(800); // lenght of Help message generally
+
+       // init LED pins
+
+       pinMode(LED1, OUTPUT);
+       pinMode(LED2, OUTPUT);
+       pinMode(LED3, OUTPUT);
+       pinMode(LED4, OUTPUT);
+
+       digitalWrite(LED1, LOW);
+       digitalWrite(LED2, LOW);
+       digitalWrite(LED3, LOW);
+       digitalWrite(LED4, LOW);
+
+#if SERIAL == 1
+
+       // Serial init
+
+       Serial.begin(115200); 
+       Serial.println();
+       Serial.println("Booting...");
+#endif
+
+       // default hostname of the unit
+
+       hostDefURI.reserve( EEPROM_HOST_MAX_LEN );
+       hostDefURI = HOST_DEFAULT + WiFi.macAddress().substring(12, 14) + WiFi.macAddress().substring(15, 17);
+
+       // read parameters from EEPROM (if it has been saved early)
+
+       EEPROM.begin( EEPROM_HOST_MAX_LEN + EEPROM_OTHER_LEN );
+       
+       if ( EEPROM.read( EEPROM_FLAG ) == 1 ){
+
+               EEPROMStrRead( EEPROM_HOST, host ); 
+
+               mainDelay       = EEPROM.read( EEPROM_MAIN_DELAY );
+               rotateDelay     = EEPROM.read( EEPROM_ROTATE_DELAY );
+
+               R               = EEPROM.read( EEPROM_R );
+               G               = EEPROM.read( EEPROM_G );
+               B               = EEPROM.read( EEPROM_B );
+               W               = EEPROM.read( EEPROM_W );
+
+       } else {
+               mainDelay       = DEF_MAIN_DELAY;
+               strcpy( host, (char*)hostDefURI.c_str() ); // default URI and host name
+       }
+
+       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);
+
+#if SERIAL == 1
+       Serial.printf("Ready. Try \"curl %s/help\" in terminal\n", host);
+#endif
+
+
+       if (WiFi.waitForConnectResult() == WL_CONNECTED) {
+
+               MDNS.begin( host );
+
+               // get time 
+
+//             timeClient.setTimeOffset(0);
+               timeClient.begin();
+               timeClient.update();
+
+               // server begin 
+
+               server.begin();
+               MDNS.addService("http", "tcp", 80);
+
+               // -------------------- handlers of parameters ----------------------
+
+               server.onNotFound( []() {
+
+                       server.sendHeader("Connection", "close");
+
+                       // no command given (see handlers of commands below)
+
+                       if ( server.uri() != "/" ) HTTPErr += "Command '" + server.uri() + "' not exist.\n";
+
+                       // check parameters
+
+                       for ( uint8_t i=0; i < server.args(); i++ ){
+
+                               paramCnt = 0;
+
+                               // ------------- specific parameters -----------------
+
+                               // GPIO switch
+
+                               if ( server.argName(i) == "switch" ){
+
+                                       rgbw = server.arg("switch");
+
+                                       R = !!str2HEX( rgbw.substring(0, 1) );
+                                       G = !!str2HEX( rgbw.substring(1, 2) );
+                                       B = !!str2HEX( rgbw.substring(2, 3) );
+                                       W = !!str2HEX( rgbw.substring(3, 4) );
+
+                                       switchState = 1;
+
+                                       paramCnt++;
+                               }
+
+                               // GPIO rotate
+
+                               if ( server.argName(i) == "rotate" ){
+
+                                       rotateDelay = server.arg("rotate").toInt(); 
+
+                                       paramCnt++;
+                               }
+
+                               // ------------- common parameters -------------------
+
+                               // 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" ){
+
+                                       EEPROMStrWrite( EEPROM_HOST, host );
+
+                                       EEPROM.write( EEPROM_MAIN_DELAY, mainDelay );
+                                       EEPROM.write( EEPROM_ROTATE_DELAY, rotateDelay );
+
+                                       EEPROM.write( EEPROM_R, R );
+                                       EEPROM.write( EEPROM_G, G );
+                                       EEPROM.write( EEPROM_B, B );
+                                       EEPROM.write( EEPROM_W, W );
+
+                                       EEPROM.write( EEPROM_FLAG, 1 ); // EEPROM changing flag
+                                       EEPROM.commit();
+
+                                       HTTPOut +=  "The current settings are saved.\n";
+
+                                       paramCnt++;
+                               }
+
+                               // reset to default parameters
+
+                               if ( server.argName(i) == "reset" ){
+
+                                       EEPROMStrWrite( EEPROM_HOST, (char*)hostDefURI.c_str() );
+
+                                       EEPROM.write( EEPROM_MAIN_DELAY, 0 );
+                                       EEPROM.write( EEPROM_ROTATE_DELAY, 0 );
+
+                                       EEPROM.write( EEPROM_R, 0 );
+                                       EEPROM.write( EEPROM_G, 0 );
+                                       EEPROM.write( EEPROM_B, 0 );
+                                       EEPROM.write( EEPROM_W, 0 );
+
+                                       EEPROM.write( EEPROM_FLAG, 0 ); // EEPROM changing flag
+                                       EEPROM.commit();
+                                       
+                                       if ( quiet > 1 ) {
+
+                                               server.send_P(200, "text/plain", PSTR("All parameters are set to default values. Unit rebooting...\n") );
+                                               server.begin();
+                                       }
+
+                                       paramCnt++;
+
+                                       delay(1000);
+                                       ESP.restart();
+                               }
+
+                               // reboot
+
+                               if ( server.argName(i) == "reboot" ){
+
+                                       if ( quiet > 1 ) {
+
+                                               server.send_P(200, "text/plain", PSTR("Unit rebooting...\n") );
+                                               server.begin();
+                                       }
+
+                                       paramCnt++;
+
+                                       delay(1000);
+                                       ESP.restart();
+                               }
+
+                               // 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" ) { // server.hasArg("json") ) {
+                                       JSONon = true;
+                                       paramCnt++;
+                               }
+
+                               if ( !paramCnt ) HTTPErr += "Parameter '" + server.argName(i) + "' not exist.\n";
+                       }
+
+                       // host default return
+
+                       if ( HTTPErr != "" ) { 
+
+                               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 {
+
+                               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 += "\"Switchers\":\""           + String( R ) + String( G ) + String( B ) + String( W ) + "\",\n";
+                                               HTTPOut += "\"Rotate\":\""              + printNum( rotateDelay, 16, ' ' ) + "\",\n";
+//                                             HTTPOut += "\"Delay\":\""               + printNum( mainDelay, 16, ' ' ) + "\"\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 += "Switchers:"         + String( R ) + " " + String( G ) + " " + String( B ) + " " + String( W ) + "\n";
+                                               HTTPOut += "Rotate:"            + printNum( rotateDelay, 16, ' ' ) + "\n";
+//                                             HTTPOut += "Delay:"             + printNum( mainDelay, 16, ' ' ) + "\n";
+
+                                       }
+                               } 
+                       }
+
+                       server.send(200, "text/plain", HTTPOut );
+
+                       HTTPOut         = "";
+                       HTTPErr         = "";
+                       JSONon          = 0;
+
+               });
+
+               // -------------------- handlers of commands ----------------------
+
+               // ui interface TODO xml inteface
+
+               server.on("/ui", HTTP_GET, []() {
+                       HTTPOut = "User interface. Not yet implemented.\n";
+                       server.sendHeader("Connection", "close");
+                       server.send(200, "text/plain", HTTPOut);
+               });
+
+               // help
+
+               server.on("/help", HTTP_GET, []() {
+
+                       HTTPOut =       "Interplay Medium ESP8266 4-channel binary switcher. 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 +=      "       ?switch=        four-bit representation of the switches status, 1 symbol per switch (1111)\n";
+//                     HTTPOut +=      "       ?blink=         Red Green Blue and White components, hex 32 bit, 8 bit/color (ff00ff00)\n";
+                       HTTPOut +=      "       ?rotate=        Switches rotation delay (ffff, set 0 to stop)\n";
+//                     HTTPOut +=      "       ?delay=         Delay of state changing, hex 16 bit (ffff)\n";
+                       HTTPOut +=      "       ?time=          Target timestamp (when the changes will take effect, good for synch), decimal UNIX timestamp\n";
+
+                       HTTPOut +=      "       \n";
+                       HTTPOut +=      "       ?host=          Rename the unit\n";
+                       HTTPOut +=      "       ?quiet=         Level of output (0 -- no output, 1 -- errors only)\n";
+                       HTTPOut +=      "       ?save           Save current settings\n";
+                       HTTPOut +=      "       ?reset          Reset to initial settings\n";
+                       HTTPOut +=      "       ?reboot         Reboot unit\n";
+                       HTTPOut +=      "       ?json           Output in JSON format\n";
+
+                       HTTPOut +=      "       \n";
+                       HTTPOut +=      "       /ui             Output in XML UI format\n";
+                       HTTPOut +=      "       /update         Wireless update of firmware (see example below)\n";
+                       HTTPOut +=      "       /help           This help\n\n";
+
+                       HTTPOut +=      "       \n";
+                       HTTPOut +=      "Usage:         curl " + String(host) + "?<parameter>=<value>\n";
+                       HTTPOut +=      "               curl " + String(host) + "/<command>\n";
+                       HTTPOut +=      "Examples:      curl \"" + String(host) + "?switch=1010\" \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) {
+
+                                       rotateDelay = 0;
+                                       
+#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("Error: 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 =========================
+// ---------------------------------------------------------
+
+void loop(void) {
+
+       server.handleClient();
+       MDNS.update();
+
+       rotateTimer ++;
+       mainTimer ++;
+
+       // 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();
+               }
+
+       } 
+
+       // main routine with synch
+
+       if ( timeClient.getEpochTime() > timeStamp ) { 
+
+               // change state 
+
+               if ( switchState == 1 ) {
+
+                       digitalWrite(LED1, R);
+                       digitalWrite(LED2, G);
+                       digitalWrite(LED3, B);
+                       digitalWrite(LED4, W);
+                       
+                       switchState = 0;
+               }
+
+               // blink switchers 
+
+                       // TODO blinking all of them and each with different freq
+
+               // rotate switchers
+
+               if ( rotateTimer >= rotateDelay && rotateDelay != 0 && switchState == 0 ) {
+
+                       uint8_t TMP = R;
+
+                       R = G;
+                       G = B;
+                       B = W;
+                       W = TMP;
+
+                       digitalWrite(LED1, R);
+                       digitalWrite(LED2, G);
+                       digitalWrite(LED3, B);
+                       digitalWrite(LED4, W);
+
+                       rotateTimer = 0;
+       
+                       // TODO rotate switchers
+               }
+       }
+}
diff --git a/4_Channel_Switcher/version.c b/4_Channel_Switcher/version.c
new file mode 100644 (file)
index 0000000..1ba1751
--- /dev/null
@@ -0,0 +1 @@
+#define        VERSION                 "09.05.24"
diff --git a/4_Channel_Switcher/version.h b/4_Channel_Switcher/version.h
new file mode 100644 (file)
index 0000000..1ba1751
--- /dev/null
@@ -0,0 +1 @@
+#define        VERSION                 "09.05.24"
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..de6e2d1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
+# 4 Channel Switcher / LED stripe controller, 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 4 Channel Switcher Dendrite](https://repository.interplaymedium.org/4_Channel_Switcher/IMG_2191.jpg)
+
+## 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 building and flashing firmware at once 
+    ./make 4_Channel_Switcher upload
+
+after that you can build the binary and uload it using remote HTTP interface
+
+    ./make 4_Channel_Switcher  
+    curl im_switch_MACLAST4DIGITS/update -F image=@4_Channel_Switcher.bin
+
+## Usage 
+
+By default dendrite unit can be reached on "`im_switch_[last 4 digits of MAC address]`" 
+
+Change the name of unit by
+
+    curl im_switch_[last 4 digits of MAC address]?host=NEWNAME
+
+Turn on each even switches 
+
+    curl im_switch_[last 4 digits of MAC address]?switch=0101
+
+Other options
+
+    curl im_switch_MACLAST4DIGITS/help
+
+## Todo 
+
+The development of this firmware is in progress. Here is a brief list of upcoming changes: 
+
+* add interface features (save, reset....)
+* state return in 2 variants  
+    txt (default)  
+    JSON  
+    http 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/make b/make
new file mode 100755 (executable)
index 0000000..66b33a0
--- /dev/null
+++ b/make
@@ -0,0 +1,48 @@
+#!/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"
+
+# CORRECT UPLOAD
+# ~/Bin/SDK/ESP/esp8266_2.5.0/tools/esptool/esptool.py  --chip esp8266 --port /dev/ttyUSB0 --baud 115200 write_flash 0x00000 4_Channel_Switcher.bin
+
+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 
+
diff --git a/todo b/todo
new file mode 100644 (file)
index 0000000..b88e080
--- /dev/null
+++ b/todo
@@ -0,0 +1,17 @@
+TODO 
+----------------
+       +       сделать единый обработчик http 
+       -       попробуй увеличить частоту вдвое
+       +       перенос логики PWM (или средствами оригинальной библиотеки попробовать сперва?)
+       +       переименование хоста
+       +-      индикация (BLUE LED)
+                       начала апдейта
+                       error
+       +-      чтение из строки адреса (дублирование в POST и GET) нужно сделать в URL чтобы тоже работало
+       +       отправка данных методом POST и GET
+       -       сохранение RGBW + Rotate в EEPROM
+
+       10.09.2021
+
+               переключение в отдельности каждого (switch1, switch2... )
+               toggle  все вместе и каждый в отдельности
Contact me: dev (at) shalnoff (dot) com
PGP fingerprint: A6B8 3B23 6013 F18A 0C71 198B 83D8 C64D 917A 5717