Firmware cleanup

This commit is contained in:
Martin Bauer 2021-11-27 21:18:54 +01:00
parent 11db5763eb
commit 4fbd7f0f1b
12 changed files with 242 additions and 142 deletions

View File

@ -1,7 +1,5 @@
import asyncio
import serial_asyncio
from enum import Enum
from dataclasses import dataclass
import struct
from led_cmds import *
@ -23,10 +21,12 @@ class MessageHostToFw(Enum):
LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 3
outgoingMsgMap = {
outgoing_msg_map = {
EffectStaticConfig: 0,
EffectAlexaSwipeConfig: 1,
EffectCircularConfig: 2,
EffectRandomTwoColorInterpolationConfig: 3,
EffectSwipeAndChange: 4,
}
@ -42,17 +42,21 @@ incomingMsgMap = {0: RfidTokenRead}
class MusicMouseProtocol(asyncio.Protocol):
def __init__(self):
super()
self._msg_callback = None
def register_message_callback(self, cb):
self._msg_callback = cb
def connection_made(self, transport):
self.transport = transport
self.in_buff = bytes()
def send_message(self, message):
msg_content = message.as_bytes()
print("Sending message content", len(msg_content))
header = struct.pack("<IBH", MAGIC_TOKEN_HOST_TO_FW, outgoingMsgMap[type(message)],
header = struct.pack("<IBH", MAGIC_TOKEN_HOST_TO_FW, outgoing_msg_map[type(message)],
len(msg_content))
print(repr(header + msg_content))
self.transport.write(header + msg_content)
def data_received(self, data):
@ -89,25 +93,5 @@ class MusicMouseProtocol(asyncio.Protocol):
def _on_msg_receive(self, msg_type, msg_payload):
parsed_msg = incomingMsgMap[msg_type](msg_payload)
print("MSG:", parsed_msg)
async def main(protocol: MusicMouseProtocol):
for i in range(10):
protocol.send_message(EffectStaticConfig(ColorRGBW(1, 0, 0, 0)))
await asyncio.sleep(2)
protocol.send_message(EffectStaticConfig(ColorRGBW(0, 1, 0, 0)))
await asyncio.sleep(2)
protocol.send_message(EffectStaticConfig(ColorRGBW(0, 1, 1, 0)))
await asyncio.sleep(2)
loop = asyncio.get_event_loop()
coro = serial_asyncio.create_serial_connection(loop,
MusicMouseProtocol,
'/dev/ttyUSB0',
baudrate=115200)
transport, protocol = loop.run_until_complete(coro)
#loop.create_task(main(protocol))
loop.run_forever()
loop.close()
if self._msg_callback is not None:
self._msg_callback(self, parsed_msg)

View File

@ -9,8 +9,13 @@ class ColorRGBW:
b: float
w: float
def repr(self):
return f"ColorRGBW({self.r}, {self.g}, {self.b}, {self.w}"
def as_bytes(self) -> bytes:
return struct.pack("<BBBB", self.r * 255, self.g * 255, self.b * 255, self.w * 255)
assert self.is_valid(), "Trying to send invalid " + repr(self)
return struct.pack("<BBBB", int(self.r * 255), int(self.g * 255), int(self.b * 255),
int(self.w * 255))
def is_valid(self):
vals = (self.r, self.g, self.b, self.w)
@ -46,14 +51,14 @@ class EffectStaticConfig:
@dataclass
class EffectAlexaSwipeConfig:
primary_color_width: float # in degrees
transition_width: float # in degrees
swipe_speed: float # in degrees per second
bell_curve_width_in_leds: float
start_position: float # in degrees
forward: bool
primary_color: ColorRGBW
secondary_color: ColorRGBW
primary_color_width: float = 20 # in degrees
transition_width: float = 30 # in degrees
swipe_speed: float = 3 * 360 # in degrees per second
bell_curve_width_in_leds: float = 3
start_position: float = 180 # in degrees
forward: bool = True
primary_color: ColorRGBW = ColorRGBW(0, 0, 1, 0)
secondary_color: ColorRGBW = ColorRGBW(0, 200 / 255, 1, 0)
def as_bytes(self) -> bytes:
return struct.pack(
@ -64,25 +69,34 @@ class EffectAlexaSwipeConfig:
@dataclass
class EffectRandomTwoColorInterpolationConfig:
cycle_durations_ms: int
start_with_existing: bool
num_segments: int
hue1_random: bool
hue2_random: bool
color1: ColorHSV
color2: ColorHSV
cycle_durations_ms: int = 6000
start_with_existing: bool = True
num_segments: int = 3
hue1_random: bool = False
hue2_random: bool = False
color1: ColorHSV = ColorHSV(240, 1, 1)
color2: ColorHSV = ColorHSV(192, 1, 1)
def as_bytes(self) -> bytes:
return struct.pack("<i?i??", self.cycle_durations_ms, self.start_with_existing,
self.num_segments, self.hue1_random,
self.hue2_random) + self.color1.as_bytes(), +self.color2.as_bytes()
self.hue2_random) + self.color1.as_bytes() + self.color2.as_bytes()
@dataclass
class EffectCircularConfig:
speed: float # in degrees per second
width: float # in degrees
color: ColorRGBW
speed: float = 360 # in degrees per second
width: float = 180 # in degrees
color: ColorRGBW = ColorRGBW(0, 0, 1, 0)
def as_bytes(self) -> bytes:
return struct.pack("<ff", self.speed, self.width) + self.color.as_bytes()
@dataclass
class EffectSwipeAndChange:
swipe: EffectAlexaSwipeConfig = EffectAlexaSwipeConfig()
change: EffectRandomTwoColorInterpolationConfig = EffectRandomTwoColorInterpolationConfig()
def as_bytes(self) -> bytes:
return self.swipe.as_bytes() + self.change.as_bytes()

View File

@ -0,0 +1,47 @@
import asyncio
import serial_asyncio
from led_cmds import (ColorRGBW, ColorHSV, EffectStaticConfig,
EffectRandomTwoColorInterpolationConfig, EffectAlexaSwipeConfig,
EffectSwipeAndChange)
from host_driver import MusicMouseProtocol, RfidTokenRead
rfid_token_map = {
bytes.fromhex("0000000000"): "None",
bytes.fromhex("88041174e9"): "Elephant",
bytes.fromhex("8804ce7230"): "Fox",
bytes.fromhex("88040d71f0"): "Owl",
}
def on_firmware_msg(protocol, message):
print("Got message", message)
if isinstance(message, RfidTokenRead) and message.id in rfid_token_map:
if rfid_token_map[message.id] == "Elephant":
print("Elephant")
eff = EffectStaticConfig(ColorRGBW(0, 0, 1, 0))
protocol.send_message(eff)
elif rfid_token_map[message.id] == "Fox":
print("Fox")
eff = EffectRandomTwoColorInterpolationConfig()
protocol.send_message(eff)
elif rfid_token_map[message.id] == "Owl":
print("Owl")
eff = EffectSwipeAndChange()
eff.swipe.primary_color = ColorRGBW(1, 1, 0, 0)
protocol.send_message(eff)
elif rfid_token_map[message.id] == "None":
eff = EffectAlexaSwipeConfig()
eff.forward = False
print("Nothing")
protocol.send_message(eff)
loop = asyncio.get_event_loop()
coro = serial_asyncio.create_serial_connection(loop,
MusicMouseProtocol,
'/dev/ttyUSB0',
baudrate=115200)
transport, protocol = loop.run_until_complete(coro)
protocol.register_message_callback(on_firmware_msg)
loop.run_forever()
loop.close()

View File

@ -6,6 +6,7 @@
#include "helpers/ColorConversions.h"
#include "helpers/BellCurve.h"
#pragma pack(push, 1)
struct EffectAlexaSwipeConfig
{
float primaryColorWidth; // in degrees
@ -17,6 +18,7 @@ struct EffectAlexaSwipeConfig
ColorRGBW primaryColor;
ColorRGBW secondaryColor;
};
#pragma pack(pop)
template <typename TLedStrip>
class EffectAlexaSwipe
@ -38,7 +40,8 @@ public:
speed_(cfg.swipeSpeed / 360 / 1000 * NUM_LEDS * DELAY_MS),
startPosition_(cfg.startPosition / 360.0f * NUM_LEDS),
primaryColor_(rgb2hsv(cfg.primaryColor)),
secondaryColor_(rgb2hsv(cfg.secondaryColor))
secondaryColor_(rgb2hsv(cfg.secondaryColor)),
finished_(false)
{
if (cfg.forward)
{
@ -52,6 +55,8 @@ public:
}
}
bool finished() { return finished_; }
int operator()()
{
clear(ledStrip_);
@ -72,8 +77,13 @@ public:
}
}
currentPosition_ += direction_ * speed_;
currentPosition_ = std::min(currentPosition_, float(NUM_LEDS) / 2.0f + bellCurveWidth_ / 2);
currentPosition_ = std::max(currentPosition_, 0.0f);
const auto maxPosition = float(NUM_LEDS) / 2.0f + bellCurveWidth_ / 2;
const auto minPosition = 0.0f;
currentPosition_ = std::min(currentPosition_, maxPosition);
currentPosition_ = std::max(currentPosition_, minPosition);
if (currentPosition_ <= minPosition || currentPosition_ >= maxPosition)
finished_ = true;
return DELAY_MS;
}
@ -119,6 +129,8 @@ private:
const ColorHSV primaryColor_;
const ColorHSV secondaryColor_;
float direction_;
bool finished_;
};
// Traits

