Added animation test framework
This framework provides the same animation interface as the ESP firmware, but sends commands to the ESP via UDP. Therefore animations can be tested without re-flashing the firmware for every tiny tuning change.
This commit is contained in:
parent
ae2eb2a25f
commit
f28de024d8
15
animation_test/CMakeLists.txt
Normal file
15
animation_test/CMakeLists.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
project(animation_test)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
set(SOURCES
|
||||
src/Fader.cpp
|
||||
src/Animation/ImageScrollerAnimation.cpp
|
||||
src/main.cpp
|
||||
|
||||
src/Animation/Animation.h
|
||||
src/Fader.h
|
||||
src/Animation/ImageScrollerAnimation.h
|
||||
)
|
||||
|
||||
add_executable(animation_test ${SOURCES})
|
||||
target_include_directories(animation_test PUBLIC src)
|
53
animation_test/src/Animation/Animation.h
Normal file
53
animation_test/src/Animation/Animation.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Fader.h"
|
||||
|
||||
class Animation
|
||||
{
|
||||
public:
|
||||
Animation(Fader *fader)
|
||||
: m_fader(fader), m_running(true)
|
||||
{}
|
||||
|
||||
/*!
|
||||
* The animation's main function. Will be called once per frame.
|
||||
*
|
||||
* \param frame The current frame since the animation's start.
|
||||
*/
|
||||
virtual void loop(uint64_t frame) = 0;
|
||||
|
||||
/*!
|
||||
* Stop the animation. After this function is called, the animation code
|
||||
* might start some transition to black (all LEDs off).
|
||||
*/
|
||||
virtual void stop(void)
|
||||
{
|
||||
m_running = false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* A function for checking wether the animation has stopped. This must
|
||||
* return true after the stop transition has finished.
|
||||
*
|
||||
* \returns True if all activity of the animation code has stopped.
|
||||
*/
|
||||
virtual bool finished(void)
|
||||
{
|
||||
return !m_running;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Reset function of the animation code. This should clear any
|
||||
* internal state.
|
||||
*/
|
||||
virtual void reset(void)
|
||||
{
|
||||
m_running = true;
|
||||
};
|
||||
|
||||
protected:
|
||||
Fader *m_fader;
|
||||
bool m_running;
|
||||
};
|
115
animation_test/src/Fader.cpp
Normal file
115
animation_test/src/Fader.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Fader.h"
|
||||
|
||||
Fader::Fader(std::size_t nstrips, std::size_t nmodules_per_strip)
|
||||
: m_strips(nstrips),
|
||||
m_modulesPerStrip(nmodules_per_strip)
|
||||
{
|
||||
m_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
}
|
||||
|
||||
void Fader::set_color(uint32_t strip, uint32_t module, const Fader::Color &color)
|
||||
{
|
||||
m_commands.emplace_back(Command{
|
||||
SET_COLOUR,
|
||||
static_cast<uint8_t>(strip),
|
||||
static_cast<uint8_t>(module),
|
||||
color
|
||||
});
|
||||
}
|
||||
|
||||
void Fader::add_color(uint32_t strip, uint32_t module, const Fader::Color &color)
|
||||
{
|
||||
m_commands.emplace_back(Command{
|
||||
ADD_COLOUR,
|
||||
static_cast<uint8_t>(strip),
|
||||
static_cast<uint8_t>(module),
|
||||
color
|
||||
});
|
||||
}
|
||||
|
||||
void Fader::fade_color(uint32_t strip, uint32_t module, const Fader::Color &color)
|
||||
{
|
||||
m_commands.emplace_back(Command{
|
||||
FADE_COLOUR,
|
||||
static_cast<uint8_t>(strip),
|
||||
static_cast<uint8_t>(module),
|
||||
color
|
||||
});
|
||||
}
|
||||
|
||||
void Fader::set_color(const Fader::Color &color)
|
||||
{
|
||||
for(std::size_t strip = 0; strip < m_strips; strip++) {
|
||||
for(std::size_t module = 0; module < m_modulesPerStrip; module++) {
|
||||
set_color(strip, module, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fader::add_color(const Fader::Color &color)
|
||||
{
|
||||
for(std::size_t strip = 0; strip < m_strips; strip++) {
|
||||
for(std::size_t module = 0; module < m_modulesPerStrip; module++) {
|
||||
add_color(strip, module, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fader::fade_color(const Fader::Color &color)
|
||||
{
|
||||
for(std::size_t strip = 0; strip < m_strips; strip++) {
|
||||
for(std::size_t module = 0; module < m_modulesPerStrip; module++) {
|
||||
fade_color(strip, module, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Fader::set_fadestep(uint8_t newFadestep)
|
||||
{
|
||||
m_commands.emplace_back(Command{
|
||||
SET_FADESTEP,
|
||||
0,
|
||||
0,
|
||||
Color{newFadestep, 0, 0, 0}
|
||||
});
|
||||
}
|
||||
|
||||
void Fader::update(void)
|
||||
{
|
||||
// TODO: convert the m_commands vector to an UDP packet and send it
|
||||
|
||||
std::vector<uint8_t> packet;
|
||||
|
||||
for(auto &command : m_commands) {
|
||||
packet.push_back(command.action);
|
||||
packet.push_back(command.strip);
|
||||
packet.push_back(command.module);
|
||||
packet.push_back(command.color.r);
|
||||
packet.push_back(command.color.g);
|
||||
packet.push_back(command.color.b);
|
||||
packet.push_back(command.color.w);
|
||||
}
|
||||
|
||||
sockaddr_in target_addr;
|
||||
|
||||
target_addr.sin_family = AF_INET;
|
||||
target_addr.sin_port = htons(2703);
|
||||
target_addr.sin_addr.s_addr = inet_addr("10.42.7.145");
|
||||
|
||||
int ret = sendto(m_fd, packet.data(), packet.size(), 0, reinterpret_cast<const sockaddr*>(&target_addr), sizeof(target_addr));
|
||||
if(ret == -1) {
|
||||
std::cerr << "sendto failed: " << strerror(errno) << std::endl;
|
||||
}
|
||||
|
||||
m_commands.clear();
|
||||
}
|
82
animation_test/src/Fader.h
Normal file
82
animation_test/src/Fader.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#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; };
|
||||
}
|
||||
|
||||
void gamma_correct(void)
|
||||
{
|
||||
r = r * r / 255;
|
||||
g = g * g / 255;
|
||||
b = b * b / 255;
|
||||
w = w * w / 255;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Fader(std::size_t nstrips, std::size_t nmodules_per_strip);
|
||||
|
||||
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(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_commands.empty(); };
|
||||
|
||||
std::size_t modules_per_strip(void) { return m_modulesPerStrip; }
|
||||
std::size_t strips(void) { return m_strips; }
|
||||
|
||||
private:
|
||||
enum Action {
|
||||
SET_COLOUR = 0,
|
||||
FADE_COLOUR = 1,
|
||||
ADD_COLOUR = 2,
|
||||
SET_FADESTEP = 3
|
||||
};
|
||||
|
||||
struct Command {
|
||||
enum Action action;
|
||||
uint8_t strip;
|
||||
uint8_t module;
|
||||
Color color;
|
||||
};
|
||||
|
||||
int m_fd;
|
||||
|
||||
std::size_t m_strips;
|
||||
std::size_t m_modulesPerStrip;
|
||||
|
||||
std::vector<Command> m_commands;
|
||||
};
|
51
animation_test/src/main.cpp
Normal file
51
animation_test/src/main.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Fader.h"
|
||||
#include "Animation/ImageScrollerAnimation.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
Fader fader(8, 16);
|
||||
|
||||
ImageScrollerAnimation::Image img(12, 3);
|
||||
img.pixel( 0 + 1, 0).r = 48;
|
||||
img.pixel( 0 + 2, 1).r = 48;
|
||||
img.pixel( 0 + 0, 2).r = 48;
|
||||
img.pixel( 0 + 1, 2).r = 48;
|
||||
img.pixel( 0 + 2, 2).r = 48;
|
||||
img.pixel( 3 + 1, 0).g = 48;
|
||||
img.pixel( 3 + 2, 1).g = 48;
|
||||
img.pixel( 3 + 0, 2).g = 48;
|
||||
img.pixel( 3 + 1, 2).g = 48;
|
||||
img.pixel( 3 + 2, 2).g = 48;
|
||||
img.pixel( 6 + 1, 0).b = 48;
|
||||
img.pixel( 6 + 2, 1).b = 48;
|
||||
img.pixel( 6 + 0, 2).b = 48;
|
||||
img.pixel( 6 + 1, 2).b = 48;
|
||||
img.pixel( 6 + 2, 2).b = 48;
|
||||
img.pixel( 9 + 1, 0).w = 48;
|
||||
img.pixel( 9 + 2, 1).w = 48;
|
||||
img.pixel( 9 + 0, 2).w = 48;
|
||||
img.pixel( 9 + 1, 2).w = 48;
|
||||
img.pixel( 9 + 2, 2).w = 48;
|
||||
|
||||
Animation *animation = new ImageScrollerAnimation(&fader, &img);
|
||||
|
||||
for(uint64_t frame = 0; !animation->finished(); frame++) {
|
||||
animation->loop(frame);
|
||||
|
||||
if(fader.something_changed()) {
|
||||
fader.update();
|
||||
}
|
||||
|
||||
if(frame == 3000) {
|
||||
animation->stop();
|
||||
}
|
||||
|
||||
usleep(100000);
|
||||
};
|
||||
|
||||
delete animation;
|
||||
}
|
Loading…
Reference in a new issue