diff --git a/espmusicmouse/host_driver.py b/espmusicmouse/host_driver.py new file mode 100644 index 0000000..029f802 --- /dev/null +++ b/espmusicmouse/host_driver.py @@ -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(" + +#pragma pack(push, 1) + +constexpr uint32_t MAGIC_TOKEN_HOST_TO_FW = 0x1d6379e3; +constexpr uint32_t MAGIC_TOKEN_FW_TO_HOST = 0x10c65631; + +template +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 +{ + 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 +{ + static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_STATIC; +}; + +template <> +struct ClassToMessageType +{ + static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_ALEXA_SWIPE; +}; + +template <> +struct ClassToMessageType +{ + static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_CIRCULAR; +}; + +template <> +struct ClassToMessageType +{ + static constexpr auto msgType = MessageHostToFw::LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION; +}; + +#pragma pack(pop) + +//---------------------------------------------------------------------------------------------------- + +template +void sendMessageToHost(const TMessage &msg) +{ + Serial.write((uint8_t *)&MAGIC_TOKEN_FW_TO_HOST, sizeof(MAGIC_TOKEN_FW_TO_HOST)); + MessageFwToHost msgType = ClassToMessageType::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 +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(msgBuffer); + ledTask->startEffect(*cfg); + } + else if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_ALEXA_SWIPE) + { + Serial.println("Alexa swipe"); + auto cfg = reinterpret_cast(msgBuffer); + ledTask->startEffect(*cfg); + } + else if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_CIRCULAR) + { + auto cfg = reinterpret_cast(msgBuffer); + ledTask->startEffect(*cfg); + } + else if (msgType == MessageHostToFw::LED_WHEEL_EFFECT_RANDOM_TWO_COLOR_INTERPOLATION) + { + auto cfg = reinterpret_cast(msgBuffer); + ledTask->startEffect(*cfg); + } + else + Serial.println("Unknown message type"); + } + else + Serial.printf("Incoming message too large (or invalid) %d\n", msgSize); +} \ No newline at end of file diff --git a/espmusicmouse/src/main.cpp b/espmusicmouse/src/main.cpp index a358fb7..908c474 100644 --- a/espmusicmouse/src/main.cpp +++ b/espmusicmouse/src/main.cpp @@ -11,6 +11,8 @@ #include "effects/AlexaSwipe.h" #include "effects/RandomTwoColorInterpolation.h" +#include "Messages.h" + #include "TaskLed.h" 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}}); delay(1000); 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) { @@ -48,6 +50,15 @@ void tag_handler(uint8_t *sn) 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, rgb2hsv(ColorRGBW{0, 0, 255, 0}), + rgb2hsv(ColorRGBW{0, 200, 255, 0}), false, false}); + } } else { @@ -109,49 +120,5 @@ bool btn2state = true; void loop() { - /* - 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); - */ + handleIncomingMessagesFromHost(&ledTask); } \ No newline at end of file