View File

@ -4,12 +4,14 @@
#include "helpers/ColorRGBW.h"
#include "helpers/BellCurve.h"
#pragma pack(push, 1)
struct EffectCircularConfig
{
float speed; // in degrees per second
float width; // width in degrees
ColorRGBW color;
};
#pragma pack(pop)
template <typename TLedStrip>
class EffectCircular

View File

@ -5,7 +5,8 @@ enum class EffectId
STATIC,
CIRCULAR,
ALEXA_SWIPE,
RANDOM_TWO_COLOR_INTERPOLATION
RANDOM_TWO_COLOR_INTERPOLATION,
SWIPE_AND_CHANGE, // combination of ALEXA_SWIPE and RANDOM_TWO_COLOR_INTERPOLATION
};
template <EffectId id>

View File

@ -6,6 +6,7 @@
#include "helpers/ColorHSV.h"
#include "helpers/ColorConversions.h"
#pragma pack(push, 1)
struct EffectRandomTwoColorInterpolationConfig
{
int32_t cycleDurationMs;
@ -18,6 +19,7 @@ struct EffectRandomTwoColorInterpolationConfig
ColorHSV color1;
ColorHSV color2;
};
#pragma pack(pop)
template <typename TLedStrip>
class EffectRandomTwoColorInterpolation
@ -43,6 +45,13 @@ public:
randomizeColors(nextColors_);
}
void begin()
{
if (config_.startWithExisting)
for (int i = 0; i < NUM_LEDS; ++i)
currentColors_[i] = rgb2hsv(getLedRGBW(ledStrip_, i));
}
int operator()()
{
for (int i = 0; i < NUM_LEDS; ++i)

View File

@ -0,0 +1,64 @@
#pragma once
#include "effects/Common.h"
#include "effects/AlexaSwipe.h"
#include "effects/RandomTwoColorInterpolation.h"
#pragma pack(push, 1)
struct EffectSwipeAndChangeConfig
{
EffectAlexaSwipeConfig swipeCfg;
EffectRandomTwoColorInterpolationConfig changeCfg;
};
#pragma pack(pop)
template <typename TLedStrip>
class EffectSwipeAndChange
{
public:
EffectSwipeAndChange(const EffectSwipeAndChangeConfig &cfg, TLedStrip &ledStrip)
: effect1_(cfg.swipeCfg, ledStrip),
effect2_(cfg.changeCfg, ledStrip),
effectRunning_(0)
{
}
int operator()()
{
if (!effect1_.finished())
{
return effect1_();
}
else
{
if (effectRunning_ == 0)
effect2_.begin();
effectRunning_ = 1;
return effect2_();
}
}
private:
EffectAlexaSwipe<TLedStrip> effect1_;
EffectRandomTwoColorInterpolation<TLedStrip> effect2_;
int effectRunning_;
};
// Traits
template <>
struct EffectIdToConfig<EffectId::SWIPE_AND_CHANGE>
{
using type = EffectSwipeAndChangeConfig;
};
template <>
struct EffectConfigToId<EffectSwipeAndChangeConfig>
{
static constexpr auto id = EffectId::SWIPE_AND_CHANGE;
};
template <typename TLedStrip>
struct EffectIdToClass<EffectId::SWIPE_AND_CHANGE, TLedStrip>
{
using type = EffectSwipeAndChange<TLedStrip>;
};

View File

@ -39,7 +39,7 @@ inline ColorHSV rgb2hsv(const ColorRGBW &in)
out.h = 0.0f; //NAN; // its now undefined
return out;
}
if (r >= max) // > is bogus, just keeps compilor happy
if (r >= max) // > is bogus, just keeps compiler happy
out.h = (g - b) / delta; // between yellow & magenta
else if (g >= max)
out.h = 2.0f + (b - r) / delta; // between cyan & yellow

