Initial commit: SK6812 LED Fader, HTTP Server

This commit is contained in:
Thomas Kolb 2019-11-17 01:16:46 +01:00
commit 61e54a2557
9 changed files with 589 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
include/WLAN_Logins.h

10
data/index.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Lampensteuerung</title>
</head>
<body>
<h1>Lampensteuerung</h1>
<p>It works!</p>
</body>
</html>

35
include/HTTPServer.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>
#include <HTTPServer.hpp>
class Fader;
class HTTPServer
{
public:
bool start(void);
void setFader(Fader *fader) { m_fader = fader;}
static HTTPServer &instance(void)
{
static HTTPServer theInstance;
return theInstance;
}
private:
httpsserver::HTTPServer *m_server;
Fader *m_fader;
HTTPServer(void);
static void serverTask(void *arg);
// handlers
static void handleRoot(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res);
static void handleColor(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res);
static void handle404(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res);
};

View file

@ -0,0 +1,13 @@
#pragma once
#include <array>
struct NetInfo {
const char *ssid;
const char *password;
};
const std::array<NetInfo, 2> NETWORKS {
NetInfo{"SomeNetwork", "ThePassword"},
NetInfo{"SomeOtherNetwork", "TheOtherPassword"}
};

22
platformio.ini Normal file
View file

@ -0,0 +1,22 @@
;PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nodemcu-32s]
platform = espressif32
board = nodemcu-32s
framework = arduino
monitor_speed = 115200
lib_deps =
ESP32 Digital RGB LED Drivers
esp32_https_server
build_flags = -std=c++11

101
src/Fader.cpp Normal file
View file

@ -0,0 +1,101 @@
#include "Fader.h"
Fader::Fader(std::size_t nmodules, uint8_t fadestep)
: m_fadestep(fadestep),
m_curColor(nmodules),
m_targetColor(nmodules)
{}
void Fader::set_color(uint32_t module, const Fader::Color &color)
{
m_targetColor[module] = m_curColor[module] = color;
m_somethingChanged = true;
}
void Fader::add_color(uint32_t module, const Fader::Color &color)
{
m_curColor[module] += color;
m_curColor[module].normalize();
m_targetColor[module] += color;
m_targetColor[module].normalize();
m_somethingChanged = true;
}
void Fader::fade_color(uint32_t module, const Fader::Color &color)
{
m_targetColor[module] = color;
m_somethingChanged = true;
}
void Fader::set_color(const Fader::Color &color)
{
for(std::size_t module = 0; module < m_curColor.size(); module++) {
m_targetColor[module] = m_curColor[module] = color;
}
m_somethingChanged = true;
}
void Fader::add_color(const Fader::Color &color)
{
for (std::size_t module = 0; module < m_curColor.size(); module++) {
m_curColor[module] += color;
m_curColor[module].normalize();
m_targetColor[module] += color;
m_targetColor[module].normalize();
}
m_somethingChanged = true;
}
void Fader::fade_color(const Fader::Color &color)
{
for (std::size_t module = 0; module < m_curColor.size(); module++) {
m_targetColor[module] = color;
}
}
void Fader::set_fadestep(uint8_t newFadestep)
{
m_fadestep = newFadestep;
}
/* internal function */
bool Fader::update_fade(int16_t *cur, const int16_t *target)
{
int16_t diff;
if(*cur > *target) {
diff = *cur - *target;
if(diff < m_fadestep) {
*cur = *target;
} else {
*cur -= m_fadestep;
}
return true;
} else if(*cur < *target) {
diff = *target - *cur;
if(diff < m_fadestep) {
*cur = *target;
} else {
*cur += m_fadestep;
}
return true;
}
return false;
}
void Fader::update(void)
{
for(std::size_t i = 0; i < m_curColor.size(); i++) {
m_somethingChanged = update_fade(&(m_curColor[i].r), &(m_targetColor[i].r)) || m_somethingChanged;
m_somethingChanged = update_fade(&(m_curColor[i].g), &(m_targetColor[i].g)) || m_somethingChanged;
m_somethingChanged = update_fade(&(m_curColor[i].b), &(m_targetColor[i].b)) || m_somethingChanged;
m_somethingChanged = update_fade(&(m_curColor[i].w), &(m_targetColor[i].w)) || m_somethingChanged;
}
}

68
src/Fader.h Normal file
View file

