Compare commits

...

10 Commits
master ... bnb

Author SHA1 Message Date
Thomas Kolb f76c71e1d2 Merge branch 'master' into bnb 2023-08-11 19:56:24 +02:00
Thomas Kolb e3fcd485f9 Latest ZAM changes 2023-07-27 22:20:22 +02:00
Thomas Kolb c9471331d3 Add functions to treat all LEDs as a single strip 2022-09-09 20:10:15 +02:00
Thomas Kolb 03ff31067a Added (untested) RacerAnimation
Port of codemonk’s Python script.
2022-07-06 21:11:19 +02:00
Thomas Kolb 3b7e04fa3c Merge branch 'master' into bnb 2022-07-06 20:00:47 +02:00
Thomas Kolb b932a10ca1 Added support for ESP32-EVB and Ethernet 2022-05-06 23:58:16 +02:00
Thomas Kolb 6ff81845ed Added MIT license 2022-03-10 20:26:18 +01:00
Thomas Kolb 3671ecc635 Added „Christmas Glitter“ animation 2022-03-10 20:24:19 +01:00
Thomas Kolb 52053ea3bb Tune some animations for the ex-Pfeiffer setup 2020-12-03 20:16:46 +01:00
Thomas Kolb d9ff33b784 Adjustments for the BnB setup (2 columns with 150 LEDs each) 2020-12-02 20:03:29 +01:00
15 changed files with 412 additions and 26 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019-2022 Thomas Kolb
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -11,3 +11,5 @@
#include "RgbwPsychedelicAnimation.h"
#include "SnowfallAnimation.h"
#include "StellarAnimation.h"
#include "ChristmasGlitterAnimation.h"
#include "RacerAnimation.h"

View File

@ -21,6 +21,8 @@ class AnimationController
STELLAR = 6,
RGBW_SINUS = 7,
RGBW_PSYCHEDELIC = 8,
CHRISTMAS_GLITTER = 9,
RACERS = 10,
NUM_DEFAULT_ANIMATIONS
};
@ -40,7 +42,9 @@ class AnimationController
"Fireworks",
"Twinkling Sky",
"RGBW Sinus",
"RGBW Psychedelic"
"RGBW Psychedelic",
"Christmas Glitter",
"Racers"
};
AnimationController(Fader *fader);

View File

@ -0,0 +1,36 @@
#pragma once
#include <random>
#include <list>
#include "Animation.h"
class ChristmasGlitterAnimation : public Animation
{
public:
ChristmasGlitterAnimation(Fader *fader,
const Fader::Color &background_color = Fader::Color{0x06, 0x04, 0x00, 0x04},
const Fader::Color &glitter_color = Fader::Color{0x60, 0x40, 0x00, 0x60},
int fadestep = 1, int background_spawns_per_frame = 20, int spawn_interval_frames = 4);
void loop(uint64_t frame) override;
void stop(void) override
{
m_stopping = true;
}
void reset(void) override;
private:
std::default_random_engine m_gen;
Fader::Color m_backgroundColor;
Fader::Color m_glitterColor;
int m_fadestep;
int m_spawnInterval; // in frames
int m_backgroundSpawnsPerFrame;
bool m_stopping;
};

View File

@ -20,8 +20,8 @@ class FireAnimation : public Animation
void reset(void) override;
private:
static const constexpr unsigned MAX_NEW_ENERGY = 160;
static const constexpr unsigned RM_ENERGY = 7;
static const constexpr unsigned MAX_NEW_ENERGY = 200;
static const constexpr unsigned RM_ENERGY = 5;
static const constexpr unsigned MAX_PULL_ENERGY_PCT = 100;
std::vector<uint32_t> m_energy;

View File

