Explorar el Código

Add LED animation support for single gradients

Includes terminal LED simulator
rmt-backend
jrhoffa hace 3 años
padre
commit
bbbb8e4cad
Se han modificado 10 ficheros con 320 adiciones y 23 borrados
  1. +3
    -0
      main/CMakeLists.txt
  2. +1
    -1
      main/device.cpp
  3. +1
    -1
      main/device.hpp
  4. +76
    -0
      main/leds.cpp
  5. +60
    -0
      main/leds.hpp
  6. +55
    -21
      main/main.cpp
  7. +61
    -0
      main/presets.cpp
  8. +47
    -0
      main/presets.hpp
  9. +10
    -0
      main/utils.cpp
  10. +6
    -0
      main/utils.hpp

+ 3
- 0
main/CMakeLists.txt Ver fichero

@@ -6,6 +6,9 @@ idf_component_register(
"ota.cpp"
"mqtt.cpp"
"device.cpp"
"utils.cpp"
"leds.cpp"
"presets.cpp"

INCLUDE_DIRS "."
)


+ 1
- 1
main/device.cpp Ver fichero

@@ -14,7 +14,7 @@ Device::Device(std::string _id) :
power_on(true),
client(start_mqtt_client(on_mqtt_connect, on_mqtt_message, this))
{}
void Device::set_mqtt_config(cJSON*) {
xSemaphoreTake(mutex, portMAX_DELAY);



+ 1
- 1
main/device.hpp Ver fichero

@@ -10,7 +10,7 @@
class Device {
public:
Device(std::string id);
void set_mqtt_config(cJSON*);

void lock() const { xSemaphoreTake(mutex, portMAX_DELAY); }


+ 76
- 0
main/leds.cpp Ver fichero

@@ -0,0 +1,76 @@
static const char *TAG = "leds";
#include <esp_log.h>

#include "leds.hpp"
#include "utils.hpp"


static inline uint8_t mix(int l, int r, Fixed x) {
return ((l << shift) + (x * (r - l))) >> shift;
}

Color Gradient::at(Fixed x) const {
int i_left = (x * n_colors) >> shift;
int i_right = i_left < n_colors - 1 ? i_left + 1 : 0;
Fixed ratio = (x * n_colors) - (i_left << shift);
//ESP_LOGI(TAG, "at(%d) = %d between %d and %d", x, ratio, i_left, i_right);
Color cl = colors[i_left];
Color cr = colors[i_right];
Color color = {
mix(cl.r, cr.r, ratio),
mix(cl.g, cr.g, ratio),
mix(cl.b, cr.b, ratio)
};
/*ESP_LOGI(TAG, "(%d, %d, %d) %f (%d, %d, %d) -> (%d, %d, %d)",
cl.r, cl.g, cl.b,
ratio / (float)factor,
cr.r, cr.g, cr.b,
color.r, color.g, color.b
);*/
return color;
}


void Pattern::step(Color pixels[], int len, int64_t &last_us, Fixed &offset) const {
int64_t now_us = time_us();
int64_t duration_us = last_us == 0 ? 0 : now_us - last_us;
last_us = now_us;
//ESP_LOGI(TAG, "duration %d", duration_us);

int offset_delta = (duration_us << shift) / (cycle_time_ms * 1000);
if (reverse) offset -= offset_delta; else offset += offset_delta;
Fixed off = march ?
(((int)offset * cycle_length) & ~((1 << shift) - 1)) / cycle_length :
offset;
//ESP_LOGI(TAG, "cycle time %d, delta %d, offset %d, off %d", cycle_time_ms, offset_delta, offset, off);
for (int i = 0; i < len; ++i) {
pixels[i] = gradient.at(off + ((i << shift) / cycle_length));
}
}


LEDStrip::LEDStrip(int _length) :
length(_length), pattern(NULL),
pixels(new Color[_length]),
last_us(0), offset(0)
{}

void LEDStrip::step() {
if (pattern) pattern->step(pixels, length, last_us, offset);
}

static std::string termcolor(Color c) {
return "\x1b[48;2;" +
std::to_string((int)c.r) + ";" +
std::to_string((int)c.g) + ";" +
std::to_string((int)c.b) + "m";
}

void TerminalLEDs::show() const {
std::string line;
for (int i = 0; i < length; i++) {
line += termcolor(pixels[i]) + " ";
}
line += "\x1b[0m";
ESP_LOGI(TAG, "%s", line.c_str());
}

+ 60
- 0
main/leds.hpp Ver fichero

@@ -0,0 +1,60 @@
#pragma once

#include <cstdint>
#include <string>


struct Color {
uint8_t r, g, b;
};

typedef uint16_t Fixed;
constexpr int factor = 65536;
constexpr int shift = 16;


struct Gradient {
Color at(Fixed x) const;

unsigned int n_colors;
Color colors[];
};

struct Pattern {
void step(Color[], int, int64_t &last_us, Fixed &offset) const;

int cycle_length;
int cycle_time_ms;
bool reverse;
bool march;
Gradient gradient;
};

class LEDStrip {
public:
LEDStrip(int length);

void step();

virtual void show() const = 0;

void setPattern(const Pattern* _pattern) { pattern = _pattern; }
const Pattern* getPattern() const { return pattern; }

int getLength() const { return length; }

protected:
const int length;
const Pattern *pattern;
Color *pixels;

private:
int64_t last_us;
Fixed offset;
};

class TerminalLEDs : public LEDStrip {
public:
TerminalLEDs(int length) : LEDStrip(length) {}
void show() const;
};

+ 55
- 21
main/main.cpp Ver fichero

@@ -11,6 +11,10 @@ static const char *TAG = "blinky";
#include "http_serv.hpp"
#include "ota.hpp"
#include "device.hpp"
#include "utils.hpp"
#include "leds.hpp"
#include "presets.hpp"
#include "utils.hpp"


// mDNS / NetBIOS
@@ -80,13 +84,6 @@ static void log_dir(const std::string &path) {
}


int64_t now_us() {
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
return (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;
}


// Entry Point

extern "C" void app_main(void) {
@@ -112,26 +109,63 @@ extern "C" void app_main(void) {

// Dummy Filesystem
start_filesystem();
log_dir("/spiffs");
//log_dir("/spiffs");

// TODO: Something useful
Device device("blinky-jr");

/*
int period_us = 1000000;
int64_t target_us = now_us();
// TODO: Load config from SPIFFS

auto LEDs = TerminalLEDs(20);
/*** *** ***/
LEDs.setPattern(Presets::find("rainbow"));
/*** *** ***/

while (true) {
int64_t delay_us = target_us - now_us();
if (delay_us < 0) delay_us = 0;
if (device.wait(delay_us / 1000))
ESP_LOGI(TAG, "Reload!");
else {
ESP_LOGI(TAG, "Timeout!");
target_us += period_us;
ESP_LOGI(TAG, "Configuring LEDs");

device.lock();

const Pattern *pattern = Presets::find(device.get_effect());
if (pattern) {
LEDs.setPattern(pattern);
} else {
ESP_LOGW(TAG, "Could not find pattern '%s'", device.get_effect());
pattern = LEDs.getPattern();
}

// TODO: Save Config to SPIFFS

bool is_on = device.is_on();

device.unlock();

if (!pattern || !is_on) {
if (!is_on) ESP_LOGI(TAG, "Device off");
if (!pattern) ESP_LOGW(TAG, "No LED pattern set");
ESP_LOGI(TAG, "Waiting for new config");

device.wait();

} else {
ESP_LOGI(TAG, "Starting animation");

int period_us = 1000000;
int64_t target_us = time_us();
while (true) {
int64_t delay_us = target_us - time_us();
if (delay_us < 0) delay_us = 0;
if (device.wait(delay_us / 1000)) {
// If the semaphore is set, go back to top of outer loop
break;
}

LEDs.step();
LEDs.show();

target_us += period_us;
}
}
}
*/
while (device.wait()) ESP_LOGI(TAG, "Reload!");

// Spin
}

+ 61
- 0
main/presets.cpp Ver fichero

@@ -0,0 +1,61 @@
#include <map>

#include "presets.hpp"


namespace Gradients {

#define GRADIENT(name, len, ...) \
constexpr Gradient name = { \
.n_colors = len, \
.colors = __VA_ARGS__, \
}

GRADIENT(rainbow, 6, {
Colors::red,
Colors::orange,
Colors::yellow,
Colors::green,
Colors::blue,
Colors::purple,
});

GRADIENT(peacock, 4, {
Colors::teal,
Colors::black,
Colors::purple,
Colors::blue,
});

} // Gradients


namespace Patterns {

#define PATTERN(x) \
static const Pattern x = { \
.cycle_length = 20, \
.cycle_time_ms = 10000, \
.reverse = false, \
.march = false, \
.gradient = Gradients::x, \
}

PATTERN(rainbow);
PATTERN(peacock);

} // Patterns


#define PRESET(x) {#x, &Patterns::x}

static const std::map <std::string, const Pattern*> presets = {
PRESET(rainbow),
PRESET(peacock),
};

const Pattern* Presets::find(const std::string &name) {
auto e = presets.find(name);
return e == presets.end() ? NULL : e->second;
}


+ 47
- 0
main/presets.hpp Ver fichero

@@ -0,0 +1,47 @@
#pragma once
#include <string>

#include "leds.hpp"


namespace Presets {

const struct Pattern* find(const std::string&);

} // Presets


namespace Colors {

constexpr Color red = {255, 0, 0};
constexpr Color orange = {78, 25, 0};
constexpr Color yellow = {255, 160, 0};
constexpr Color green = {0, 255, 0};
constexpr Color blue = {0, 0, 255};
constexpr Color purple = {38, 10, 42};

constexpr Color dark_red = {32, 0, 0};
constexpr Color dark_orange = {20, 6, 0};
constexpr Color dark_yellow = {64, 40, 0};
constexpr Color dark_green = {0, 32, 0};
constexpr Color dark_blue = {0, 0, 32};
constexpr Color dark_purple = {8, 3, 10};

constexpr Color pale_red = {255, 64, 64};
constexpr Color pale_orange = {78, 25, 10};
constexpr Color pale_yellow = {255, 160, 10};
constexpr Color pale_green = {64, 255, 25};
constexpr Color pale_blue = {70, 70, 255};
constexpr Color pale_purple = {42, 20, 42};

constexpr Color black = {0, 0, 0};
constexpr Color white = {255, 255, 255};

constexpr Color pink = pale_red;
constexpr Color peach = pale_orange;
// TODO: chartreuse
constexpr Color brown = {6, 2, 0};
constexpr Color teal = {0, 85, 35};
constexpr Color magenta = {255, 0, 255};

} // Colors

+ 10
- 0
main/utils.cpp Ver fichero

@@ -0,0 +1,10 @@
#include <sys/time.h>

#include "utils.hpp"


int64_t time_us() {
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
return (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;
}

+ 6
- 0
main/utils.hpp Ver fichero

@@ -0,0 +1,6 @@
#pragma once

#include <cstdint>


int64_t time_us();

Cargando…
Cancelar
Guardar