@ -0,0 +1,68 @@
#pragma once
#include <vector>
class Fader
{
public:
struct Color {
int16_t r, g, b, w;
Color(int16_t ir = 0, int16_t ig = 0, int16_t ib = 0, int16_t iw = 0) : r(ir), g(ig), b(ib), w(iw) {}
void operator += (const Color &color)
{
this->r += color.r;
this->g += color.g;
this->b += color.b;
this->w += color.w;
}
void normalize(void)
{
if(r > 255) { r = 255; } else if (r < 0) { r = 0; };
if(g > 255) { g = 255; } else if (g < 0) { g = 0; };
if(b > 255) { b = 255; } else if (b < 0) { b = 0; };
if(w > 255) { w = 255; } else if (w < 0) { w = 0; };
}
};
Fader(std::size_t nmodules, uint8_t fadestep = 1);
void set_color(uint32_t module, const Color &color);
void fade_color(uint32_t module, const Color &color);
void add_color(uint32_t module, const Color &color);
void set_color(const Color &color); // for all LEDs
void fade_color(const Color &color); // for all LEDs
void add_color(const Color &color); // for all LEDs
void set_fadestep(uint8_t newFadestep);
void update(void);
/*!
* \returns Whether any color value has changed since the last call to getColorValues().
*/
bool something_changed(void) const {return m_somethingChanged; };
const std::vector<Color>& get_color_values(void) {
m_somethingChanged = false;
return m_curColor;
}
private:
/*!
* Fade the colour value in cur towards target.
*
* \param cur The colour value to update.
* \param target The target value that should be reached.
* \returns Whether the value was actually changed.
*/
bool update_fade(int16_t *cur, const int16_t *target);
uint8_t m_fadestep;
bool m_somethingChanged;
std::vector<Color> m_curColor;
std::vector<Color> m_targetColor;
};

135
src/HTTPServer.cpp Normal file
View file

@ -0,0 +1,135 @@
/* HTTP Server setup and handler functions */
#include "HTTPServer.h"
#include "Fader.h"
HTTPServer::HTTPServer(void)
: m_fader(NULL)
{
m_server = new httpsserver::HTTPServer();
}
void HTTPServer::handleRoot(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res)
{
res->setHeader("Content-Type", "text/plain");
res->println("Hello World!");
res->print("Uptime: ");
res->print(millis());
res->println(" ms");
}
static void error400(httpsserver::HTTPResponse *res, const std::string &reason)
{
res->println("Error 400: Bad Request");
res->println(reason.c_str());
res->setStatusCode(400);
}
void HTTPServer::handleColor(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res)
{
res->setHeader("Content-Type", "text/plain");
httpsserver::ResourceParameters * params = req->getParams();
if(!params->isRequestParameterSet("color")) {
error400(res, "Parameter 'color' not set.");
return;
}
std::string colorstr = params->getRequestParameter("color");
if(colorstr.length() != 8) {
error400(res, "Wrong length of color string (expected 8 hex digits).");
return;
}
uint32_t color = 0;
for(size_t i = 0; i < 8; i++) {
color <<= 4;
char c = colorstr[i];
if(c >= 'A' && c <= 'F') {
color |= c - 'A' + 10;
} else if(c >= 'a' && c <= 'f') {
color |= c - 'a' + 10;
} else if(c >= '0' && c <= '9') {
color |= c - '0';
} else {
error400(res, "Invalid character in color string (allowed: 0-9, a-f, A-F)");
return;
}
}
uint8_t r = (color & 0xFF000000) >> 24;
uint8_t g = (color & 0x00FF0000) >> 16;
uint8_t b = (color & 0x0000FF00) >> 8;
uint8_t w = (color & 0x000000FF) >> 0;
res->print("Color changed to r:");
res->print(r);
res->print(" g:");
res->print(g);
res->print(" b:");
res->print(b);
res->print(" w:");
res->println(w);
HTTPServer::instance().m_fader->fade_color({r,g,b,w});
}
void HTTPServer::handle404(httpsserver::HTTPRequest *req, httpsserver::HTTPResponse *res)
{
req->discardRequestBody();
res->setStatusCode(404);
res->setHeader("Content-Type", "text/plain");
res->println("Error 404: Not found");
}
void HTTPServer::serverTask(void *arg)
{
httpsserver::ResourceNode *nodeRoot = new httpsserver::ResourceNode("/", "GET",
HTTPServer::handleRoot);
httpsserver::ResourceNode *nodeColor = new httpsserver::ResourceNode("/color", "GET",
HTTPServer::handleColor);
httpsserver::ResourceNode *node404 = new httpsserver::ResourceNode("", "GET",
HTTPServer::handle404);
HTTPServer &server = HTTPServer::instance();
server.m_server->registerNode(nodeRoot);
server.m_server->registerNode(nodeColor);
server.m_server->setDefaultNode(node404);
Serial.println("[server] Starting HTTP Server...");
server.m_server->start();
if (server.m_server->isRunning())
{
Serial.println("[server] Server ready.");
while (true)
{
server.m_server->loop();
}
}
Serial.println("[server] Server died?! This should never happen.");
}
bool HTTPServer::start(void)
{
xTaskCreate(
HTTPServer::serverTask, /* Task function. */
"HTTP Server Task", /* name of task. */
6144, /* Stack size of task */
NULL, /* parameter of the task */
1, /* priority of the task */
NULL); /* Task handle to keep track of created task */
return true;
}

198
src/main.cpp Normal file
View file

@ -0,0 +1,198 @@
#include <array>
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include "WLAN_Logins.h"
#include "HTTPServer.h"
#include "Fader.h"
#include <esp32_digital_led_lib.h>
#include <esp32_digital_led_funcs.h>
const uint32_t FRAME_INTERVAL_US = 16666;
const uint32_t NUM_LEDS = 138;
std::array<strand_t, 1> STRANDS { // Avoid using any of the strapping pins on the ESP32, anything >=32, 16, 17... not much left.
strand_t {.rmtChannel = 0, .gpioNum = 4, .ledType = LED_SK6812W_V1, .brightLimit = 32, .numPixels = NUM_LEDS},
};
bool led_on = false;
size_t led_idx;
size_t frame;
WiFiMulti WiFiMulti;
Fader ledFader(NUM_LEDS);
bool initLEDs()
{
/****************************************************************************
If you have multiple strands connected, but not all are in use, the
GPIO power-on defaults for the unused strand data lines will typically be
high-impedance. Unless you are pulling the data lines high or low via a
resistor, this will lead to noise on those unused but connected channels
and unwanted driving of those unallocated strands.
This optional gpioSetup() code helps avoid that problem programmatically.
****************************************************************************/
digitalLeds_initDriver();
for (int i = 0; i < STRANDS.size(); i++) {
gpioSetup(STRANDS[i].gpioNum, OUTPUT, LOW);
}
strand_t *strands[STRANDS.size()];
for (int i = 0; i < STRANDS.size(); i++)
{
strands[i] = &STRANDS[i];
}
int rc = digitalLeds_addStrands(strands, STRANDS.size());
if (rc)
{
Serial.print("Init rc = ");
Serial.println(rc);
return false;
}
for (int i = 0; i < STRANDS.size(); i++)
{
strand_t *pStrand = strands[i];
Serial.print("Strand ");
Serial.print(i);
Serial.print(" = ");
Serial.print((uint32_t)(pStrand->pixels), HEX);
Serial.println();
#if DEBUG_ESP32_DIGITAL_LED_LIB
dumpDebugBuffer(-2, digitalLeds_debugBuffer);
#endif
}
led_idx = 0;
frame = 0;
return true;
}
static void ledTask( void * parameter )
{
/* loop forever */
for(;;){
uint32_t start_time = micros();
led_idx++;
if(led_idx >= STRANDS[0].numPixels) {
led_idx = 0;
}
strand_t *strands[STRANDS.size()];
for (int i = 0; i < STRANDS.size(); i++)
{
strands[i] = &STRANDS[i];
}
ledFader.update();
if(ledFader.something_changed()) {
const std::vector<Fader::Color> &colors = ledFader.get_color_values();
for (size_t i = 0; i < STRANDS[0].numPixels; i++) {
const Fader::Color &c = colors[i];
STRANDS[0].pixels[i] = pixelFromRGBW(c.r, c.g, c.b, c.w);
}
}
digitalLeds_drawPixels(strands, 1);
frame++;
uint32_t now = micros();
uint32_t duration = now - start_time;
if(frame % 50 == 0) {
uint32_t load = 100 * duration / FRAME_INTERVAL_US;
Serial.print("CPU Load: ");
Serial.print(load);
Serial.println(" %");
}
if(duration < FRAME_INTERVAL_US) {
delayMicroseconds(FRAME_INTERVAL_US - duration);
}
}
/* delete a task when finish,
this will never happen because this is infinity loop */
vTaskDelete( NULL );
}
void setup()
{
pinMode(2, OUTPUT); // On-Board LED
Serial.begin(115200);
delay(10);
if(!initLEDs()) {
Serial.println("LED setup failed!");
while(true) {
delay(100);
}
}
// We start by connecting to a WiFi network
digitalWrite(2, HIGH);
Serial.println();
Serial.println();
for (auto &net : NETWORKS)
{
Serial.print("Adding network ");
Serial.println(net.ssid);
WiFiMulti.addAP(net.ssid, net.password);
}
while (WiFiMulti.run() != WL_CONNECTED)
{
led_on = !led_on;
digitalWrite(2, led_on);
delay(250);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
HTTPServer::instance().setFader(&ledFader);
ledFader.set_color(Fader::Color{64,0,0,0});
ledFader.fade_color(Fader::Color{0,64,0,0});
xTaskCreate(
ledTask, /* Task function. */
"LED Task", /* name of task. */
10000, /* Stack size of task */
NULL, /* parameter of the task */
2, /* priority of the task */
NULL); /* Task handle to keep track of created task */
HTTPServer::instance().start();
}
void loop() {
led_on = !led_on;
digitalWrite(2, led_on);
delay(500);
//Serial.print("RSSI: ");
//Serial.println(WiFi.RSSI());
//server.loop();
}