2way host-firmware comm
This commit is contained in:
parent
e1807360f8
commit
11db5763eb
|
@ -4,6 +4,8 @@ from enum import Enum
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
|
from led_cmds import *
|
||||||
|
|
||||||
MAGIC_TOKEN_HOST_TO_FW = 0x1d6379e3
|
MAGIC_TOKEN_HOST_TO_FW = 0x1d6379e3
|
||||||
MAGIC_TOKEN_FW_TO_HOST = 0x10c65631
|
MAGIC_TOKEN_FW_TO_HOST = 0x10c65631
|
||||||
|
|
||||||
|
@ -21,75 +23,41 @@ class MessageHostToFw(Enum):
|
||||||
LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 3
|
LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION = 3
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
outgoingMsgMap = {
|
||||||
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,
|
EffectStaticConfig: 0,
|
||||||
EffectAlexaSwipeConfig: 1,
|
EffectAlexaSwipeConfig: 1,
|
||||||
EffectCircularConfig: 2,
|
EffectCircularConfig: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RfidTokenRead:
|
||||||
|
def __init__(self, id: bytes):
|
||||||
|
self.id = id
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "RFID Token (" + " ".join(f"{v:02x}" for v in self.id) + ")"
|
||||||
|
|
||||||
|
|
||||||
|
incomingMsgMap = {0: RfidTokenRead}
|
||||||
|
|
||||||
|
|
||||||
class MusicMouseProtocol(asyncio.Protocol):
|
class MusicMouseProtocol(asyncio.Protocol):
|
||||||
def connection_made(self, transport):
|
def connection_made(self, transport):
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
|
self.in_buff = bytes()
|
||||||
|
|
||||||
def send_message(self, message):
|
def send_message(self, message):
|
||||||
msg_content = message.as_bytes()
|
msg_content = message.as_bytes()
|
||||||
print("Sending message content", len(msg_content))
|
print("Sending message content", len(msg_content))
|
||||||
header = struct.pack("<IBH", MAGIC_TOKEN_HOST_TO_FW, messageTypeMap[type(message)],
|
header = struct.pack("<IBH", MAGIC_TOKEN_HOST_TO_FW, outgoingMsgMap[type(message)],
|
||||||
len(msg_content))
|
len(msg_content))
|
||||||
|
|
||||||
print(repr(header + msg_content))
|
print(repr(header + msg_content))
|
||||||
self.transport.write(header + msg_content)
|
self.transport.write(header + msg_content)
|
||||||
|
|
||||||
def data_received(self, data):
|
def data_received(self, data):
|
||||||
print('data received', repr(data))
|
self.in_buff += data
|
||||||
|
self._parse_message()
|
||||||
|
|
||||||
def connection_lost(self, exc):
|
def connection_lost(self, exc):
|
||||||
print('port closed')
|
print('port closed')
|
||||||
|
@ -103,6 +71,26 @@ class MusicMouseProtocol(asyncio.Protocol):
|
||||||
print(self.transport.get_write_buffer_size())
|
print(self.transport.get_write_buffer_size())
|
||||||
print('resume writing')
|
print('resume writing')
|
||||||
|
|
||||||
|
def _parse_message(self):
|
||||||
|
HEADER_SIZE = 4 + 1 + 2
|
||||||
|
if len(self.in_buff) == 0:
|
||||||
|
return
|
||||||
|
if len(self.in_buff) >= HEADER_SIZE:
|
||||||
|
token, msg_type, msg_size = struct.unpack("<IBH", self.in_buff[:HEADER_SIZE])
|
||||||
|
if token == MAGIC_TOKEN_FW_TO_HOST and len(self.in_buff) >= HEADER_SIZE + msg_size:
|
||||||
|
self._on_msg_receive(msg_type, self.in_buff[HEADER_SIZE:HEADER_SIZE + msg_size])
|
||||||
|
self.in_buff = self.in_buff[HEADER_SIZE + msg_size:]
|
||||||
|
else:
|
||||||
|
idx = self.in_buff.find("\n".encode())
|
||||||
|
if idx >= 0:
|
||||||
|
text_msg = self.in_buff[:idx]
|
||||||
|
print("LOG:", text_msg.decode())
|
||||||
|
self.in_buff = self.in_buff[idx + 1:]
|
||||||
|
|
||||||
|
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):
|
async def main(protocol: MusicMouseProtocol):
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
|
@ -120,6 +108,6 @@ coro = serial_asyncio.create_serial_connection(loop,
|
||||||
'/dev/ttyUSB0',
|
'/dev/ttyUSB0',
|
||||||
baudrate=115200)
|
baudrate=115200)
|
||||||
transport, protocol = loop.run_until_complete(coro)
|
transport, protocol = loop.run_until_complete(coro)
|
||||||
loop.create_task(main(protocol))
|
#loop.create_task(main(protocol))
|
||||||
loop.run_forever()
|
loop.run_forever()
|
||||||
loop.close()
|
loop.close()
|
|
@ -0,0 +1,88 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import struct
|
||||||
|
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
vals = (self.r, self.g, self.b, self.w)
|
||||||
|
return all(0 <= v <= 1 for v in vals)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ColorHSV:
|
||||||
|
h: float
|
||||||
|
s: float
|
||||||
|
v: float
|
||||||
|
|
||||||
|
def as_bytes(self) -> bytes:
|
||||||
|
return struct.pack("<fff", self.h, self.s, self.v)
|
||||||
|
|
||||||
|
def is_valid(self):
|
||||||
|
if not 0 <= self.h <= 360:
|
||||||
|
return False
|
||||||
|
if not 0 <= self.s <= 1:
|
||||||
|
return False
|
||||||
|
if not 0 <= self.v <= 2:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EffectStaticConfig:
|
||||||
|
color: ColorRGBW
|
||||||
|
|
||||||
|
def as_bytes(self) -> bytes:
|
||||||
|
return self.color.as_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
def as_bytes(self) -> bytes:
|
||||||
|
return struct.pack(
|
||||||
|
"<fffff?", self.primary_color_width, self.transition_width, self.swipe_speed,
|
||||||
|
self.bell_curve_width_in_leds, self.start_position,
|
||||||
|
self.forward) + self.primary_color.as_bytes() + self.secondary_color.as_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class EffectRandomTwoColorInterpolationConfig:
|
||||||
|
cycle_durations_ms: int
|
||||||
|
start_with_existing: bool
|
||||||
|
num_segments: int
|
||||||
|
hue1_random: bool
|
||||||
|
hue2_random: bool
|
||||||
|
color1: ColorHSV
|
||||||
|
color2: ColorHSV
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
@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()
|
|
@ -8,15 +8,15 @@
|
||||||
|
|
||||||
struct EffectRandomTwoColorInterpolationConfig
|
struct EffectRandomTwoColorInterpolationConfig
|
||||||
{
|
{
|
||||||
int cycleDurationMs;
|
int32_t cycleDurationMs;
|
||||||
bool startWithExisting;
|
bool startWithExisting;
|
||||||
int numSegments;
|
int32_t numSegments;
|
||||||
|
|
||||||
ColorHSV color1;
|
|
||||||
ColorHSV color2;
|
|
||||||
|
|
||||||
bool hue1Random;
|
bool hue1Random;
|
||||||
bool hue2Random;
|
bool hue2Random;
|
||||||
|
|
||||||
|
ColorHSV color1;
|
||||||
|
ColorHSV color2;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename TLedStrip>
|
template <typename TLedStrip>
|
||||||
|
|
|
@ -34,6 +34,7 @@ void tag_handler(uint8_t *sn)
|
||||||
|
|
||||||
Serial.printf("Tag: %#x %#x %#x %#x %#x\n",
|
Serial.printf("Tag: %#x %#x %#x %#x %#x\n",
|
||||||
sn[0], sn[1], sn[2], sn[3], sn[4]);
|
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)
|
if (sn[4] == 0x30)
|
||||||
{
|
{
|
||||||
Serial.println("Fuchs");
|
Serial.println("Fuchs");
|
||||||
|
@ -41,8 +42,8 @@ void tag_handler(uint8_t *sn)
|
||||||
//ledTask.startEffect(EffectCircularConfig{2 * 360, 180, ColorRGBW{0, 0, 255, 0}});
|
//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}});
|
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, false, false, rgb2hsv(ColorRGBW{128, 0, 0, 0}),
|
||||||
rgb2hsv(ColorRGBW{0, 0, 128, 0}), false, false});
|
rgb2hsv(ColorRGBW{0, 0, 128, 0})});
|
||||||
}
|
}
|
||||||
if (sn[4] == 0xf0)
|
if (sn[4] == 0xf0)
|
||||||
{
|
{
|
||||||
|
@ -56,8 +57,8 @@ void tag_handler(uint8_t *sn)
|
||||||
fox = true;
|
fox = true;
|
||||||
ledTask.startEffect(EffectAlexaSwipeConfig{20, 30, 3 * 360, 3, 180, true, ColorRGBW{0, 0, 255, 0}, ColorRGBW{0, 200, 255, 0}});
|
ledTask.startEffect(EffectAlexaSwipeConfig{20, 30, 3 * 360, 3, 180, true, ColorRGBW{0, 0, 255, 0}, ColorRGBW{0, 200, 255, 0}});
|
||||||
delay(1000);
|
delay(1000);
|
||||||
ledTask.startEffect(EffectRandomTwoColorInterpolationConfig{6000, true, 3, rgb2hsv(ColorRGBW{0, 0, 255, 0}),
|
ledTask.startEffect(EffectRandomTwoColorInterpolationConfig{6000, true, 3, false, false, rgb2hsv(ColorRGBW{0, 0, 255, 0}),
|
||||||
rgb2hsv(ColorRGBW{0, 200, 255, 0}), false, false});
|
rgb2hsv(ColorRGBW{0, 200, 255, 0})});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue