Initial commit with Arduino and client
This commit is contained in:
parent
1004bb979f
commit
740239b983
21 changed files with 1937 additions and 1 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
clients/web/node_modules/
|
336
Arduino/McLighting/McLighting.ino
Normal file
336
Arduino/McLighting/McLighting.ino
Normal file
|
@ -0,0 +1,336 @@
|
|||
#include "definitions.h"
|
||||
|
||||
// ***************************************************************************
|
||||
// Load libraries for: WebServer / WiFiManager / WebSockets
|
||||
// ***************************************************************************
|
||||
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
|
||||
|
||||
// needed for library WiFiManager
|
||||
#include <DNSServer.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
|
||||
|
||||
#include <WiFiClient.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <FS.h>
|
||||
|
||||
#include <WebSockets.h> //https://github.com/Links2004/arduinoWebSockets
|
||||
#include <WebSocketsServer.h>
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// Instanciate HTTP(80) / WebSockets(81) Server
|
||||
// ***************************************************************************
|
||||
ESP8266WebServer server ( 80 );
|
||||
WebSocketsServer webSocket = WebSocketsServer(81);
|
||||
|
||||
|
||||
// ***************************************************************************
|
||||
// Load libraries / Instanciate Neopixel
|
||||
// ***************************************************************************
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#ifdef __AVR__
|
||||
#include <avr/power.h>
|
||||
#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.h>
|
||||
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();
|
||||
}
|
||||
}
|
218
Arduino/McLighting/colormodes.h
Normal file
218
Arduino/McLighting/colormodes.h
Normal file
|
@ -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<twitch)
|
||||
{
|
||||
led=random(0,11);
|
||||
analogLevel=random(50,255);// set the range of the 3 pwm leds
|
||||
ledState = ledState == LOW ? HIGH: LOW; // if the LED is off turn it on and vice-versa:
|
||||
|
||||
updateLed(led, (ledState) ? 255 : 0);
|
||||
|
||||
if (dipCount > 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
BIN
Arduino/McLighting/data/edit.htm.gz
Normal file
BIN
Arduino/McLighting/data/edit.htm.gz
Normal file
Binary file not shown.
BIN
Arduino/McLighting/data/favicon.ico
Normal file
BIN
Arduino/McLighting/data/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
BIN
Arduino/McLighting/data/graphs.js.gz
Normal file
BIN
Arduino/McLighting/data/graphs.js.gz
Normal file
Binary file not shown.
97
Arduino/McLighting/data/index.htm
Normal file
97
Arduino/McLighting/data/index.htm
Normal file
|
@ -0,0 +1,97 @@
|
|||
<!--
|
||||
FSWebServer - Example Index Page
|
||||
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
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>ESP Monitor</title>
|
||||
<script type="text/javascript" src="graphs.js"></script>
|
||||
<script type="text/javascript">
|
||||
var heap,temp,digi;
|
||||
var reloadPeriod = 1000;
|
||||
var running = false;
|
||||
|
||||
function loadValues(){
|
||||
if(!running) return;
|
||||
var xh = new XMLHttpRequest();
|
||||
xh.onreadystatechange = function(){
|
||||
if (xh.readyState == 4){
|
||||
if(xh.status == 200) {
|
||||
var res = JSON.parse(xh.responseText);
|
||||
heap.add(res.heap);
|
||||
temp.add(res.analog);
|
||||
digi.add(res.gpio);
|
||||
if(running) setTimeout(loadValues, reloadPeriod);
|
||||
} else running = false;
|
||||
}
|
||||
};
|
||||
xh.open("GET", "/status", true);
|
||||
xh.send(null);
|
||||
};
|
||||
|
||||
function run(){
|
||||
if(!running){
|
||||
running = true;
|
||||
loadValues();
|
||||
}
|
||||
}
|
||||
|
||||
function onBodyLoad(){
|
||||
var refreshInput = document.getElementById("refresh-rate");
|
||||
refreshInput.value = reloadPeriod;
|
||||
refreshInput.onchange = function(e){
|
||||
var value = parseInt(e.target.value);
|
||||
reloadPeriod = (value > 0)?value:0;
|
||||
e.target.value = reloadPeriod;
|
||||
}
|
||||
var stopButton = document.getElementById("stop-button");
|
||||
stopButton.onclick = function(e){
|
||||
running = false;
|
||||
}
|
||||
var startButton = document.getElementById("start-button");
|
||||
startButton.onclick = function(e){
|
||||
run();
|
||||
}
|
||||
|
||||
// Example with 10K thermistor
|
||||
//function calcThermistor(v) {
|
||||
// var t = Math.log(((10230000 / v) - 10000));
|
||||
// t = (1/(0.001129148+(0.000234125*t)+(0.0000000876741*t*t*t)))-273.15;
|
||||
// return (t>120)?0:Math.round(t*10)/10;
|
||||
//}
|
||||
//temp = createGraph(document.getElementById("analog"), "Temperature", 100, 128, 10, 40, false, "cyan", calcThermistor);
|
||||
|
||||
temp = createGraph(document.getElementById("analog"), "Analog Input", 100, 128, 0, 1023, false, "cyan");
|
||||
heap = createGraph(document.getElementById("heap"), "Current Heap", 100, 125, 0, 30000, true, "orange");
|
||||
digi = createDigiGraph(document.getElementById("digital"), "GPIO", 100, 146, [0, 4, 5, 16], "gold");
|
||||
run();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body id="index" style="margin:0; padding:0;" onload="onBodyLoad()">
|
||||
<div id="controls" style="display: block; border: 1px solid rgb(68, 68, 68); padding: 5px; margin: 5px; width: 362px; background-color: rgb(238, 238, 238);">
|
||||
<label>Period (ms):</label>
|
||||
<input type="number" id="refresh-rate"/>
|
||||
<input type="button" id="start-button" value="Start"/>
|
||||
<input type="button" id="stop-button" value="Stop"/>
|
||||
</div>
|
||||
<div id="heap"></div>
|
||||
<div id="analog"></div>
|
||||
<div id="digital"></div>
|
||||
</body>
|
||||
</html>
|
33
Arduino/McLighting/definitions.h
Normal file
33
Arduino/McLighting/definitions.h
Normal file
|
@ -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
|
205
Arduino/McLighting/request_handlers.h
Normal file
205
Arduino/McLighting/request_handlers.h
Normal file
|
@ -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,
|
||||
"<!DOCTYPE html>\
|
||||
<html>\
|
||||
<head>\
|
||||
<title>ESP8266 Upload</title>\
|
||||
<meta charset=\"utf-8\">\
|
||||
<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\
|
||||
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\
|
||||
</head>\
|
||||
<body>\
|
||||
<form action=\"/edit\" method=\"post\" enctype=\"multipart/form-data\">\
|
||||
<input type=\"file\" name=\"data\">\
|
||||
<input type=\"text\" name=\"path\" value=\"/\">\
|
||||
<button>Upload</button>\
|
||||
</form>\
|
||||
</body>\
|
||||
</html>",
|
||||
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();
|
||||
}
|
158
Arduino/McLighting/spiffs_webserver.h
Normal file
158
Arduino/McLighting/spiffs_webserver.h
Normal file
|
@ -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);
|
||||
}
|
||||
|
65
README.md
65
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: <some 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: <some 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.*
|
||||
|
|
180
clients/web/build/index.html
Normal file
180
clients/web/build/index.html
Normal file
|
@ -0,0 +1,180 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--Import Google Icon Font-->
|
||||
<link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<!--Import materialize.css-->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css" media="screen,projection" />
|
||||
|
||||
<!--Let browser know website is optimized for mobile-->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="light-blueXXX lighten-1XXX" role="navigation" id="mc-nav">
|
||||
<div class="nav-wrapper container"><a id="logo-container" href="#" class="brand-logo">Mc Lighting</a>
|
||||
<ul class="right hide-on-med-and-down">
|
||||
<li><a href="#" class="mc-navlink" data-pane="pane1">Wheel</a></li>
|
||||
<li><a href="#" class="mc-navlink" data-pane="pane2">Modes</a></li>
|
||||
</ul>
|
||||
|
||||
<ul id="nav-mobile" class="side-nav">
|
||||
<li><a href="#" class="mc-navlink" data-pane="pane1">Wheel</a></li>
|
||||
<li><a href="#" class="mc-navlink" data-pane="pane2">Modes</a></li>
|
||||
|
||||
</ul>
|
||||
<a href="#" data-activates="nav-mobile" class="button-collapse"><i class="material-icons">menu</i></a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mc_pane" id="pane0">
|
||||
<div class="section">
|
||||
<div class="row" id="mc-wsloader">
|
||||
<div class="col">
|
||||
<div class="preloader-wrapper active">
|
||||
<div class="spinner-layer spinner-red-only">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row hide" id="mc-wserror">
|
||||
<div class="col">
|
||||
<div>Fehler beim Herstellen der WebSocket-Verbindung.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mc_pane hide" id="pane1">
|
||||
<div class="section">
|
||||
<div class="row">
|
||||
<div class="col s12 m6">
|
||||
<div style="height: 330px; width: 330px;">
|
||||
<canvas id="myCanvas" width="330" height="330" style="-webkit-user-select: none;-webkit-tap-highlight-color: rgba(0,0,0,0);-moz-user-select:none;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s12 m6">
|
||||
<div class="card-panel" id="status">
|
||||
<div id="status_pos">pick a color</div>
|
||||
<div id="status_color"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mc_pane hide" id="pane2">
|
||||
<div class="section">
|
||||
<div class="row">
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<button class="btn waves-effect waves-light btn_mode" name="action" data-mode="off">Off
|
||||
<i class="material-icons right">send</i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="all">All
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="wipe">Wipe
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="rainbow">Rainbow
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="rainbowCycle">Rainbow cycle
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="theaterchase">Theaterchase
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="theaterchaseRainbow">Theaterchase rainbow
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="tv">TV
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<form class="col s12">
|
||||
<div class="row">
|
||||
<div class="input-field col s12 l4">
|
||||
<label for="txt_red">Red</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_red" min="0" max="255" class="update_changes" /></p>
|
||||
</div>
|
||||
<div class="input-field col s12 l4">
|
||||
<label for="txt_green">Green</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_green" min="0" max="255" class="update_changes" /></p>
|
||||
</div>
|
||||
<div class="input-field col s12 l4">
|
||||
<label for="txt_blue">Blue</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_blue" min="0" max="255" class="update_changes" /></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<label for="txt_delay">Delay</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_delay" min="0" max="500" value="50" class="update_changes" /></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<label for="txt_delay">Brightness</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_brightness" min="0" max="255" value="255" /></p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="page-footer">
|
||||
<div class="footer-copyright">
|
||||
<div class="container">
|
||||
© 2016 Copyright Tobias Blum
|
||||
<a class="grey-text text-lighten-4 right" href="#!">More Links</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style type="text/css">
|
||||
.btn_grid {
|
||||
margin: 7px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--Import jQuery before materialize.js-->
|
||||
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/js/materialize.min.js"></script>
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
211
clients/web/build/js/script.js
Normal file
211
clients/web/build/js/script.js
Normal file
|
@ -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
|
28
clients/web/gulpfile.js
Normal file
28
clients/web/gulpfile.js
Normal file
|
@ -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']);
|
180
clients/web/index.html
Normal file
180
clients/web/index.html
Normal file
|
@ -0,0 +1,180 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--Import Google Icon Font-->
|
||||
<link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<!--Import materialize.css-->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/css/materialize.min.css" media="screen,projection" />
|
||||
|
||||
<!--Let browser know website is optimized for mobile-->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="light-blueXXX lighten-1XXX" role="navigation" id="mc-nav">
|
||||
<div class="nav-wrapper container"><a id="logo-container" href="#" class="brand-logo">Mc Lighting</a>
|
||||
<ul class="right hide-on-med-and-down">
|
||||
<li><a href="#" class="mc-navlink" data-pane="pane1">Wheel</a></li>
|
||||
<li><a href="#" class="mc-navlink" data-pane="pane2">Modes</a></li>
|
||||
</ul>
|
||||
|
||||
<ul id="nav-mobile" class="side-nav">
|
||||
<li><a href="#" class="mc-navlink" data-pane="pane1">Wheel</a></li>
|
||||
<li><a href="#" class="mc-navlink" data-pane="pane2">Modes</a></li>
|
||||
|
||||
</ul>
|
||||
<a href="#" data-activates="nav-mobile" class="button-collapse"><i class="material-icons">menu</i></a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container mc_pane" id="pane0">
|
||||
<div class="section">
|
||||
<div class="row" id="mc-wsloader">
|
||||
<div class="col">
|
||||
<div class="preloader-wrapper active">
|
||||
<div class="spinner-layer spinner-red-only">
|
||||
<div class="circle-clipper left">
|
||||
<div class="circle"></div>
|
||||
</div><div class="gap-patch">
|
||||
<div class="circle"></div>
|
||||
</div><div class="circle-clipper right">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row hide" id="mc-wserror">
|
||||
<div class="col">
|
||||
<div>Fehler beim Herstellen der WebSocket-Verbindung.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mc_pane hide" id="pane1">
|
||||
<div class="section">
|
||||
<div class="row">
|
||||
<div class="col s12 m6">
|
||||
<div style="height: 330px; width: 330px;">
|
||||
<canvas id="myCanvas" width="330" height="330" style="-webkit-user-select: none;-webkit-tap-highlight-color: rgba(0,0,0,0);-moz-user-select:none;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col s12 m6">
|
||||
<div class="card-panel" id="status">
|
||||
<div id="status_pos">pick a color</div>
|
||||
<div id="status_color"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mc_pane hide" id="pane2">
|
||||
<div class="section">
|
||||
<div class="row">
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<button class="btn waves-effect waves-light btn_mode" name="action" data-mode="off">Off
|
||||
<i class="material-icons right">send</i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="all">All
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="wipe">Wipe
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="rainbow">Rainbow
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="rainbowCycle">Rainbow cycle
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="theaterchase">Theaterchase
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="theaterchaseRainbow">Theaterchase rainbow
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m6 l3 btn_grid">
|
||||
<a class="btn waves-effect waves-light btn_mode" name="action" data-mode="tv">TV
|
||||
<i class="material-icons right">send</i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<form class="col s12">
|
||||
<div class="row">
|
||||
<div class="input-field col s12 l4">
|
||||
<label for="txt_red">Red</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_red" min="0" max="255" class="update_changes" /></p>
|
||||
</div>
|
||||
<div class="input-field col s12 l4">
|
||||
<label for="txt_green">Green</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_green" min="0" max="255" class="update_changes" /></p>
|
||||
</div>
|
||||
<div class="input-field col s12 l4">
|
||||
<label for="txt_blue">Blue</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_blue" min="0" max="255" class="update_changes" /></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<label for="txt_delay">Delay</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_delay" min="0" max="500" value="50" class="update_changes" /></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<label for="txt_delay">Brightness</label><br/>
|
||||
<p class="range-field"><input type="range" id="rng_brightness" min="0" max="255" value="255" /></p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="page-footer">
|
||||
<div class="footer-copyright">
|
||||
<div class="container">
|
||||
© 2016 Copyright Tobias Blum
|
||||
<a class="grey-text text-lighten-4 right" href="#!">More Links</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style type="text/css">
|
||||
.btn_grid {
|
||||
margin: 7px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--Import jQuery before materialize.js-->
|
||||
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.6/js/materialize.min.js"></script>
|
||||
<script src="js/script.js"></script>
|
||||
</body>
|
||||
</html>
|
211
clients/web/js/script.js
Normal file
211
clients/web/js/script.js
Normal file
|
@ -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
|
15
clients/web/package.json
Normal file
15
clients/web/package.json
Normal file
|
@ -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"
|
||||
}
|
||||
}
|
BIN
documentation/fritzing/McLighting-NodeMCU_Board.fzz
Normal file
BIN
documentation/fritzing/McLighting-NodeMCU_Board.fzz
Normal file
Binary file not shown.
BIN
documentation/pics/McLighting-NodeMCU_Board.png
Normal file
BIN
documentation/pics/McLighting-NodeMCU_Board.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 206 KiB |
BIN
documentation/pics/arduino_boards_manager.png
Normal file
BIN
documentation/pics/arduino_boards_manager.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
BIN
documentation/pics/arduino_preferences.png
Normal file
BIN
documentation/pics/arduino_preferences.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
Loading…
Reference in a new issue