diff --git a/CLC-qthing/SiliconTorch/FxCyanRGB8.cpp b/CLC-qthing/SiliconTorch/FxCyanRGB8.cpp index 640001de726ce737c3b2ec656432b814d65c61ea..760aa0014e5f26b4643b96515dbf2311eae06491 100644 --- a/CLC-qthing/SiliconTorch/FxCyanRGB8.cpp +++ b/CLC-qthing/SiliconTorch/FxCyanRGB8.cpp @@ -2,7 +2,9 @@ // C++ system level #include <string> +#include <cstring> // memset #include <cinttypes> +#include <algorithm> #include <functional> // ESP32 specific @@ -21,10 +23,90 @@ namespace SiliconTorch { namespace FxCyanRGB8 { + const std::string HEADER("fxCyanRGB8"); - FxCyanRGB8::FxCyanRGB8() { + + FxCyanRGB8::FxCyanRGB8(uint16_t leds, uint16_t startIdx) : leds(leds), startIdx(startIdx) { + + buffer0 = new Pixel[leds]; + buffer1 = new Pixel[leds]; + + std::memset(buffer0, 0x00, leds * sizeof(Pixel)); + std::memset(buffer1, 0x00, leds * sizeof(Pixel)); + + + + } + + void FxCyanRGB8::handlePacket(uint8_t* buffer, std::size_t length) { + + if (length < 3) { + ESP_LOGE(HEADER.c_str(), "Wrong |buffer|[ %d ]: Must be greater than 2!", length); + return; + } + + if ((length - 2) % 3 > 0) { + ESP_LOGE(HEADER.c_str(), "Wrong |buffer|[ %d ]: (Length - 2) must be divisible by 3!", length); + return; + } + + + Pixel* packetPx = reinterpret_cast<Pixel*>(buffer+2); + uint16_t packetPxs = (length - 2) / 3; + uint16_t packetStartIdx = (buffer[0] << 8) | buffer[1]; + + + if (packetStartIdx == startIdx) { + + ESP_LOGW("Mapping[ X -> X ]", "startIdx[ %d ] packetIdx[ %d ] mapping: localPx[ 0 ] to packetPx[ 0 ]", startIdx, packetStartIdx); + + for (uint16_t px = 0; px < std::min(leds, packetPxs); px++) + buffer1[px] = packetPx[px]; + + } else if(packetStartIdx > startIdx) { + + ESP_LOGW("Mapping[ P > W ]", "startIdx[ %d ] packetIdx[ %d ] mapping: packetPx[ 0 ] to localPx[ %d ]", startIdx, packetStartIdx, packetStartIdx-startIdx); + + } else if (packetStartIdx < startIdx) { + + // ESP_LOGW("Mapping[ P < W ]", "startIdx[ %d ] packetIdx[ %d ] mapping: localPx[ 0 ] to packetPx[ 0 ]", startIdx, packetStartIdx, ); + + } + } + + void FxCyanRGB8::registerUDPHandler() { + if (!udpHandlerRegistered) { + udpHandlerRegistered = true; + + uint8_t headerLength = HEADER.length(); + + qthing::addUDPPacketCallback(HEADER, [&, headerLength](qthing::udpPacket packet) { + this->handlePacket((uint8_t*)(packet.payload + headerLength), packet.length - headerLength); + }); + } + } + + // qthing::Animator + qthing::RGB FxCyanRGB8::render(uint16_t addr) { + qthing::RGB out; + + return out; } + // qthing::Animator + void FxCyanRGB8::step() { + if (swapRequested) swapBuffers(); + } + + void FxCyanRGB8::requestBufferSwap() { + swapRequested = true; + } + void FxCyanRGB8::swapBuffers() { + swapRequested = false; + Pixel* tmp = buffer0; + buffer0 = buffer1; + buffer1 = tmp; + } } } diff --git a/CLC-qthing/SiliconTorch/FxCyanRGB8.hpp b/CLC-qthing/SiliconTorch/FxCyanRGB8.hpp index 691604f48cd862d9bae82a4d66998a36f818297c..457aba448750b5fbd39a82a8676f5fa3a635c147 100644 --- a/CLC-qthing/SiliconTorch/FxCyanRGB8.hpp +++ b/CLC-qthing/SiliconTorch/FxCyanRGB8.hpp @@ -13,7 +13,7 @@ // #include "LambdaTask.hpp" // qthing stuff -// #include "" +#include <qthing> @@ -21,13 +21,46 @@ namespace SiliconTorch { namespace FxCyanRGB8 { + typedef struct __attribute__((__packed__)) { + uint8_t r; + uint8_t g; + uint8_t b; + } Pixel; - class FxCyanRGB8 { + extern const std::string HEADER; + + class FxCyanRGB8 : public qthing::Animation<qthing::RGB> { public: - FxCyanRGB8(); + FxCyanRGB8(uint16_t leds, uint16_t startIdx = 0); + + void getAnimator(); + + void requestBufferSwap(); + + // Register qthing UDP packet handler + // mainly used for testing purposes + void registerUDPHandler(); + + // Strip the header before calling this function! + void handlePacket(uint8_t* buffer, std::size_t length); + + // qthing::Animator + void step(); + qthing::RGB render(uint16_t addr); private: + Pixel* buffer0; + Pixel* buffer1; + + bool swapRequested = false; + bool udpHandlerRegistered = false; + + uint16_t leds; + uint16_t startIdx; + + + void swapBuffers(); // TODO: visibility…? }; diff --git a/CLC-qthing/device_main.cpp b/CLC-qthing/device_main.cpp index 3ca3499c8b02ac2e71045a99c3f87031c5d2ee7b..6c88c6acf95d323d1631974ee2a3ac653d13a0fa 100644 --- a/CLC-qthing/device_main.cpp +++ b/CLC-qthing/device_main.cpp @@ -16,12 +16,16 @@ // #include "SpiderLib/Util.hpp" #include "SiliconTorch/CyanBusCRC.hpp" #include "SiliconTorch/CyanBus.hpp" + +#include "SiliconTorch/FxCyanRGB8.hpp" + // ### END LIBS ### SiliconTorch::CyanBus::CyanBus* cyanBus = NULL; +SiliconTorch::FxCyanRGB8::FxCyanRGB8* cyanRGB = NULL; qthing::Config cfg; @@ -33,15 +37,31 @@ void device_main() { // Needed for packet parsing, animation rendering and stuff qthing::power_managment_max_power(); - cyanBus = new SiliconTorch::CyanBus::CyanBus(13, 14, 12, 15, 2000000); // Pinout of CyanStripe - cyanBus->packetCallback += [](SiliconTorch::CyanBus::PacketData& data) { + + cyanRGB = new SiliconTorch::FxCyanRGB8::FxCyanRGB8(50); + + + + + + + + + + + + + + + + cyanBus->packetCallback += [](const SiliconTorch::CyanBus::PacketData& data) { ESP_LOGW("PacketDataTestCallback", "|buffer|[ %d ] buffer[ %s ]", data.length, data.buffer); }; - cyanBus->metricsCallback += [](SiliconTorch::CyanBus::PacketMetrics& metrics) { + cyanBus->metricsCallback += [](const SiliconTorch::CyanBus::PacketMetrics& metrics) { ESP_LOGW("PacketMetricsTestCallback", "|buffer|[ %d ] crcâ»[ %s ]", metrics.payloadLEN, metrics.crcOK ? "✔" : "✘"); // TODO: better use ✅ / ⌠?? }; diff --git a/devtools/FxCyanRGB8.py b/devtools/FxCyanRGB8.py new file mode 100755 index 0000000000000000000000000000000000000000..c516a20dca6ed2cdcf861cfb9a5d478da42f5f81 --- /dev/null +++ b/devtools/FxCyanRGB8.py @@ -0,0 +1,64 @@ +#!/usr/bin/env nix-shell +#!nix-shell -i python -p python3 + + +import sys +import time +import struct +from socket import * + + +if len(sys.argv) < 2: + print(f'Usage: {sys.argv[0]} HOST') + exit(1) + + +port = 4213 +host = sys.argv[1] +print(f'Using host[ {host} ]') + + +def sendRaw(data: bytes, _socket = socket(AF_INET, SOCK_DGRAM)): + try: + _socket.sendto(data, (host, port)) + except: + pass + + +class RGB: + def __init__(self, r: int = 0, g: int = 0, b: int = 0): + self.r = r + self.g = g + self.b = b + + def toBytes(self): + return struct.pack('BBB', self.r, self.g, self.b) + + + +def sendPacket(startIdx: int = 0, pixel: list = [], header: bytes = b'fxCyanRGB8'): + data = header + struct.pack('>H', startIdx) + + for px in pixel: + data += px.toBytes() + + print(f'Sending |pixel|[ {len(pixel)} ]…') + sendRaw(data=data) + + +if __name__ == '__main__': + white = RGB(255, 255, 255) + black = RGB( 0, 0, 0) + red = RGB(255, 0, 0) + green = RGB( 0, 255, 0) + blue = RGB( 0, 0, 255) + + pixels = [ + white, + red, green, blue, + white + ] + + while True: + sendPacket(pixel=pixels) + time.sleep(1)