diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..685006f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +clients/web/node_modules/ diff --git a/Arduino/McLighting/McLighting.ino b/Arduino/McLighting/McLighting.ino new file mode 100644 index 0000000..d753644 --- /dev/null +++ b/Arduino/McLighting/McLighting.ino @@ -0,0 +1,336 @@ +#include "definitions.h" + +// *************************************************************************** +// Load libraries for: WebServer / WiFiManager / WebSockets +// *************************************************************************** +#include //https://github.com/esp8266/Arduino + +// needed for library WiFiManager +#include +#include +#include //https://github.com/tzapu/WiFiManager + +#include +#include +#include + +#include //https://github.com/Links2004/arduinoWebSockets +#include + + +// *************************************************************************** +// Instanciate HTTP(80) / WebSockets(81) Server +// *************************************************************************** +ESP8266WebServer server ( 80 ); +WebSocketsServer webSocket = WebSocketsServer(81); + + +// *************************************************************************** +// Load libraries / Instanciate Neopixel +// *************************************************************************** +#include +#ifdef __AVR__ + #include +#endif + +// Parameter 1 = number of pixels in strip +// Parameter 2 = Arduino pin number (most are valid) +// Parameter 3 = pixel type flags, add together as needed: +// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) +// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) +// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) +// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) +Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMLEDS, PIN, NEO_GRB + NEO_KHZ800); + +// IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across +// pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input +// and minimize distance between Arduino and first pixel. Avoid connecting +// on a live circuit...if you must, connect GND first. + + +// *************************************************************************** +// Load library "ticker" for blinking status led +// *************************************************************************** +#include +Ticker ticker; + +void tick() +{ + //toggle state + int state = digitalRead(BUILTIN_LED); // get the current state of GPIO1 pin + digitalWrite(BUILTIN_LED, !state); // set pin to the opposite state +} + + +// *************************************************************************** +// Callback for WiFiManager library when config mode is entered +// *************************************************************************** +//gets called when WiFiManager enters configuration mode +void configModeCallback (WiFiManager *myWiFiManager) { + DBG_OUTPUT_PORT.println("Entered config mode"); + DBG_OUTPUT_PORT.println(WiFi.softAPIP()); + //if you used auto generated SSID, print it + DBG_OUTPUT_PORT.println(myWiFiManager->getConfigPortalSSID()); + //entered config mode, make led toggle faster + ticker.attach(0.2, tick); +} + + + +// *************************************************************************** +// Include: Webserver +// *************************************************************************** +#include "spiffs_webserver.h" + +// *************************************************************************** +// Include: Request handlers +// *************************************************************************** +#include "request_handlers.h" + +// *************************************************************************** +// Include: Color modes +// *************************************************************************** +#include "colormodes.h" + + + +// *************************************************************************** +// MAIN +// *************************************************************************** +void setup() { + DBG_OUTPUT_PORT.begin(115200); + + // set builtin led pin as output + pinMode(BUILTIN_LED, OUTPUT); + // start ticker with 0.5 because we start in AP mode and try to connect + ticker.attach(0.6, tick); + + // *************************************************************************** + // Setup: WiFiManager + // *************************************************************************** + //Local intialization. Once its business is done, there is no need to keep it around + WiFiManager wifiManager; + //reset settings - for testing + //wifiManager.resetSettings(); + + //set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode + wifiManager.setAPCallback(configModeCallback); + + //fetches ssid and pass and tries to connect + //if it does not connect it starts an access point with the specified name + //here "AutoConnectAP" + //and goes into a blocking loop awaiting configuration + if (!wifiManager.autoConnect()) { + DBG_OUTPUT_PORT.println("failed to connect and hit timeout"); + //reset and try again, or maybe put it to deep sleep + ESP.reset(); + delay(1000); + } + + //if you get here you have connected to the WiFi + DBG_OUTPUT_PORT.println("connected...yeey :)"); + ticker.detach(); + //keep LED on + digitalWrite(BUILTIN_LED, LOW); + + + // *************************************************************************** + // Setup: Neopixel + // *************************************************************************** + strip.begin(); + strip.setBrightness(brightness); + strip.show(); // Initialize all pixels to 'off' + + + // *************************************************************************** + // Setup: MDNS responder + // *************************************************************************** + MDNS.begin(HOSTNAME); + DBG_OUTPUT_PORT.print("Open http://"); + DBG_OUTPUT_PORT.print(HOSTNAME); + DBG_OUTPUT_PORT.println(".local/edit to see the file browser"); + + + // *************************************************************************** + // Setup: WebSocket server + // *************************************************************************** + webSocket.begin(); + webSocket.onEvent(webSocketEvent); + + + // *************************************************************************** + // Setup: SPIFFS + // *************************************************************************** + SPIFFS.begin(); + { + Dir dir = SPIFFS.openDir("/"); + while (dir.next()) { + String fileName = dir.fileName(); + size_t fileSize = dir.fileSize(); + DBG_OUTPUT_PORT.printf("FS File: %s, size: %s\n", fileName.c_str(), formatBytes(fileSize).c_str()); + } + DBG_OUTPUT_PORT.printf("\n"); + } + + // *************************************************************************** + // Setup: SPIFFS Webserver handler + // *************************************************************************** + //list directory + server.on("/list", HTTP_GET, handleFileList); + //load editor + server.on("/edit", HTTP_GET, [](){ + if(!handleFileRead("/edit.htm")) server.send(404, "text/plain", "FileNotFound"); + }); + //create file + server.on("/edit", HTTP_PUT, handleFileCreate); + //delete file + server.on("/edit", HTTP_DELETE, handleFileDelete); + //first callback is called after the request has ended with all parsed arguments + //second callback handles file uploads at that location + server.on("/edit", HTTP_POST, [](){ server.send(200, "text/plain", ""); }, handleFileUpload); + //get heap status, analog input value and all GPIO statuses in one json call + server.on("/status", HTTP_GET, [](){ + String json = "{"; + json += "\"heap\":"+String(ESP.getFreeHeap()); + json += ", \"analog\":"+String(analogRead(A0)); + json += ", \"gpio\":"+String((uint32_t)(((GPI | GPO) & 0xFFFF) | ((GP16I & 0x01) << 16))); + json += "}"; + server.send(200, "text/json", json); + json = String(); + }); + + + //called when the url is not defined here + //use it to load content from SPIFFS + server.onNotFound([](){ + if(!handleFileRead(server.uri())) + handleNotFound(); + }); + + + server.on("/upload", handleMinimalUpload); + + // *************************************************************************** + // Setup: SPIFFS Webserver handler + // *************************************************************************** + server.on("/brightness", []() { + brightness = server.arg("p").toInt(); + if (brightness > 255) { + brightness = 255; + } + if (brightness < 0) { + brightness = 0; + } + strip.setBrightness(brightness); + + if (mode == HOLD) { + mode = ALL; + } + + getStatusJSON(); + }); + + server.on("/status", []() { + getStatusJSON(); + }); + + server.on("/off", []() { + exit_func = true; + mode = OFF; + getArgs(); + getStatusJSON(); + }); + + server.on("/all", []() { + exit_func = true; + mode = ALL; + getArgs(); + getStatusJSON(); + }); + + server.on("/wipe", []() { + exit_func = true; + mode = WIPE; + getArgs(); + getStatusJSON(); + }); + + server.on("/rainbow", []() { + exit_func = true; + mode = RAINBOW; + getArgs(); + getStatusJSON(); + }); + + server.on("/rainbowCycle", []() { + exit_func = true; + mode = RAINBOWCYCLE; + getArgs(); + getStatusJSON(); + }); + + server.on("/theaterchase", []() { + exit_func = true; + mode = THEATERCHASE; + getArgs(); + getStatusJSON(); + }); + + server.on("/theaterchaseRainbow", []() { + exit_func = true; + mode = THEATERCHASERAINBOW; + getArgs(); + getStatusJSON(); + }); + + server.on("/tv", []() { + exit_func = true; + mode = TV; + getArgs(); + getStatusJSON(); + }); + + server.begin(); +} + +void loop() { + server.handleClient(); + webSocket.loop(); + + // Simple statemachine that handles the different modes + if (mode == OFF) { + colorWipe(strip.Color(0, 0, 0), 50); + mode = HOLD; + } + if (mode == ALL) { + uint16_t i; + for (i = 0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, main_color.red, main_color.green, main_color.blue); + } + strip.show(); + mode = HOLD; + } + if (mode == WIPE) { + colorWipe(strip.Color(main_color.red, main_color.green, main_color.blue), delay_ms); + } + if (mode == RAINBOW) { + rainbow(delay_ms); + } + if (mode == RAINBOWCYCLE) { + rainbowCycle(delay_ms); + } + if (mode == THEATERCHASE) { + theaterChase(strip.Color(main_color.red, main_color.green, main_color.blue), delay_ms); + } + if (mode == THEATERCHASERAINBOW) { + theaterChaseRainbow(delay_ms); + } + if (mode == HOLD) { + if (exit_func) { + exit_func = false; + } + } + if (mode == TV) { + tv(); + } +} diff --git a/Arduino/McLighting/colormodes.h b/Arduino/McLighting/colormodes.h new file mode 100644 index 0000000..a077494 --- /dev/null +++ b/Arduino/McLighting/colormodes.h @@ -0,0 +1,218 @@ +// *************************************************************************** +// Color modes +// *************************************************************************** + +int dipInterval = 10; +int darkTime = 250; +unsigned long currentDipTime; +unsigned long dipStartTime; +unsigned long currentMillis; +int ledState = LOW; +long previousMillis = 0; +int led = 5; +int interval = 2000; +int twitch = 50; +int dipCount = 0; +int analogLevel = 100; +boolean timeToDip = false; +int ledStates[12]; + +void hsb2rgbAN1(uint16_t index, uint8_t sat, uint8_t bright, uint8_t myled) { + // Source: https://blog.adafruit.com/2012/03/14/constant-brightness-hsb-to-rgb-algorithm/ + uint8_t temp[5], n = (index >> 8) % 3; + temp[0] = temp[3] = (uint8_t)(( (sat ^ 255) * bright) / 255); + temp[1] = temp[4] = (uint8_t)((((( (index & 255) * sat) / 255) + (sat ^ 255)) * bright) / 255); + temp[2] = (uint8_t)(((((((index & 255) ^ 255) * sat) / 255) + (sat ^ 255)) * bright) / 255); + + strip.setPixelColor(myled, temp[n + 2], temp[n + 1], temp[n]); +} + + +void updateLed (int led, int brightness) { + ledStates[led] = brightness; + + for (int i=0;i<12;i++) + { + uint16_t index = (i%3 == 0) ? 400 : random(0,767); + hsb2rgbAN1(index, 200, ledStates[i], i); + } + strip.show(); +} + + +// Input a value 0 to 255 to get a color value. +// The colours are a transition r - g - b - back to r. +uint32_t Wheel(byte WheelPos) { + WheelPos = 255 - WheelPos; + if (WheelPos < 85) { + return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); + } + if (WheelPos < 170) { + WheelPos -= 85; + return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); + } + WheelPos -= 170; + return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); +} + + +// See: http://forum.mysensors.org/topic/85/phoneytv-for-vera-is-here/13 +void tv() { + checkForRequests(); + if (exit_func) { + exit_func = false; + return; + } + + if (timeToDip == false) + { + currentMillis = millis(); + if(currentMillis-previousMillis > interval) + { + previousMillis = currentMillis; + interval = random(750,4001);//Adjusts the interval for more/less frequent random light changes + twitch = random(40,100);// Twitch provides motion effect but can be a bit much if too high + dipCount = dipCount++; + } + if(currentMillis-previousMillis dipInterval) + { + DBG_OUTPUT_PORT.println("dip"); + timeToDip = true; + dipCount = 0; + dipStartTime = millis(); + darkTime = random(50,150); + dipInterval = random(5,250);// cycles of flicker + } + //strip.show(); + } + } + else + { + DBG_OUTPUT_PORT.println("Dip Time"); + currentDipTime = millis(); + if (currentDipTime - dipStartTime < darkTime) + { + for (int i=3;i<9;i++) + { + updateLed (i, 0); + } + } + else + { + timeToDip = false; + } + strip.show(); + } +} + + +// Fill the dots one after the other with a color +void colorWipe(uint32_t c, uint8_t wait) { + for (uint16_t i = 0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, c); + strip.show(); + + checkForRequests(); + if (exit_func) { + exit_func = false; + return; + } + + delay(wait); + } + mode = HOLD; +} + +void rainbow(uint8_t wait) { + uint16_t i, j; + + for (j = 0; j < 256; j++) { + for (i = 0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, Wheel((i + j) & 255)); + } + strip.show(); + + checkForRequests(); + if (exit_func) { + exit_func = false; + return; + } + + delay(wait); + } +} + +// Slightly different, this makes the rainbow equally distributed throughout +void rainbowCycle(uint8_t wait) { + uint16_t i, j; + + for (j = 0; j < 256; j++) { // 1 cycle of all colors on wheel + for (i = 0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255)); + } + strip.show(); + + checkForRequests(); + if (exit_func) { + exit_func = false; + return; + } + + delay(wait); + } +} + +// Theatre-style crawling lights. +void theaterChase(uint32_t c, uint8_t wait) { + for (int q = 0; q < 3; q++) { + for (int i = 0; i < strip.numPixels(); i = i + 3) { + strip.setPixelColor(i + q, c); //turn every third pixel on + } + strip.show(); + + checkForRequests(); + if (exit_func) { + exit_func = false; + return; + } + delay(wait); + + for (int i = 0; i < strip.numPixels(); i = i + 3) { + strip.setPixelColor(i + q, 0); //turn every third pixel off + } + } +} + +// Theatre-style crawling lights with rainbow effect +void theaterChaseRainbow(uint8_t wait) { + for (int j = 0; j < 256; j++) { // cycle all 256 colors in the wheel + for (int q = 0; q < 3; q++) { + for (int i = 0; i < strip.numPixels(); i = i + 3) { + strip.setPixelColor(i + q, Wheel( (i + j) % 255)); //turn every third pixel on + } + strip.show(); + + checkForRequests(); + if (exit_func) { + exit_func = false; + return; + } + delay(wait); + + for (int i = 0; i < strip.numPixels(); i = i + 3) { + strip.setPixelColor(i + q, 0); //turn every third pixel off + } + } + } +} + + + diff --git a/Arduino/McLighting/data/edit.htm.gz b/Arduino/McLighting/data/edit.htm.gz new file mode 100644 index 0000000..69ce414 Binary files /dev/null and b/Arduino/McLighting/data/edit.htm.gz differ diff --git a/Arduino/McLighting/data/favicon.ico b/Arduino/McLighting/data/favicon.ico new file mode 100644 index 0000000..71b25fe Binary files /dev/null and b/Arduino/McLighting/data/favicon.ico differ diff --git a/Arduino/McLighting/data/graphs.js.gz b/Arduino/McLighting/data/graphs.js.gz new file mode 100644 index 0000000..7243544 Binary files /dev/null and b/Arduino/McLighting/data/graphs.js.gz differ diff --git a/Arduino/McLighting/data/index.htm b/Arduino/McLighting/data/index.htm new file mode 100644 index 0000000..6aa58cc --- /dev/null +++ b/Arduino/McLighting/data/index.htm @@ -0,0 +1,97 @@ + + + + + + ESP Monitor + + + + +
+ + + + +
+
+
+
+ + \ No newline at end of file diff --git a/Arduino/McLighting/definitions.h b/Arduino/McLighting/definitions.h new file mode 100644 index 0000000..9a4d2fe --- /dev/null +++ b/Arduino/McLighting/definitions.h @@ -0,0 +1,33 @@ +// Neopixel +#define PIN 5 // PIN where neopixel / WS2811 strip is attached +#define NUMLEDS 12 // Number of leds in the strip + + +#define HOSTNAME "ESP8266_01" // Friedly hostname + + +// *************************************************************************** +// Global variables / definitions +// *************************************************************************** +#define DBG_OUTPUT_PORT Serial // Set debug output port + +// List of all color modes +enum MODE { HOLD, OFF, ALL, WIPE, RAINBOW, RAINBOWCYCLE, THEATERCHASE, THEATERCHASERAINBOW, TV }; + +MODE mode = RAINBOWCYCLE; // Standard mode that is active when software starts + +int delay_ms = 50; // Global variable for storing the delay between color changes --> smaller == faster +int brightness = 128; // Global variable for storing the brightness (255 == 100%) + +bool exit_func = false; // Global helper variable to get out of the color modes when mode changes + +struct ledstate // Data structure to store a state of a single led +{ + uint8_t red; + uint8_t green; + uint8_t blue; +}; + +typedef struct ledstate LEDState; // Define the datatype LEDState +LEDState ledstates[NUMLEDS]; // Get an array of led states to store the state of the whole strip +LEDState main_color; // Store the "main color" of the strip used in single color modes diff --git a/Arduino/McLighting/request_handlers.h b/Arduino/McLighting/request_handlers.h new file mode 100644 index 0000000..b82808d --- /dev/null +++ b/Arduino/McLighting/request_handlers.h @@ -0,0 +1,205 @@ +// *************************************************************************** +// Request handlers +// *************************************************************************** +void getArgs() { + main_color.red = server.arg("r").toInt(); + main_color.green = server.arg("g").toInt(); + main_color.blue = server.arg("b").toInt(); + delay_ms = server.arg("d").toInt(); + + if (main_color.red > 255) { + main_color.red = 255; + } + if (main_color.green > 255) { + main_color.green = 255; + } + if (main_color.blue > 255) { + main_color.blue = 255; + } + + if (main_color.red < 0) { + main_color.red = 0; + } + if (main_color.green < 0) { + main_color.green = 0; + } + if (main_color.blue < 0) { + main_color.blue = 0; + } + + if (server.arg("d") == "") { + delay_ms = 20; + } + + DBG_OUTPUT_PORT.print("Mode: "); + DBG_OUTPUT_PORT.print(mode); + DBG_OUTPUT_PORT.print(", Color: "); + DBG_OUTPUT_PORT.print(main_color.red); + DBG_OUTPUT_PORT.print(", "); + DBG_OUTPUT_PORT.print(main_color.green); + DBG_OUTPUT_PORT.print(", "); + DBG_OUTPUT_PORT.print(main_color.blue); + DBG_OUTPUT_PORT.print(", Delay:"); + DBG_OUTPUT_PORT.print(delay_ms); + DBG_OUTPUT_PORT.print(", Brightness:"); + DBG_OUTPUT_PORT.println(brightness); +} + +void handleMinimalUpload() { + char temp[1500]; + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + + snprintf ( temp, 1500, + "\ + \ + \ + ESP8266 Upload\ + \ + \ + \ + \ + \ +
\ + \ + \ + \ +
\ + \ + ", + hr, min % 60, sec % 60 + ); + server.send ( 200, "text/html", temp ); +} + +void handleNotFound() { + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += ( server.method() == HTTP_GET ) ? "GET" : "POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + for ( uint8_t i = 0; i < server.args(); i++ ) { + message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n"; + } + server.send ( 404, "text/plain", message ); +} + +void getStatusJSON() { + char json[255]; + snprintf(json, sizeof(json), "{\"mode\":%d, \"delay_ms\":%d, \"brightness\":%d, \"color\":[%d, %d, %d]}", mode, delay_ms, brightness, main_color.red, main_color.green, main_color.blue); + server.send ( 200, "application/json", json ); +} + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { + switch(type) { + case WStype_DISCONNECTED: + DBG_OUTPUT_PORT.printf("WS: [%u] Disconnected!\n", num); + break; + + case WStype_CONNECTED: { + IPAddress ip = webSocket.remoteIP(num); + DBG_OUTPUT_PORT.printf("WS: [%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); + + // send message to client + webSocket.sendTXT(num, "Connected"); + } + break; + + case WStype_TEXT: + DBG_OUTPUT_PORT.printf("WS: [%u] get Text: %s\n", num, payload); + + // # ==> Set main color + if(payload[0] == '#') { + // decode rgb data + uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); + main_color.red = ((rgb >> 16) & 0xFF); + main_color.green = ((rgb >> 8) & 0xFF); + main_color.blue = ((rgb >> 0) & 0xFF); + DBG_OUTPUT_PORT.printf("Set main color to: [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue); + } + + // # ==> Set delay + if(payload[0] == '?') { + // decode rgb data + uint8_t d = (uint8_t) strtol((const char *) &payload[1], NULL, 16); + delay_ms = ((d >> 0) & 0xFF); + DBG_OUTPUT_PORT.printf("WS: Set delay to: [%u]\n", delay_ms); + } + + // * ==> Set main color and light all LEDs (Shortcut) + if(payload[0] == '*') { + // decode rgb data + uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16); + + main_color.red = ((rgb >> 16) & 0xFF); + main_color.green = ((rgb >> 8) & 0xFF); + main_color.blue = ((rgb >> 0) & 0xFF); + + for (int i = 0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, main_color.red, main_color.green, main_color.blue); + } + strip.show(); + DBG_OUTPUT_PORT.printf("WS: Set all leds to main color: [%u] [%u] [%u]\n", main_color.red, main_color.green, main_color.blue); + mode = HOLD; + } + + // ! ==> Set single LED in given color + if(payload[0] == '!') { + // decode led index + uint64_t rgb = (uint64_t) strtol((const char *) &payload[1], NULL, 16); + + uint8_t led = ((rgb >> 24) & 0xFF); + if (led < strip.numPixels()) { + ledstates[led].red = ((rgb >> 16) & 0xFF); + ledstates[led].green = ((rgb >> 8) & 0xFF); + ledstates[led].blue = ((rgb >> 0) & 0xFF); + DBG_OUTPUT_PORT.printf("WS: Set single led [%u] to [%u] [%u] [%u]!\n", led, ledstates[led].red, ledstates[led].green, ledstates[led].blue); + + for (uint8_t i = 0; i < strip.numPixels(); i++) { + strip.setPixelColor(i, ledstates[i].red, ledstates[i].green, ledstates[i].blue); + //DBG_OUTPUT_PORT.printf("[%u]--[%u] [%u] [%u] [%u] LED index!\n", rgb, i, ledstates[i].red, ledstates[i].green, ledstates[i].blue); + } + strip.show(); + } + mode = HOLD; + } + + // ! ==> Activate mode + if(payload[0] == '=') { + // we get mode data + String str_mode = String((char *) &payload[0]); + + exit_func = true; + if (str_mode.startsWith("=wipe")) { + mode = WIPE; + } + if (str_mode.startsWith("=rainbow")) { + mode = RAINBOW; + } + if (str_mode.startsWith("=rainbowCycle")) { + mode = RAINBOWCYCLE; + } + if (str_mode.startsWith("=theaterchase")) { + mode = THEATERCHASE; + } + if (str_mode.startsWith("=theaterchaseRainbow")) { + mode = THEATERCHASERAINBOW; + } + if (str_mode.startsWith("=tv")) { + mode = TV; + } + + DBG_OUTPUT_PORT.printf("Activated mode [%u]!\n", mode); + } + break; + } +} + +void checkForRequests() { + webSocket.loop(); + server.handleClient(); +} diff --git a/Arduino/McLighting/spiffs_webserver.h b/Arduino/McLighting/spiffs_webserver.h new file mode 100644 index 0000000..c68af2f --- /dev/null +++ b/Arduino/McLighting/spiffs_webserver.h @@ -0,0 +1,158 @@ +// *************************************************************************** +// SPIFFS Webserver +// Source: https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer/examples/FSBrowser +// *************************************************************************** + +/* + FSWebServer - Example WebServer with SPIFFS backend for esp8266 + Copyright (c) 2015 Hristo Gochkov. All rights reserved. + This file is part of the ESP8266WebServer library for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + upload the contents of the data folder with MkSPIFFS Tool ("ESP8266 Sketch Data Upload" in Tools menu in Arduino IDE) + or you can upload the contents of a folder if you CD in that folder and run the following command: + for file in `ls -A1`; do curl -F "file=@$PWD/$file" esp8266fs.local/edit; done + + access the sample web page at http://esp8266fs.local + edit the page by going to http://esp8266fs.local/edit +*/ + +File fsUploadFile; + +//format bytes +String formatBytes(size_t bytes) { + if (bytes < 1024) { + return String(bytes) + "B"; + } else if (bytes < (1024 * 1024)) { + return String(bytes / 1024.0) + "KB"; + } else if (bytes < (1024 * 1024 * 1024)) { + return String(bytes / 1024.0 / 1024.0) + "MB"; + } else { + return String(bytes / 1024.0 / 1024.0 / 1024.0) + "GB"; + } +} + +String getContentType(String filename) { + if (server.hasArg("download")) return "application/octet-stream"; + else if (filename.endsWith(".htm")) return "text/html"; + else if (filename.endsWith(".html")) return "text/html"; + else if (filename.endsWith(".css")) return "text/css"; + else if (filename.endsWith(".js")) return "application/javascript"; + else if (filename.endsWith(".png")) return "image/png"; + else if (filename.endsWith(".gif")) return "image/gif"; + else if (filename.endsWith(".jpg")) return "image/jpeg"; + else if (filename.endsWith(".ico")) return "image/x-icon"; + else if (filename.endsWith(".xml")) return "text/xml"; + else if (filename.endsWith(".pdf")) return "application/x-pdf"; + else if (filename.endsWith(".zip")) return "application/x-zip"; + else if (filename.endsWith(".gz")) return "application/x-gzip"; + return "text/plain"; +} + +bool handleFileRead(String path) { + DBG_OUTPUT_PORT.println("handleFileRead: " + path); + if (path.endsWith("/")) path += "index.htm"; + String contentType = getContentType(path); + String pathWithGz = path + ".gz"; + if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) { + if (SPIFFS.exists(pathWithGz)) + path += ".gz"; + File file = SPIFFS.open(path, "r"); + size_t sent = server.streamFile(file, contentType); + file.close(); + return true; + } + return false; +} + +void handleFileUpload() { + if (server.uri() != "/edit") return; + HTTPUpload& upload = server.upload(); + if (upload.status == UPLOAD_FILE_START) { + String filename = upload.filename; + if (!filename.startsWith("/")) filename = "/" + filename; + DBG_OUTPUT_PORT.print("handleFileUpload Name: "); + DBG_OUTPUT_PORT.println(filename); + fsUploadFile = SPIFFS.open(filename, "w"); + filename = String(); + } else if (upload.status == UPLOAD_FILE_WRITE) { + //DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize); + if (fsUploadFile) + fsUploadFile.write(upload.buf, upload.currentSize); + } else if (upload.status == UPLOAD_FILE_END) { + if (fsUploadFile) + fsUploadFile.close(); + DBG_OUTPUT_PORT.print("handleFileUpload Size: "); DBG_OUTPUT_PORT.println(upload.totalSize); + } +} + +void handleFileDelete() { + if (server.args() == 0) return server.send(500, "text/plain", "BAD ARGS"); + String path = server.arg(0); + DBG_OUTPUT_PORT.println("handleFileDelete: " + path); + if (path == "/") + return server.send(500, "text/plain", "BAD PATH"); + if (!SPIFFS.exists(path)) + return server.send(404, "text/plain", "FileNotFound"); + SPIFFS.remove(path); + server.send(200, "text/plain", ""); + path = String(); +} + +void handleFileCreate() { + if (server.args() == 0) + return server.send(500, "text/plain", "BAD ARGS"); + String path = server.arg(0); + DBG_OUTPUT_PORT.println("handleFileCreate: " + path); + if (path == "/") + return server.send(500, "text/plain", "BAD PATH"); + if (SPIFFS.exists(path)) + return server.send(500, "text/plain", "FILE EXISTS"); + File file = SPIFFS.open(path, "w"); + if (file) + file.close(); + else + return server.send(500, "text/plain", "CREATE FAILED"); + server.send(200, "text/plain", ""); + path = String(); +} + +void handleFileList() { + if (!server.hasArg("dir")) { + server.send(500, "text/plain", "BAD ARGS"); + return; + } + + String path = server.arg("dir"); + DBG_OUTPUT_PORT.println("handleFileList: " + path); + Dir dir = SPIFFS.openDir(path); + path = String(); + + String output = "["; + while (dir.next()) { + File entry = dir.openFile("r"); + if (output != "[") output += ','; + bool isDir = false; + output += "{\"type\":\""; + output += (isDir) ? "dir" : "file"; + output += "\",\"name\":\""; + output += String(entry.name()).substring(1); + output += "\"}"; + entry.close(); + } + + output += "]"; + server.send(200, "text/json", output); +} + diff --git a/README.md b/README.md index 097d04e..8cd180b 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,69 @@ - A WS2811 or WS2812 led strip that you can get in many sizes and forms. I'm using a ring of 12 leds. When you use more than about 15-20 leds you may have to use a dedicated 5V power source. - Power via USB +## Wiring + +Fritzing: + +Parts via: +- https://github.com/squix78/esp8266-fritzing-parts +- https://github.com/adafruit/Fritzing-Library/ + +## Software installation +You need to complete the following steps to build your development environment that enables you to flash the Mc Lighting software to the ESP8622. + +### Arduino Software (tested with 1.6.8) +Download and install the arduino software (IDE) at https://www.arduino.cc/en/Main/Software + +### ESP8266 board support for arduino IDE +In the Arduino IDE open the preferences dialog and enter the following URL as "Additional Boards Manger URL":\ +http://arduino.esp8266.com/stable/package_esp8266com_index.json +--> arduino_preferences.png + +Go to "Tools" > "Board: " > "Boards Manager ...", search for "esp" and install the "esp8266 by ESP8266 Community" in version 2.2.0 (https://github.com/esp8266/Arduino): +--> arduino_boards_manager.png + +Now go to "Tools" > "Board: " and choose "NodeMCU 1.0 (ESP-12E Module)", set CPU frequency to 80 MHz, and Flash size to "4M (1M SPIFFS)"leave upload spped at 115200. Select the right COM port. + +### Used Libraries +Go to "Sketch" > "Include Library" > "Manage LIbraries ..." and install the following libraries by searching for them and installing: +- WiFiManager by @tzapu (tested with version 0.11.0) + https://github.com/tzapu/WiFiManager +- WebSockets by @Links2004 (tested with version 2.0.2) + https://github.com/Links2004/arduinoWebSockets +- Adafruit NeoPixel by @adafruit (tested with 1.0.5) + https://github.com/adafruit/Adafruit_NeoPixel + +The sketch also uses the following built-in library: +- Ticker by @igrr + +Parts of the code were taken or inspired by the following sources: +- HSB3RGB conversion + https://blog.adafruit.com/2012/03/14/constant-brightness-hsb-to-rgb-algorithm/ +- TV simulator logic inspired by @BulldogLowell + https://github.com/BulldogLowell/PhoneyTV +- SPIFS Webserver by Hristo Gochkov + https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer/examples/FSBrowser + +Thank you to all the authors for distributing their software that way. +I hope I didn't miss any sources and mentioned every author. In case I forgot someone please let me know and I will fix it. + +### Compiling and upload +Now open the MC Lighting Arduino sketch in the IDE via "File" > "Open ...". Have a look at the "definitions.h" and change the values here to fit your setup: +```c +// Neopixel +#define PIN 5 // PIN where neopixel / WS2811 strip is attached +#define NUMLEDS 12 // Number of leds in the strip + +#define HOSTNAME "ESP8266_01" // Friedly hostname +``` + +Now you have done everything to get all the dependencies. You should now be able to build the software by choosing "Sketch" > "Verify / Compile" (or clicking the tick mark in the tool bar). + +Please verify that you have connected the ESP board correctly to your computer via USB and that the correct COM port is chosen. + +Now you should be able to upload the compiled sketch to the board via "Sketch" > "Upload" (or by clicking the right arrow in the tool bar). + ## Todos - [x] Fix issue with websockets connection problems - [ ] Switch to the [NeoPixelBus library](https://github.com/Makuna/NeoPixelBus/wiki) @@ -23,4 +86,4 @@ -*More information will be added as soon as I clean up the code.* +*More information will be added as soon as I clean up the code and complete documentation.* diff --git a/clients/web/build/index.html b/clients/web/build/index.html new file mode 100644 index 0000000..a374a35 --- /dev/null +++ b/clients/web/build/index.html @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Fehler beim Herstellen der WebSocket-Verbindung.
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
pick a color
+
+
+
+
+
+
+ +
+
+ + +
+
+
+
+
+

+
+
+
+

+
+
+
+

+
+
+ +
+
+
+

+
+
+ +
+
+
+

+
+
+
+
+
+
+ +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/clients/web/build/js/script.js b/clients/web/build/js/script.js new file mode 100644 index 0000000..25fc4fc --- /dev/null +++ b/clients/web/build/js/script.js @@ -0,0 +1,211 @@ +(function($){ + $(function(){ + + var host = "192.168.0.24"; + + $('.button-collapse').sideNav(); + + + // Navlinks + $('#mc-nav').on('click', '.mc-navlink', function(){ + console.log("Nav to: ", $(this).data("pane")); + showPane($(this).data("pane")); + }); + + function showPane(pane) { + $('.mc_pane').addClass('hide'); + $('#' + pane).removeClass('hide'); + $('.button-collapse').sideNav('hide'); + } + + + // ****************************************************************** + // init() + // ****************************************************************** + var connection = new WebSocket('ws://' + host + ':81', ['arduino']); + + // When the connection is open, send some data to the server + connection.onopen = function () { + //connection.send('Ping'); // Send the message 'Ping' to the server + console.log('WebSocket Open'); + showPane('pane1'); + }; + + // Log errors + connection.onerror = function (error) { + console.log('WebSocket Error ' + error); + $('#mc-wsloader').addClass('hide'); + $('#mc-wserror').removeClass('hide'); + }; + + // Log messages from the server + connection.onmessage = function (e) { + console.log('Server: ' + e.data); + }; + + + // ****************************************************************** + // Modes + // ****************************************************************** + $("#pane2").on("click", ".btn_mode", function() { + var mode = $(this).attr("data-mode"); + last_mode = mode; + var btn = $(this); + setMode(mode, function() { + $(".btn_mode").removeClass("blue"); + btn.addClass("blue"); + }); + }); + + function setMode(mode, finish_funtion) { + var url = "http://" + host + "/" + mode; + console.log("Mode: ", mode); + + var red = $("#rng_red").val(); + var green = $("#rng_green").val(); + var blue = $("#rng_blue").val(); + var delay = $("#rng_delay").val(); + + var params = {"r":red, "g":green, "b":blue, "d":delay}; + connection.send("=" + mode); + + /* + $.getJSON(url, params, function(data) { + updateStatus(data); + finish_funtion(); + }); + */ + } + + function updateStatus(data) { + console.log("Returned: ", data); + $("#result").val("Mode: " + data.mode + "\nColor: "+ data.color[0] + "," + data.color[1] + "," + data.color[2] + "\nDelay:" + data.delay_ms + "\nBrightness:" + data.brightness); + $('#result').trigger('autoresize'); + } + + + // ****************************************************************** + // Colorwheel + // ****************************************************************** + // this is supposed to work on mobiles (touch) as well as on a desktop (click) + // since we couldn't find a decent one .. this try of writing one by myself + // + google. swiping would be really nice - I will possibly implement it with + // jquery later - or never. + + var canvas = document.getElementById("myCanvas"); + // FIX: Cancel touch end event and handle click via touchstart + // canvas.addEventListener("touchend", function(e) { e.preventDefault(); }, false); + canvas.addEventListener("touchmove", doTouch, false); + canvas.addEventListener("click", doClick, false); + + var context = canvas.getContext('2d'); + var centerX = canvas.width / 2; + var centerY = canvas.height / 2; + var innerRadius = canvas.width / 4.5; + var outerRadius = (canvas.width - 10) / 2 + + //outer border + context.beginPath(); + //outer circle + context.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI, false); + //draw the outer border: (gets drawn around the circle!) + context.lineWidth = 4; + context.strokeStyle = '#000000'; + context.stroke(); + context.closePath(); + + //fill with beautiful colors + //taken from here: http://stackoverflow.com/questions/18265804/building-a-color-wheel-in-html5 + for(var angle=0; angle<=360; angle+=1) { + var startAngle = (angle-2)*Math.PI/180; + var endAngle = angle * Math.PI/180; + context.beginPath(); + context.moveTo(centerX, centerY); + context.arc(centerX, centerY, outerRadius, startAngle, endAngle, false); + context.closePath(); + context.fillStyle = 'hsl('+angle+', 100%, 50%)'; + context.fill(); + context.closePath(); + } + + //inner border + context.beginPath(); + //context.arc(centerX, centerY, radius, startAngle, endAngle, counterClockwise); + context.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI, false); + //fill the center + var my_gradient=context.createLinearGradient(0,0,170,0); + my_gradient.addColorStop(0,"black"); + my_gradient.addColorStop(1,"white"); + + context.fillStyle = my_gradient; + context.fillStyle = "white"; + context.fill(); + + //draw the inner line + context.lineWidth = 2; + context.strokeStyle = '#000000'; + context.stroke(); + context.closePath(); + + //get Mouse x/y canvas position + function getMousePos(canvas, evt) { + var rect = canvas.getBoundingClientRect(); + return { + x: evt.clientX - rect.left, + y: evt.clientY - rect.top + }; + } + + //comp to Hex + function componentToHex(c) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; + } + + //rgb/rgba to Hex + function rgbToHex(rgb) { + return "#" + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]); + } + + //display the touch/click position and color info + function showStatus(pos, color) { + var hexColor = rgbToHex(color); + + connection.send("*" + componentToHex(color[0]) + componentToHex(color[1]) + componentToHex(color[2])); + + $('#status').css("backgroundColor", hexColor); + $('#status_color').text(hexColor + " - R=" + color[0] + ", G=" + color[1] + ", B=" + color[2]); + $('#status_pos').text("x: " + pos.x + " - y: " + pos.y); + document.getElementById('status').style.backgroundColor=hexColor; + } + + //handle the touch event + function doTouch(event) { + //to not also fire on click + event.preventDefault(); + var el = event.target; + + //touch position + var pos = {x: Math.round(event.targetTouches[0].pageX - el.offsetLeft), + y: Math.round(event.targetTouches[0].pageY - el.offsetTop)}; + //color + var color = context.getImageData(pos.x, pos.y, 1, 1).data; + + showStatus(pos, color); + } + + function doClick(event) { + //click position + var pos = getMousePos(canvas, event); + //color + var color = context.getImageData(pos.x, pos.y, 1, 1).data; + + console.log("click", pos.x, pos.y, color); + showStatus(pos, color); + + //now do sth with the color rgbToHex(color); + //don't do stuff when #000000 (outside circle and lines + } + + }); // end of document ready +})(jQuery); // end of jQuery name space \ No newline at end of file diff --git a/clients/web/gulpfile.js b/clients/web/gulpfile.js new file mode 100644 index 0000000..fe4d475 --- /dev/null +++ b/clients/web/gulpfile.js @@ -0,0 +1,28 @@ +var gulp = require('gulp'), + connect = require('gulp-connect'); + +gulp.task('html', function() { + gulp.src('*.html') + .pipe(gulp.dest('build')) + .pipe(connect.reload()); +}); + +gulp.task('js', function() { + gulp.src('js/*.js') + .pipe(gulp.dest('build/js')) + .pipe(connect.reload()); +}); + +gulp.task('connect', function() { + connect.server({ + root: 'build', + livereload: true + }); +}); + +gulp.task('watch', function() { + gulp.watch('*.html', ['html']); + gulp.watch('js/*.js', ['js']); +}); + +gulp.task('default', ['watch', 'connect']); \ No newline at end of file diff --git a/clients/web/index.html b/clients/web/index.html new file mode 100644 index 0000000..a374a35 --- /dev/null +++ b/clients/web/index.html @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Fehler beim Herstellen der WebSocket-Verbindung.
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
pick a color
+
+
+
+
+
+
+ +
+
+ + +
+
+
+
+
+

+
+
+
+

+
+
+
+

+
+
+ +
+
+
+

+
+
+ +
+
+
+

+
+
+
+
+
+
+ +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/clients/web/js/script.js b/clients/web/js/script.js new file mode 100644 index 0000000..25fc4fc --- /dev/null +++ b/clients/web/js/script.js @@ -0,0 +1,211 @@ +(function($){ + $(function(){ + + var host = "192.168.0.24"; + + $('.button-collapse').sideNav(); + + + // Navlinks + $('#mc-nav').on('click', '.mc-navlink', function(){ + console.log("Nav to: ", $(this).data("pane")); + showPane($(this).data("pane")); + }); + + function showPane(pane) { + $('.mc_pane').addClass('hide'); + $('#' + pane).removeClass('hide'); + $('.button-collapse').sideNav('hide'); + } + + + // ****************************************************************** + // init() + // ****************************************************************** + var connection = new WebSocket('ws://' + host + ':81', ['arduino']); + + // When the connection is open, send some data to the server + connection.onopen = function () { + //connection.send('Ping'); // Send the message 'Ping' to the server + console.log('WebSocket Open'); + showPane('pane1'); + }; + + // Log errors + connection.onerror = function (error) { + console.log('WebSocket Error ' + error); + $('#mc-wsloader').addClass('hide'); + $('#mc-wserror').removeClass('hide'); + }; + + // Log messages from the server + connection.onmessage = function (e) { + console.log('Server: ' + e.data); + }; + + + // ****************************************************************** + // Modes + // ****************************************************************** + $("#pane2").on("click", ".btn_mode", function() { + var mode = $(this).attr("data-mode"); + last_mode = mode; + var btn = $(this); + setMode(mode, function() { + $(".btn_mode").removeClass("blue"); + btn.addClass("blue"); + }); + }); + + function setMode(mode, finish_funtion) { + var url = "http://" + host + "/" + mode; + console.log("Mode: ", mode); + + var red = $("#rng_red").val(); + var green = $("#rng_green").val(); + var blue = $("#rng_blue").val(); + var delay = $("#rng_delay").val(); + + var params = {"r":red, "g":green, "b":blue, "d":delay}; + connection.send("=" + mode); + + /* + $.getJSON(url, params, function(data) { + updateStatus(data); + finish_funtion(); + }); + */ + } + + function updateStatus(data) { + console.log("Returned: ", data); + $("#result").val("Mode: " + data.mode + "\nColor: "+ data.color[0] + "," + data.color[1] + "," + data.color[2] + "\nDelay:" + data.delay_ms + "\nBrightness:" + data.brightness); + $('#result').trigger('autoresize'); + } + + + // ****************************************************************** + // Colorwheel + // ****************************************************************** + // this is supposed to work on mobiles (touch) as well as on a desktop (click) + // since we couldn't find a decent one .. this try of writing one by myself + // + google. swiping would be really nice - I will possibly implement it with + // jquery later - or never. + + var canvas = document.getElementById("myCanvas"); + // FIX: Cancel touch end event and handle click via touchstart + // canvas.addEventListener("touchend", function(e) { e.preventDefault(); }, false); + canvas.addEventListener("touchmove", doTouch, false); + canvas.addEventListener("click", doClick, false); + + var context = canvas.getContext('2d'); + var centerX = canvas.width / 2; + var centerY = canvas.height / 2; + var innerRadius = canvas.width / 4.5; + var outerRadius = (canvas.width - 10) / 2 + + //outer border + context.beginPath(); + //outer circle + context.arc(centerX, centerY, outerRadius, 0, 2 * Math.PI, false); + //draw the outer border: (gets drawn around the circle!) + context.lineWidth = 4; + context.strokeStyle = '#000000'; + context.stroke(); + context.closePath(); + + //fill with beautiful colors + //taken from here: http://stackoverflow.com/questions/18265804/building-a-color-wheel-in-html5 + for(var angle=0; angle<=360; angle+=1) { + var startAngle = (angle-2)*Math.PI/180; + var endAngle = angle * Math.PI/180; + context.beginPath(); + context.moveTo(centerX, centerY); + context.arc(centerX, centerY, outerRadius, startAngle, endAngle, false); + context.closePath(); + context.fillStyle = 'hsl('+angle+', 100%, 50%)'; + context.fill(); + context.closePath(); + } + + //inner border + context.beginPath(); + //context.arc(centerX, centerY, radius, startAngle, endAngle, counterClockwise); + context.arc(centerX, centerY, innerRadius, 0, 2 * Math.PI, false); + //fill the center + var my_gradient=context.createLinearGradient(0,0,170,0); + my_gradient.addColorStop(0,"black"); + my_gradient.addColorStop(1,"white"); + + context.fillStyle = my_gradient; + context.fillStyle = "white"; + context.fill(); + + //draw the inner line + context.lineWidth = 2; + context.strokeStyle = '#000000'; + context.stroke(); + context.closePath(); + + //get Mouse x/y canvas position + function getMousePos(canvas, evt) { + var rect = canvas.getBoundingClientRect(); + return { + x: evt.clientX - rect.left, + y: evt.clientY - rect.top + }; + } + + //comp to Hex + function componentToHex(c) { + var hex = c.toString(16); + return hex.length == 1 ? "0" + hex : hex; + } + + //rgb/rgba to Hex + function rgbToHex(rgb) { + return "#" + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]); + } + + //display the touch/click position and color info + function showStatus(pos, color) { + var hexColor = rgbToHex(color); + + connection.send("*" + componentToHex(color[0]) + componentToHex(color[1]) + componentToHex(color[2])); + + $('#status').css("backgroundColor", hexColor); + $('#status_color').text(hexColor + " - R=" + color[0] + ", G=" + color[1] + ", B=" + color[2]); + $('#status_pos').text("x: " + pos.x + " - y: " + pos.y); + document.getElementById('status').style.backgroundColor=hexColor; + } + + //handle the touch event + function doTouch(event) { + //to not also fire on click + event.preventDefault(); + var el = event.target; + + //touch position + var pos = {x: Math.round(event.targetTouches[0].pageX - el.offsetLeft), + y: Math.round(event.targetTouches[0].pageY - el.offsetTop)}; + //color + var color = context.getImageData(pos.x, pos.y, 1, 1).data; + + showStatus(pos, color); + } + + function doClick(event) { + //click position + var pos = getMousePos(canvas, event); + //color + var color = context.getImageData(pos.x, pos.y, 1, 1).data; + + console.log("click", pos.x, pos.y, color); + showStatus(pos, color); + + //now do sth with the color rgbToHex(color); + //don't do stuff when #000000 (outside circle and lines + } + + }); // end of document ready +})(jQuery); // end of jQuery name space \ No newline at end of file diff --git a/clients/web/package.json b/clients/web/package.json new file mode 100644 index 0000000..d811855 --- /dev/null +++ b/clients/web/package.json @@ -0,0 +1,15 @@ +{ + "name": "mc_lighting", + "version": "1.0.0", + "description": "Web client for Mc Lighting", + "main": "index.html", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Tobias Blum", + "license": "MIT", + "dependencies": { + "gulp-connect": "^4.0.0", + "materialize-css": "^0.97.6" + } +} diff --git a/documentation/fritzing/McLighting-NodeMCU_Board.fzz b/documentation/fritzing/McLighting-NodeMCU_Board.fzz new file mode 100644 index 0000000..9dce0f1 Binary files /dev/null and b/documentation/fritzing/McLighting-NodeMCU_Board.fzz differ diff --git a/documentation/pics/McLighting-NodeMCU_Board.png b/documentation/pics/McLighting-NodeMCU_Board.png new file mode 100644 index 0000000..3072152 Binary files /dev/null and b/documentation/pics/McLighting-NodeMCU_Board.png differ diff --git a/documentation/pics/arduino_boards_manager.png b/documentation/pics/arduino_boards_manager.png new file mode 100644 index 0000000..13934f5 Binary files /dev/null and b/documentation/pics/arduino_boards_manager.png differ diff --git a/documentation/pics/arduino_preferences.png b/documentation/pics/arduino_preferences.png new file mode 100644 index 0000000..c8c67a9 Binary files /dev/null and b/documentation/pics/arduino_preferences.png differ