@ -0,0 +1,49 @@
// vim: sw=4 ts=4 sts=4 expandtab
#pragma once
#include <random>
#include <vector>
#include "Animation.h"
#include "fasttrigon.h"
class RacerAnimation : public Animation
{
public:
RacerAnimation(Fader *fader, uint32_t racer_count);
void loop(uint64_t frame) override;
void stop(void) override
{
m_stopping = true;
}
void reset(void) override;
private:
class Racer
{
public:
Racer(Fader *fader, int32_t pos, int32_t speed, const Fader::Color &color);
void move(void);
void render(void) const;
private:
Fader *m_fader;
int32_t m_speed; // in 1/256 LEDs
int32_t m_pos; // in 1/256 LEDs
Fader::Color m_color;
};
std::vector<Racer> m_racers;
std::default_random_engine m_gen;
bool m_stopping;
};

View File

@ -11,7 +11,7 @@ class StellarAnimation : public Animation
StellarAnimation(Fader *fader,
const Fader::Color &background_color = Fader::Color{0x00, 0x02, 0x0c, 0x00},
const Fader::Color &star_color = Fader::Color{0x60, 0x30, 0x00, 0x60},
int fadestep = 1, int spawn_interval_frames = 12);
int fadestep = 1, int spawn_interval_frames = 6);
void loop(uint64_t frame) override;

View File

@ -14,6 +14,8 @@ class Fader
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) {}
Color(const Color &c) : r(c.r), g(c.g), b(c.b), w(c.w) {}
void operator += (const Color &color)
{
this->r += color.r;
@ -42,12 +44,18 @@ class Fader
Fader(std::size_t nstrips, std::size_t nmodules_per_strip, uint8_t fadestep = 1, uint32_t flip_strips_mask = 0x00);
void set_color(uint32_t pos, const Color &color);
void set_color(uint32_t strip, uint32_t module, const Color &color);
void fade_color(uint32_t strip, uint32_t module, const Color &color);
void add_color(uint32_t strip, uint32_t module, const Color &color);
void set_color(const Color &color); // for all LEDs
void fade_color(uint32_t pos, const Color &color);
void fade_color(uint32_t strip, uint32_t module, const Color &color);
void fade_color(const Color &color); // for all LEDs
void add_color(uint32_t pos, const Color &color);
void add_color(uint32_t strip, uint32_t module, const Color &color);
void add_color(const Color &color); // for all LEDs
void set_fadestep(uint8_t newFadestep);
void update(void);
@ -65,6 +73,11 @@ class Fader
std::size_t modules_per_strip(void) { return m_modulesPerStrip; }
std::size_t strips(void) { return m_strips; }
uint32_t make_module_idx(uint32_t strip, uint32_t module) const;
uint32_t get_led_idx(uint32_t module_pos) const;
uint32_t get_strip_idx(uint32_t module_pos) const;
private:
/*!
* Fade the colour value in cur towards target.
@ -75,8 +88,6 @@ class Fader
*/
bool update_fade(int16_t *cur, const int16_t *target);
inline uint32_t make_module_idx(uint32_t strip, uint32_t module);
std::size_t m_strips;
std::size_t m_modulesPerStrip;
uint8_t m_fadestep;

View File

@ -8,9 +8,9 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nodemcu-32s]
[env:esp32-evb]
platform = espressif32 @ 5.2.0
board = nodemcu-32s
board = esp32-evb
framework = arduino
monitor_speed = 115200

View File

@ -82,6 +82,14 @@ void AnimationController::changeAnimation(AnimationController::DefaultAnimation
anim.reset(new RgbwPsychedelicAnimation(m_fader));
break;
case CHRISTMAS_GLITTER:
anim.reset(new ChristmasGlitterAnimation(m_fader));
break;
case RACERS:
anim.reset(new RacerAnimation(m_fader, m_fader->strips() * m_fader->modules_per_strip() / 8));
break;
default:
return; // unknown id, do nothing
}

View File

