started host <-> fw communication
This commit is contained in:
parent
da9a96fdfa
commit
e1807360f8
|
@ -0,0 +1,125 @@
|
||||||
|
import asyncio
|
||||||
|
import serial_asyncio
|
||||||
|
from enum import Enum
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import struct
|
||||||
|
|
||||||
|
MAGIC_TOKEN_HOST_TO_FW = 0x1d6379e3
|
||||||
|
MAGIC_TOKEN_FW_TO_HOST = 0x10c65631
|
||||||
|
|
||||||
|
|
||||||
|
class MessageFwToHost(Enum):
|
||||||
|
RFID_TOKEN_READ = 0
|
||||||
|
BUTTON_NORMAL_PRESS = 1
|
||||||
|
ROTARY_ENCODER = 2
|
||||||
|
|
||||||
|
|
||||||
|
class MessageHostToFw(Enum):
|
||||||
|
LED_WHEEL_EFFECT_STATIC = 0
|
||||||
|
LED_WHEEL_EFFECT_ALEXA_SWIPE = 1
|
||||||
|
LED_WHEEL_EFFECT_CIRCULAR = 2
|
||||||
|
LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 3
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ColorRGBW:
|
||||||
|
r: float
|
||||||
|
g: float
|
||||||
|
b: float
|
||||||
|
w: float
|
||||||
|
|
||||||
|
def as_bytes(self) -> bytes:
|
||||||
|
return struct.pack("BBBB", self.r * 255, self.g * 255, self.b * 255, self.w * 255)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EffectStaticConfig:
|
||||||
|
color: ColorRGBW
|
||||||
|
|
||||||
|
def as_bytes(self) -> bytes:
|
||||||
|
return self.color.as_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EffectAlexaSwipeConfig:
|
||||||
|
primaryColorWidth: float # in degrees
|
||||||
|
transitionWidth: float # in degrees
|
||||||
|
swipeSpeed: float # in degrees per second
|
||||||
|
bellCurveWidthInLeds: float
|
||||||
|
startPosition: float # in degrees
|
||||||
|
forward: bool
|
||||||
|
primaryColor: ColorRGBW
|
||||||
|
secondaryColor: ColorRGBW
|
||||||
|
|
||||||
|
def as_bytes(self) -> bytes:
|
||||||
|
return struct.pack(
|
||||||
|
"fffff?", self.primaryColorWidth, self.transitionWidth, self.swipeSpeed,
|
||||||
|
self.bellCurveWidthInLeds, self.startPosition,
|
||||||
|
self.forward) + self.primaryColor.as_bytes() + self.secondaryColor.as_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EffectCircularConfig:
|
||||||
|
speed: float # in degrees per second
|
||||||
|
width: float # in degrees
|
||||||
|
color: ColorRGBW
|
||||||
|
|
||||||
|
def as_bytes(self) -> bytes:
|
||||||
|
return struct.pack("ff", self.speed, self.width) + self.color.as_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
messageTypeMap = {
|
||||||
|
EffectStaticConfig: 0,
|
||||||
|
EffectAlexaSwipeConfig: 1,
|
||||||
|
EffectCircularConfig: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MusicMouseProtocol(asyncio.Protocol):
|
||||||
|
def connection_made(self, transport):
|
||||||
|
self.transport = transport
|
||||||
|
|
||||||
|
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, messageTypeMap[type(message)],
|
||||||
|
len(msg_content))
|
||||||
|
|
||||||
|
print(repr(header + msg_content))
|
||||||
|
self.transport.write(header + msg_content)
|
||||||
|
|
||||||
|
def data_received(self, data):
|
||||||
|
print('data received', repr(data))
|
||||||
|
|
||||||
|
def connection_lost(self, exc):
|
||||||
|
print('port closed')
|
||||||
|
self.transport.loop.stop()
|
||||||
|
|
||||||
|
def pause_writing(self):
|
||||||
|
print('pause writing')
|
||||||
|
print(self.transport.get_write_buffer_size())
|
||||||
|
|
||||||
|
def resume_writing(self):
|
||||||
|
print(self.transport.get_write_buffer_size())
|
||||||
|
print('resume writing')
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
|
@ -0,0 +1,139 @@
|
||||||
|
#include "effects/Circular.h"
|
||||||
|
#include "effects/Static.h"
|
||||||
|
#include "effects/AlexaSwipe.h"
|
||||||
|
#include "effects/RandomTwoColorInterpolation.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;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ClassToMessageType
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MessageFwToHost : uint8_t
|
||||||
|
{
|
||||||
|
RFID_TOKEN_READ = 0,
|
||||||
|
BUTTON_NORMAL_PRESS = 1,
|
||||||
|
ROTARY_ENCODER = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MsgRfidTokenRead
|
||||||
|
{
|
||||||
|
uint8_t tagId[5];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ClassToMessageType<MsgRfidTokenRead>
|
||||||
|
{
|
||||||
|
static constexpr auto msgType = MessageFwToHost::RFID_TOKEN_READ;
|
||||||
|
};
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ClassToMessageType<EffectStaticConfig>
|
||||||
|
{
|
||||||
|
static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_STATIC;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ClassToMessageType<EffectAlexaSwipeConfig>
|
||||||
|
{
|
||||||
|
static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_ALEXA_SWIPE;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ClassToMessageType<EffectCircularConfig>
|
||||||
|
{
|
||||||
|
static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_CIRCULAR;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ClassToMessageType<EffectRandomTwoColorInterpolationConfig>
|
||||||
|
{
|
||||||
|
static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <typename TMessage>
|
||||||
|
void sendMessageToHost(const TMessage &msg)
|
||||||
|
{
|
||||||
|
Serial.write((uint8_t *)&MAGIC_TOKEN_FW_TO_HOST, sizeof(MAGIC_TOKEN_FW_TO_HOST));
|
||||||
|
MessageFwToHost msgType = ClassToMessageType<TMessage>::msgType;
|
||||||
|
Serial.write((uint8_t *)&msgType, sizeof(msgType));
|
||||||
|
|
||||||
|
uint16_t msgSize = sizeof(msg);
|
||||||
|
Serial.write((uint8_t *)&msgSize, sizeof(msgSize));
|
||||||
|
Serial.write((uint8_t *)&msg, sizeof(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename LedTask>
|
||||||
|
inline void handleIncomingMessagesFromHost(LedTask *ledTask)
|
||||||
|
{
|
||||||
|
if (Serial.available() < sizeof(MAGIC_TOKEN_FW_TO_HOST) + sizeof(MessageHostToFw) + sizeof(uint16_t))
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t token;
|
||||||
|
Serial.readBytes((uint8_t *)(&token), sizeof(token));
|
||||||
|
if (token != MAGIC_TOKEN_HOST_TO_FW)
|
||||||
|
{
|
||||||
|
Serial.println("Received invalid message");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageHostToFw msgType;
|
||||||
|
Serial.readBytes((uint8_t *)(&msgType), sizeof(msgType));
|
||||||
|
|
||||||
|
uint16_t msgSize;
|
||||||
|
Serial.readBytes((uint8_t *)(&msgSize), sizeof(msgSize));
|
||||||
|
|
||||||
|
static constexpr int maxIncomingBufferSize = 1024;
|
||||||
|
static uint8_t msgBuffer[maxIncomingBufferSize];
|
||||||
|
if (msgSize < maxIncomingBufferSize)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_CIRCULAR)
|
||||||
|
{
|
||||||
|
auto cfg = reinterpret_cast<EffectCircularConfig *>(msgBuffer);
|
||||||
|
ledTask->startEffect(*cfg);
|
||||||
|
}
|
||||||
|
else if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION)
|
||||||
|
{
|
||||||
|
auto cfg = reinterpret_cast<EffectRandomTwoColorInterpolationConfig *>(msgBuffer);
|
||||||
|
ledTask->startEffect(*cfg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Serial.println("Unknown message type");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Serial.printf("Incoming message too large (or invalid) %d\n", msgSize);
|
||||||
|
}
|
|
@ -11,6 +11,8 @@
|
||||||
#include "effects/AlexaSwipe.h"
|
#include "effects/AlexaSwipe.h"
|
||||||
#include "effects/RandomTwoColorInterpolation.h"
|
#include "effects/RandomTwoColorInterpolation.h"
|
||||||
|
|
||||||
|
#include "Messages.h"
|
||||||
|
|
||||||
#include "TaskLed.h"
|
#include "TaskLed.h"
|
||||||
|
|
||||||
MFRC522 rfid; // Instance of the class
|
MFRC522 rfid; // Instance of the class
|
||||||
|
@ -40,7 +42,7 @@ void tag_handler(uint8_t *sn)
|
||||||
ledTask.startEffect(EffectAlexaSwipeConfig{20, 30, 3 * 360, 3, 180, true, ColorRGBW{0, 255, 0, 0}, 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);
|
delay(1000);
|
||||||
ledTask.startEffect(EffectRandomTwoColorInterpolationConfig{6000, true, 6, rgb2hsv(ColorRGBW{128, 0, 0, 0}),
|
ledTask.startEffect(EffectRandomTwoColorInterpolationConfig{6000, true, 6, rgb2hsv(ColorRGBW{128, 0, 0, 0}),
|
||||||
rgb2hsv(ColorRGBW{0, 0, 128, 0}), true, true});
|
rgb2hsv(ColorRGBW{0, 0, 128, 0}), false, false});
|
||||||
}
|
}
|
||||||
if (sn[4] == 0xf0)
|
if (sn[4] == 0xf0)
|
||||||
{
|
{
|
||||||
|
@ -48,6 +50,15 @@ void tag_handler(uint8_t *sn)
|
||||||
fox = false;
|
fox = false;
|
||||||
ledTask.startEffect(EffectCircularConfig{360, 180, ColorRGBW{0, 0, 255, 0}});
|
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, rgb2hsv(ColorRGBW{0, 0, 255, 0}),
|
||||||
|
rgb2hsv(ColorRGBW{0, 200, 255, 0}), false, false});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -109,49 +120,5 @@ bool btn2state = true;
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
/*
|
handleIncomingMessagesFromHost(&ledTask);
|
||||||
rotary_encoder_event_t event = {0};
|
|
||||||
if (xQueueReceive(event_queue, &event, 1000 / portTICK_PERIOD_MS) == pdTRUE)
|
|
||||||
{
|
|
||||||
Serial.printf("Event: position %d, direction %s\n", event.state.position,
|
|
||||||
event.state.direction ? (event.state.direction == ROTARY_ENCODER_DIRECTION_CLOCKWISE ? "CW" : "CCW") : "NOT_SET");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
for (int i = 48; i < ledStrip.numLeds(); ++i)
|
|
||||||
{
|
|
||||||
clear(ledStrip);
|
|
||||||
setLedRGBW(ledStrip, i, 0, 0, 255, 255);
|
|
||||||
Serial.println(i);
|
|
||||||
ledDriver.writeSync(ledStrip.rawData(), ledStrip.numLeds());
|
|
||||||
delay(3000);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
int delayVal = 100;
|
|
||||||
if (owl)
|
|
||||||
delayVal = effectOwl();
|
|
||||||
else if (fox)
|
|
||||||
delayVal = effectFox();
|
|
||||||
else
|
|
||||||
clear(ledStrip);
|
|
||||||
|
|
||||||
ledDriver.writeSync(ledStrip.rawData(), ledStrip.numLeds());
|
|
||||||
delay(delayVal);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
auto delayMs = animation(led);
|
|
||||||
led.transmit();
|
|
||||||
delay(delayMs);
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Serial.printf("btn1 %d btn2 %d rot %d\n", digitalRead(25), digitalRead(14), digitalRead(13));
|
|
||||||
delay(500);
|
|
||||||
btn2state = !btn2state;
|
|
||||||
digitalWrite(12, btn2state);
|
|
||||||
*/
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue