From bb9c7a511940787b56b5bc5af95356e0d2714fcc Mon Sep 17 00:00:00 2001 From: Jochen Vothknecht <jochen3120@gmail.com> Date: Sun, 24 Apr 2022 09:36:37 +0200 Subject: [PATCH] Devtools for FxCyanRGB8 and a bit of C++ impl --- CLC-qthing/SiliconTorch/FxCyanRGB8.cpp | 84 +++++++++++++++++++++++++- CLC-qthing/SiliconTorch/FxCyanRGB8.hpp | 39 +++++++++++- CLC-qthing/device_main.cpp | 26 +++++++- devtools/FxCyanRGB8.py | 64 ++++++++++++++++++++ 4 files changed, 206 insertions(+), 7 deletions(-) create mode 100755 devtools/FxCyanRGB8.py diff --git a/CLC-qthing/SiliconTorch/FxCyanRGB8.cpp b/CLC-qthing/SiliconTorch/FxCyanRGB8.cpp index 640001d..760aa00 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 691604f..457aba4 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 3ca3499..6c88c6a 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 0000000..c516a20 --- /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) -- GitLab