@ -0,0 +1,58 @@
#include "Animation/ChristmasGlitterAnimation.h"
#include <iostream>
ChristmasGlitterAnimation::ChristmasGlitterAnimation(Fader *fader,
const Fader::Color &background_color, const Fader::Color &glitter_color,
int fadestep, int background_spawns_per_frame, int spawn_interval_frames)
: Animation(fader),
m_backgroundColor(background_color),
m_glitterColor(glitter_color),
m_fadestep(fadestep),
m_spawnInterval(spawn_interval_frames),
m_backgroundSpawnsPerFrame(background_spawns_per_frame)
{
reset();
}
void ChristmasGlitterAnimation::loop(uint64_t frame)
{
int nled = m_fader->modules_per_strip();
int nstrip = m_fader->strips();
if(frame == 0) {
m_fader->set_fadestep(m_fadestep);
m_fader->fade_color(m_backgroundColor);
}
// create new pixels
std::uniform_int_distribution<int> stripRng(0, nstrip-1);
std::uniform_int_distribution<int> ledRng(0, nled-1);
if(!m_stopping) {
if((frame % m_spawnInterval) == 0) {
int strip = stripRng(m_gen);
int led = ledRng(m_gen);
m_fader->add_color(strip, led, m_glitterColor);
m_fader->fade_color(strip, led, Fader::Color{0x10,0x06,0,0x03});
}
for(size_t i = 0; i < m_backgroundSpawnsPerFrame; i++) {
int strip = stripRng(m_gen);
int led = ledRng(m_gen);
m_fader->add_color(strip, led, m_backgroundColor);
m_fader->fade_color(strip, led, Fader::Color{0x10,0x06,0,0x03});
}
}
m_fader->update();
m_running = !m_stopping || m_fader->something_changed();
}
void ChristmasGlitterAnimation::reset(void)
{
m_stopping = false;
m_running = true;
}

View File