View File

@ -2,12 +2,11 @@
#include "effects/Static.h"
#include "effects/AlexaSwipe.h"
#include "effects/RandomTwoColorInterpolation.h"
#include "effects/SwipeAndChange.h"
#include "Arduino.h"
#include <cstdint>
#pragma pack(push, 1)
constexpr uint32_t MAGIC_TOKEN_HOST_TO_FW = 0x1d6379e3;
constexpr uint32_t MAGIC_TOKEN_FW_TO_HOST = 0x10c65631;
@ -41,7 +40,8 @@ enum class MessageHostToFw : uint8_t
LED_WHEEL_EFFECT_STATIC = 0,
LED_WHEEL_EFFECT_ALEXA_SWIPE = 1,
LED_WHEEL_EFFECT_CIRCULAR = 2,
LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 3
LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 3,
LED_WHEEL_EFFECT_SWIPE_AND_CHANGE = 4
};
template <>
@ -68,7 +68,11 @@ struct ClassToMessageType<EffectRandomTwoColorInterpolationConfig>
static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION;
};
#pragma pack(pop)
template <>
struct ClassToMessageType<EffectSwipeAndChangeConfig>
{
static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_SWIPE_AND_CHANGE;
};
//----------------------------------------------------------------------------------------------------
@ -111,13 +115,11 @@ inline void handleIncomingMessagesFromHost(LedTask *ledTask)
Serial.readBytes(msgBuffer, msgSize);
if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_STATIC)
{
Serial.println("Static color");
auto cfg = reinterpret_cast<EffectStaticConfig *>(msgBuffer);
ledTask->startEffect(*cfg);
}
else if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_ALEXA_SWIPE)
{
Serial.println("Alexa swipe");
auto cfg = reinterpret_cast<EffectAlexaSwipeConfig *>(msgBuffer);
ledTask->startEffect(*cfg);
}
@ -131,6 +133,11 @@ inline void handleIncomingMessagesFromHost(LedTask *ledTask)
auto cfg = reinterpret_cast<EffectRandomTwoColorInterpolationConfig *>(msgBuffer);
ledTask->startEffect(*cfg);
}
else if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_SWIPE_AND_CHANGE)
{
auto cfg = reinterpret_cast<EffectSwipeAndChangeConfig *>(msgBuffer);
ledTask->startEffect(*cfg);
}
else
Serial.println("Unknown message type");
}

