Initial commit

This commit is contained in:
Tanner Collin 2017-09-14 22:35:56 -06:00
commit a950a0769d
3 changed files with 389 additions and 0 deletions

90
index.html.h Normal file
View File

@ -0,0 +1,90 @@
const char index_html[] = R"=====(
<!DOCTYPE html>
<html lang='en'>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
<meta name='viewport' content='width=device-width' />
<title>WS2812FX Ctrl</title>
<script type='text/javascript' src='main.js'></script>
<style>
* {
font-family:sans-serif;
margin:0;
padding:0;
}
body {
width:100%;
max-width:675px;
background-color:#202020;
}
h1 {
width:65%;
margin:25px 0 25px 25%;
color:#454545;
text-align:center;
}
#colorbar {
float:left;
}
#controls {
width:65%;
display:inline-block;
padding-left:5px;
}
ul {
text-align:center;
}
ul#mode li {
display:block;
}
ul#brightness li, ul#speed li {
display:inline-block;
width:30%;
}
ul li a {
display:block;
margin:3px;
padding:10px 5px;
border:2px solid #454545;
border-radius:5px;
color:#454545;
font-weight:bold;
text-decoration:none;
}
ul li a.active {
border:2px solid #909090;
color:#909090;
}
</style>
</head>
<body>
<h1>WS2812FX Control</h1>
<canvas id='colorbar' width='75' height='1080'></canvas>
<div id='controls'>
<ul id='mode'></ul>
<ul id='brightness'>
<li><a href='#' class='b' id='-'>&#9788;</a></li>
<li><a href='#' class='b' id='+'>&#9728;</a></li>
</ul>
<ul id='speed'>
<li><a href='#' class='s' id='-'>&#8722;</a></li>
<li><a href='#' class='s' id='+'>&#43;</a></li>
</ul>
</div>
</body>
</html>
)=====";

119
main.js.h Normal file
View File

@ -0,0 +1,119 @@
const char main_js[] = R"=====(
window.addEventListener('load', setup);
window.addEventListener('resize', drawColorbar);
function handle_M_B_S(e) {
e.preventDefault();
var name = e.target.className;
var val = e.target.id;
if(e.target.className.indexOf('m') > -1) {
elems = document.querySelectorAll('#mode li a');
[].forEach.call(elems, function(el) {
el.classList.remove('active');
name = e.target.className;
});
e.target.classList.add('active');
}
submitVal(name, val);
}
function submitVal(name, val) {
var xhttp = new XMLHttpRequest();
xhttp.open('GET', 'set?' + name + '=' + val, true);
xhttp.send();
}
function compToHex(c) {
hex = c.toString(16);
return hex.length == 1 ? '0' + hex : hex;
}
function getMousePos(can, evt) {
r = can.getBoundingClientRect();
return {
x: evt.clientX - r.left,
y: evt.clientY - r.top
};
}
function Touch(e) {
e.preventDefault();
pos = {
x: Math.round(e.targetTouches[0].pageX),
y: Math.round(e.targetTouches[0].pageY)
};
rgb = ctx.getImageData(pos.x, pos.y, 1, 1).data;
drawColorbar(rgb);
submitVal('c', compToHex(rgb[0]) + compToHex(rgb[1]) + compToHex(rgb[2]));
}
function Click(e) {
pos = getMousePos(can, e);
rgb = ctx.getImageData(pos.x, pos.y, 1, 1).data;
drawColorbar(rgb);
submitVal('c', compToHex(rgb[0]) + compToHex(rgb[1]) + compToHex(rgb[2]));
}
// Thanks to the backup at http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
function rgbToHsl(r, g, b){
r = r / 255;
g = g / 255;
b = b / 255;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min) {
h = s = 0;
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h = h / 6;
}
return [h, s, l];
}
function drawColorbar(rgb = [0, 0, 0]) {
can = document.getElementById('colorbar');
ctx = can.getContext('2d');
can.width = document.body.clientWidth * 0.25;
var h = can.height / 360;
var hsl = rgbToHsl(rgb[0], rgb[1], rgb[2]);
for(var i=0; i<=360; i++) {
ctx.fillStyle = 'hsl('+i+', 100%, 50%)';
ctx.fillRect(0, i * h, can.width/2, h);
ctx.fillStyle = 'hsl(' + hsl[0] * 360 + ', 100%, ' + i * (100/360) + '%)';
ctx.fillRect(can.width/2, i * h, can.width/2, h);
}
}
function setup(){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
document.getElementById('mode').innerHTML = xhttp.responseText;
elems = document.querySelectorAll('ul li a'); // adds listener also to existing s and b buttons
[].forEach.call(elems, function(el) {
el.addEventListener('touchstart', handle_M_B_S, false);
el.addEventListener('click', handle_M_B_S, false);
});
}
};
xhttp.open('GET', 'modes', true);
xhttp.send();
var can = document.getElementById('colorbar');
var ctx = can.getContext('2d');
drawColorbar();
can.addEventListener('touchstart', Touch, false);
can.addEventListener('click', Click, false);
}
)=====";

180
neopixel-wifi-control.ino Normal file
View File

@ -0,0 +1,180 @@
/*
WS2812FX Webinterface.
Harm Aldick - 2016
www.aldick.org
FEATURES
* Webinterface with mode, color, speed and brightness selectors
LICENSE: The MIT License (MIT)
Copyright (c) 2016 Harm Aldick
CHANGELOG
2016-11-26 initial version
Tanner Collin:
2017-09-14 Create Wifi AP to anyone can configure the LEDs.
Set up captive portal so web interface auto opens.
Clean up.
*/
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WS2812FX.h>
#include <WiFiClient.h>
#include <DNSServer.h>
#include <ESP8266mDNS.h>
#include "index.html.h"
#include "main.js.h"
const char *ssid = "ADD SSID HERE";
const char *myHostname = "ws2812";
#define HTTP_PORT 80
#define DNS_PORT 53
// QUICKFIX...See https://github.com/esp8266/Arduino/issues/263
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define LED_PIN D7 // 0 = GPIO0, 2=GPIO2
#define LED_COUNT 80
#define DEFAULT_COLOR 0xFF5900
#define DEFAULT_BRIGHTNESS 255
#define DEFAULT_SPEED 200
#define DEFAULT_MODE FX_MODE_STATIC
#define BRIGHTNESS_STEP 15 // in/decrease brightness by this amount per click
#define SPEED_STEP 10 // in/decrease brightness by this amount per click
String modes = "";
WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
ESP8266WebServer server(HTTP_PORT);
DNSServer dnsServer;
IPAddress apIP(192, 168, 4, 1);
IPAddress netMsk(255, 255, 255, 0);
void setup(){
Serial.begin(115200);
Serial.println();
Serial.println();
Serial.println("Starting...");
modes.reserve(5000);
modes_setup();
Serial.println("WS2812FX setup");
ws2812fx.init();
ws2812fx.setMode(DEFAULT_MODE);
ws2812fx.setColor(DEFAULT_COLOR);
ws2812fx.setSpeed(DEFAULT_SPEED);
ws2812fx.setBrightness(DEFAULT_BRIGHTNESS);
ws2812fx.start();
Serial.println("Wifi setup");
WiFi.softAPConfig(apIP, apIP, netMsk);
WiFi.softAP(ssid);
delay(500);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(DNS_PORT, "*", apIP);
Serial.println("HTTP server setup");
server.on("/", srv_handle_index_html);
server.on("/main.js", srv_handle_main_js);
server.on("/modes", srv_handle_modes);
server.on("/set", srv_handle_set);
server.on("/generate_204", srv_handle_index_html); //Android captive portal. Maybe not needed. Might be handled by notFound handler.
server.on("/fwlink", srv_handle_index_html); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
server.onNotFound(srv_handle_not_found);
server.begin();
Serial.println("HTTP server started.");
Serial.println("ready!");
}
void loop() {
dnsServer.processNextRequest();
server.handleClient();
ws2812fx.service();
}
/*
* Build <li> string for all modes.
*/
void modes_setup() {
modes = "";
for(uint8_t i=0; i < ws2812fx.getModeCount(); i++) {
modes += "<li><a href='#' class='m' id='";
modes += i;
modes += "'>";
modes += ws2812fx.getModeName(i);
modes += "</a></li>";
}
}
/* #####################################################
# Webserver Functions
##################################################### */
void srv_handle_not_found() {
server.send(404, "text/plain", "File Not Found");
}
void srv_handle_index_html() {
server.send_P(200,"text/html", index_html);
}
void srv_handle_main_js() {
server.send_P(200,"application/javascript", main_js);
}
void srv_handle_modes() {
server.send(200,"text/plain", modes);
}
void srv_handle_set() {
for (uint8_t i=0; i < server.args(); i++){
if(server.argName(i) == "c") {
uint32_t tmp = (uint32_t) strtol(&server.arg(i)[0], NULL, 16);
if(tmp >= 0x000000 && tmp <= 0xFFFFFF) {
ws2812fx.setColor(tmp);
}
}
if(server.argName(i) == "m") {
uint8_t tmp = (uint8_t) strtol(&server.arg(i)[0], NULL, 10);
ws2812fx.setMode(tmp % ws2812fx.getModeCount());
}
if(server.argName(i) == "b") {
if(server.arg(i)[0] == '-') {
ws2812fx.decreaseBrightness(BRIGHTNESS_STEP);
} else {
ws2812fx.increaseBrightness(BRIGHTNESS_STEP);
}
}
if(server.argName(i) == "s") {
if(server.arg(i)[0] == '-') {
ws2812fx.decreaseSpeed(SPEED_STEP);
} else {
ws2812fx.increaseSpeed(SPEED_STEP);
}
}
}
server.send(200, "text/plain", "OK");
}