@ -6,7 +6,7 @@ FireAnimation::FireAnimation(Fader *fader, bool cold)
m_energySmooth(fader->modules_per_strip() * fader->strips()),
m_stopping(false)
{
m_colorMapIndices.assign({0, 70, 120, 200, 225, 255});
m_colorMapIndices.assign({0, 100, 160, 200, 225, 255});
if(cold) {
m_colorMapColors.emplace_back(Fader::Color{ 0, 0, 0, 0});
@ -160,9 +160,12 @@ void FireAnimation::loop(uint64_t frame)
Fader::Color color;
for(std::size_t strip = 0; strip < nstrip; strip++) {
for(std::size_t led = 0; led < nled; led++) {
colormap(m_energySmooth[idx(strip, led)], &color);
m_fader->fade_color(strip, led, color);
for(std::size_t led = 0; led < nled; led+=4) {
colormap(m_energySmooth[idx(strip, led/4)], &color);
for(int i = 0; i < 4; i++) {
m_fader->fade_color(strip, led+i, color);
}
}
}
}

View File

@ -0,0 +1,116 @@
#include <algorithm>
#include "Animation/RacerAnimation.h"
/********* Racer *********/
RacerAnimation::Racer::Racer(Fader *fader, int32_t pos, int32_t speed, const Fader::Color &color)
: m_fader(fader),
m_speed(speed),
m_pos(pos),
m_color(color)
{
}
void RacerAnimation::Racer::move(void)
{
int32_t maxpos = m_fader->strips() * m_fader->modules_per_strip() * 256;
m_pos += m_speed;
if(m_pos >= maxpos) {
m_pos = maxpos - (m_pos % maxpos);
m_speed = -m_speed;
} else if(m_pos < 0) {
m_pos = -m_pos;
m_speed = -m_speed;
}
}
void RacerAnimation::Racer::render(void) const
{
int32_t idx1 = m_pos / 256;
int32_t idx2 = m_pos / 256 + 1;
int32_t scale1 = (idx2 * 256) - m_pos;
int32_t scale2 = m_pos - (idx1 * 256);
m_fader->add_color(idx1,
Fader::Color{
static_cast<int16_t>(m_color.r * scale1 / 256),
static_cast<int16_t>(m_color.g * scale1 / 256),
static_cast<int16_t>(m_color.b * scale1 / 256),
static_cast<int16_t>(m_color.w * scale1 / 256)
});
m_fader->add_color(idx2,
Fader::Color{
static_cast<int16_t>(m_color.r * scale2 / 256),
static_cast<int16_t>(m_color.g * scale2 / 256),
static_cast<int16_t>(m_color.b * scale2 / 256),
static_cast<int16_t>(m_color.w * scale2 / 256)
});
}
/********* RacerAnimation *********/
RacerAnimation::RacerAnimation(Fader *fader, uint32_t racer_count)
: Animation(fader)
{
reset();
int32_t maxpos = m_fader->strips() * m_fader->modules_per_strip() * 256;
std::uniform_int_distribution<int32_t> posRng(0, maxpos);
std::uniform_int_distribution<int32_t> speedRng(-128, 128);
std::uniform_int_distribution<int16_t> colorRng(0, 255);
std::uniform_int_distribution<int16_t> whiteRng(0, 48);
m_racers.reserve(racer_count);
for(size_t i = 0; i < racer_count; i++) {
int32_t speed = speedRng(m_gen);
if(speed == 0) {
speed++;
}
m_racers.emplace_back(Racer{
fader,
posRng(m_gen),
speed,
Fader::Color{
colorRng(m_gen),
colorRng(m_gen),
colorRng(m_gen),
whiteRng(m_gen),
}
});
}
}
void RacerAnimation::loop(uint64_t frame)
{
if(!m_stopping) {
// clear the frame buffer
m_fader->set_color(Fader::Color{0, 0, 0, 0});
// move snow flakes and render
for(auto &flake: m_racers) {
flake.move();
flake.render();
}
} else {
m_fader->set_fadestep(4);
m_fader->fade_color(Fader::Color{0, 0, 0, 0});
}
m_fader->update();
if(m_stopping && !m_fader->something_changed()) {
m_running = false;
}
}
void RacerAnimation::reset(void)
{
m_stopping = false;
m_running = true;
}

View File

@ -12,8 +12,12 @@ Fader::Fader(std::size_t nstrips, std::size_t nmodules_per_strip, uint8_t fadest
void Fader::set_color(uint32_t strip, uint32_t module, const Fader::Color &color)
{
uint32_t idx = make_module_idx(strip, module);
set_color(idx, color);
}
m_targetColor[idx] = m_curColor[idx] = color;
void Fader::set_color(uint32_t pos, const Fader::Color &color)
{
m_targetColor[pos] = m_curColor[pos] = color;
m_somethingChanged = true;
}
@ -21,11 +25,15 @@ void Fader::set_color(uint32_t strip, uint32_t module, const Fader::Color &color
void Fader::add_color(uint32_t strip, uint32_t module, const Fader::Color &color)
{
uint32_t idx = make_module_idx(strip, module);
add_color(idx, color);
}
m_curColor[idx] += color;
m_curColor[idx].normalize();
m_targetColor[idx] += color;
m_targetColor[idx].normalize();
void Fader::add_color(uint32_t pos, const Fader::Color &color)
{
m_curColor[pos] += color;
m_curColor[pos].normalize();
m_targetColor[pos] += color;
m_targetColor[pos].normalize();
m_somethingChanged = true;
}
@ -33,8 +41,12 @@ void Fader::add_color(uint32_t strip, uint32_t module, const Fader::Color &color
void Fader::fade_color(uint32_t strip, uint32_t module, const Fader::Color &color)
{
uint32_t idx = make_module_idx(strip, module);
fade_color(idx, color);
}
m_targetColor[idx] = color;
void Fader::fade_color(uint32_t pos, const Fader::Color &color)
{
m_targetColor[pos] = color;
m_somethingChanged = true;
}
@ -109,7 +121,7 @@ void Fader::update(void)
}
}
uint32_t Fader::make_module_idx(uint32_t strip, uint32_t module)
uint32_t Fader::make_module_idx(uint32_t strip, uint32_t module) const
{
strip = (strip + STRIP_OFFSET) % m_strips;
@ -119,3 +131,22 @@ uint32_t Fader::make_module_idx(uint32_t strip, uint32_t module)
}
return strip * m_modulesPerStrip + module;
}
uint32_t Fader::get_strip_idx(uint32_t module_pos) const
{
return ((module_pos / m_strips) + m_strips - STRIP_OFFSET) % m_strips;
}
uint32_t Fader::get_led_idx(uint32_t module_pos) const
{
uint32_t strip = get_strip_idx(module_pos);
uint32_t led = module_pos % m_modulesPerStrip;
bool flip = m_flipStripsMask & (1 << strip);
if(flip) {
led = m_modulesPerStrip - led - 1;
}
return led;
}

View File

@ -6,6 +6,8 @@
#include <WiFiMulti.h>
#include <SPIFFS.h>
#include <ETH.h>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
@ -24,10 +26,12 @@
#include "coreids.h"
#define HOSTNAME "zamusiclight"
const uint32_t FRAME_INTERVAL_US = 16666;
const uint32_t NUM_STRIPS = 8;
const uint32_t NUM_LEDS = 16;
const uint32_t FLIP_STRIPS_MASK = 0x000000AA;
const uint32_t NUM_STRIPS = 2;
const uint32_t NUM_LEDS = 150;
const uint32_t FLIP_STRIPS_MASK = 0x00000002;
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 * NUM_STRIPS},
@ -49,6 +53,45 @@ UpdateServer *updateServer;
AnimationController animController(&ledFader);
static bool eth_connected = false;
void WiFiEvent(WiFiEvent_t event)
{
switch (event) {
case SYSTEM_EVENT_ETH_START:
Serial.println("ETH Started");
//set eth hostname here
ETH.setHostname(HOSTNAME);
break;
case SYSTEM_EVENT_ETH_CONNECTED:
Serial.println("ETH Connected");
break;
case SYSTEM_EVENT_ETH_GOT_IP:
Serial.print("ETH MAC: ");
Serial.print(ETH.macAddress());
Serial.print(", IPv4: ");
Serial.print(ETH.localIP());
if (ETH.fullDuplex()) {
Serial.print(", FULL_DUPLEX");
}
Serial.print(", ");
Serial.print(ETH.linkSpeed());
Serial.println("Mbps");
eth_connected = true;
break;
case SYSTEM_EVENT_ETH_DISCONNECTED:
Serial.println("ETH Disconnected");
eth_connected = false;
break;
case SYSTEM_EVENT_ETH_STOP:
Serial.println("ETH Stopped");
eth_connected = false;
break;
default:
break;
}
}
bool initLEDs()
{
/****************************************************************************
@ -198,7 +241,7 @@ static void ledFSM(void)
}
// change to last used animation after some time
if((millis() - stateEnteredTime) > 60000) {
if((millis() - stateEnteredTime) > 20000) {
// FIXME: does not work with transition because of restart() call in ANIMATION state
animController.changeAnimation(loadSavedAnimation(), false);
ledState = ANIMATION;
@ -354,7 +397,7 @@ void wifi_setup(void)
{
Serial.println("Trying to connect...");
WiFi.setHostname("mlmini");
WiFi.setHostname(HOSTNAME);
for(size_t tries = 0; tries < 10; tries++)
{
@ -433,6 +476,8 @@ void setup()
NULL, /* Task handle to keep track of created task */
CORE_ID_LED);
WiFi.onEvent(WiFiEvent);
// Connect the WiFi network (or start an AP if that doesn't work)
for (auto &net : Config::instance().getWLANList())
{
@ -443,6 +488,8 @@ void setup()
wifi_setup();
ETH.begin();
// start the UDP server
udpProto.start(2703);