Initial commit
This commit is contained in:
commit
a950a0769d
90
index.html.h
Normal file
90
index.html.h
Normal 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='-'>☼</a></li>
|
||||
<li><a href='#' class='b' id='+'>☀</a></li>
|
||||
</ul>
|
||||
|
||||
<ul id='speed'>
|
||||
<li><a href='#' class='s' id='-'>−</a></li>
|
||||
<li><a href='#' class='s' id='+'>+</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
)=====";
|
119
main.js.h
Normal file
119
main.js.h
Normal 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
180
neopixel-wifi-control.ino
Normal 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");
|
||||
}
|
Loading…
Reference in New Issue
Block a user