Initial commit: SK6812 LED Fader, HTTP Server
This commit is contained in:
commit
61e54a2557
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal 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
10
data/index.html
Normal 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
35
include/HTTPServer.h
Normal 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);
|
||||
};
|
13
include/WLAN_Logins.h.example
Normal file
13
include/WLAN_Logins.h.example
Normal 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
22
platformio.ini
Normal 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
101
src/Fader.cpp
Normal 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
68
src/Fader.h
Normal 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
135
src/HTTPServer.cpp
Normal 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
198
src/main.cpp
Normal 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();
|
||||
}
|
Loading…
Reference in a new issue