View File

@ -75,10 +75,11 @@ void _led_task_func(void *params)
TLedStrip &ledStrip = *(task->ledStrip_);
// clang-format off
if (dispatchEffectId<EffectId::CIRCULAR >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed circular");}
else if (dispatchEffectId<EffectId::STATIC >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Parsed static");}
else if (dispatchEffectId<EffectId::ALEXA_SWIPE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("Alexa swipe");}
else if (dispatchEffectId<EffectId::RANDOM_TWO_COLOR_INTERPOLATION>(id, effectFunction, ledStrip, msgBuffer, effectStorage)) { Serial.println("random color ip");}
if (dispatchEffectId<EffectId::CIRCULAR >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
else if (dispatchEffectId<EffectId::STATIC >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
else if (dispatchEffectId<EffectId::ALEXA_SWIPE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
else if (dispatchEffectId<EffectId::RANDOM_TWO_COLOR_INTERPOLATION>(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
else if (dispatchEffectId<EffectId::SWIPE_AND_CHANGE >(id, effectFunction, ledStrip, msgBuffer, effectStorage)) {}
// clang-format on
timeoutMsForEffect = 0;

View File

@ -15,111 +15,70 @@
#include "TaskLed.h"
MFRC522 rfid; // Instance of the class
// -------------------------------------------------- RFID Reader ----------------------------------------
MFRC522 rfid;
MFRC522::MIFARE_Key key;
//LedStrip led(46, 23);
LedStripRGBW<51> ledStrip;
Esp32DriverRGBW ledDriver;
LedTask<decltype(ledStrip)> ledTask;
bool fox;
void tag_handler(uint8_t *sn)
void tagHandler(uint8_t *sn)
{
// serial number is always 5 bytes long
if (sn != nullptr)
{
Serial.printf("Tag: %#x %#x %#x %#x %#x\n",
sn[0], sn[1], sn[2], sn[3], sn[4]);
sendMessageToHost(MsgRfidTokenRead{{sn[0], sn[1], sn[2], sn[3], sn[4]}});
if (sn[4] == 0x30)
{
Serial.println("Fuchs");
fox = true;
//ledTask.startEffect(EffectCircularConfig{2 * 360, 180, ColorRGBW{0, 0, 255, 0}});
ledTask.startEffect(EffectAlexaSwipeConfig{20, 30, 3 * 360, 3, 180, true, ColorRGBW{0, 255, 0, 0}, ColorRGBW{0, 0, 255, 0}});
delay(1000);
ledTask.startEffect(EffectRandomTwoColorInterpolationConfig{6000, true, 6, false, false, rgb2hsv(ColorRGBW{128, 0, 0, 0}),
rgb2hsv(ColorRGBW{0, 0, 128, 0})});
}
if (sn[4] == 0xf0)
{
Serial.println("Eule");
fox = false;
ledTask.startEffect(EffectCircularConfig{360, 180, ColorRGBW{0, 0, 255, 0}});
}
if (sn[4] == 0xe9)
{
Serial.println("Elephant");
fox = true;
ledTask.startEffect(EffectAlexaSwipeConfig{20, 30, 3 * 360, 3, 180, true, ColorRGBW{0, 0, 255, 0}, ColorRGBW{0, 200, 255, 0}});
delay(1000);
ledTask.startEffect(EffectRandomTwoColorInterpolationConfig{6000, true, 3, false, false, rgb2hsv(ColorRGBW{0, 0, 255, 0}),
rgb2hsv(ColorRGBW{0, 200, 255, 0})});
}
}
else
{
Serial.println("Nichts");
if (fox)
{
fox = false;
ledTask.startEffect(EffectAlexaSwipeConfig{20, 30, 3 * 360, 3, 180, false, ColorRGBW{0, 255, 0, 0}, ColorRGBW{0, 0, 255, 0}});
sendMessageToHost(MsgRfidTokenRead{{0, 0, 0, 0, 0}});
}
else
ledTask.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}});
}
//led.transmit();
}
QueueHandle_t event_queue;
rotary_encoder_info_t info;
void setup()
void setupRfidReader()
{
Serial.begin(115200);
//led.begin();
ledDriver.begin(23, 0);
const rc522_start_args_t start_args = {
21, // MISO
5, // MOSI
18, // SCK
19, // SDA
VSPI_HOST,
&tag_handler,
&tagHandler,
125, // scan_interval_ms
8 * 1024, // stacksize
4 // task priority
};
rc522_start(start_args);
}
// -------------------------------------------------- Rotary Enc ----------------------------------------
QueueHandle_t eventQueueRotaryEncoder;
rotary_encoder_info_t info;
void setupRotaryEncoder()
{
ESP_ERROR_CHECK(gpio_install_isr_service(0));
ESP_ERROR_CHECK(rotary_encoder_init(&info, GPIO_NUM_26, GPIO_NUM_27));
ESP_ERROR_CHECK(rotary_encoder_enable_half_steps(&info, false));
event_queue = rotary_encoder_create_queue();
ESP_ERROR_CHECK(rotary_encoder_set_queue(&info, event_queue));
//button leds
//pinMode(33, OUTPUT);
//digitalWrite(33, HIGH);
//pinMode(12, OUTPUT);
//digitalWrite(12, HIGH);
//// button in
//pinMode(25, INPUT_PULLUP);
//pinMode(14, INPUT_PULLUP);
//pinMode(13, INPUT_PULLUP);
ledTask.begin(ledStrip, ledDriver);
ledTask.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}});
eventQueueRotaryEncoder = rotary_encoder_create_queue();
ESP_ERROR_CHECK(rotary_encoder_set_queue(&info, eventQueueRotaryEncoder));
}
bool btn2state = true;
// -------------------------------------------------- Led circle ----------------------------------------
LedStripRGBW<51> ledStripCircle;
Esp32DriverRGBW ledDriverCircle;
LedTask<decltype(ledStripCircle)> ledTaskCircle;
void setupLedCircle()
{
ledDriverCircle.begin(23, 0);
ledTaskCircle.begin(ledStripCircle, ledDriverCircle);
ledTaskCircle.startEffect(EffectStaticConfig{ColorRGBW{0, 0, 0, 0}});
}
//-------------------------------------------------------------------------------------------------------
void setup()
{
Serial.begin(115200);
setupRfidReader();
setupRotaryEncoder();
setupLedCircle();
}
void loop()
{
handleIncomingMessagesFromHost(&ledTask);
handleIncomingMessagesFromHost(&ledTaskCircle);
}