From d4ccc55310694dc0abe8ed6af3f5c4ef738542c5 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Mon, 15 Dec 2025 21:22:49 +0100 Subject: [PATCH 01/42] added experimental SPI support --- CMakeLists.txt | 2 + src/ZeDMD.cpp | 158 +++++++++++++++++++++++++++++++++++--- src/ZeDMD.h | 3 + src/ZeDMDSpi.cpp | 192 +++++++++++++++++++++++++++++++++++++++++++++++ src/ZeDMDSpi.h | 42 +++++++++++ 5 files changed, 385 insertions(+), 12 deletions(-) create mode 100644 src/ZeDMDSpi.cpp create mode 100644 src/ZeDMDSpi.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e957cf..604ce19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,8 @@ set(CMAKE_C_VISIBILITY_PRESET hidden) set(ZEDMD_SOURCES src/ZeDMDComm.h src/ZeDMDComm.cpp + src/ZeDMDSpi.h + src/ZeDMDSpi.cpp src/ZeDMDWiFi.h src/ZeDMDWiFi.cpp src/ZeDMD.h diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 318a0b1..9ed6d3d 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -64,6 +64,12 @@ void ZeDMD::Close() m_pZeDMDWiFi->Flush(false); m_pZeDMDWiFi->Disconnect(); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); + m_pZeDMDSpi->Flush(false); + m_pZeDMDSpi->Disconnect(); + } } void ZeDMD::Reset() @@ -153,7 +159,11 @@ const char* ZeDMD::GetFirmwareVersion() { return m_pZeDMDComm->GetFirmwareVersion(); } - return m_pZeDMDWiFi->GetFirmwareVersion(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetFirmwareVersion(); + } + return "SPI"; } uint16_t const ZeDMD::GetId() @@ -162,7 +172,11 @@ uint16_t const ZeDMD::GetId() { return m_pZeDMDComm->GetId(); } - return m_pZeDMDWiFi->GetId(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetId(); + } + return 0; } const char* ZeDMD::GetIdString() @@ -231,7 +245,11 @@ uint8_t ZeDMD::GetRGBOrder() { return m_pZeDMDComm->GetRGBOrder(); } - return m_pZeDMDWiFi->GetRGBOrder(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetRGBOrder(); + } + return 0; } uint8_t ZeDMD::GetBrightness() @@ -240,7 +258,11 @@ uint8_t ZeDMD::GetBrightness() { return m_pZeDMDComm->GetBrightness(); } - return m_pZeDMDWiFi->GetBrightness(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetBrightness(); + } + return 0; } uint8_t ZeDMD::GetPanelClockPhase() @@ -249,7 +271,11 @@ uint8_t ZeDMD::GetPanelClockPhase() { return m_pZeDMDComm->GetPanelClockPhase(); } - return m_pZeDMDWiFi->GetPanelClockPhase(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetPanelClockPhase(); + } + return 0; } uint8_t ZeDMD::GetPanelI2sSpeed() @@ -267,7 +293,11 @@ uint8_t ZeDMD::GetPanelLatchBlanking() { return m_pZeDMDComm->GetPanelLatchBlanking(); } - return m_pZeDMDWiFi->GetPanelLatchBlanking(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetPanelLatchBlanking(); + } + return 0; } uint8_t ZeDMD::GetPanelMinRefreshRate() @@ -276,7 +306,11 @@ uint8_t ZeDMD::GetPanelMinRefreshRate() { return m_pZeDMDComm->GetPanelMinRefreshRate(); } - return m_pZeDMDWiFi->GetPanelMinRefreshRate(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetPanelMinRefreshRate(); + } + retrun 0; } uint8_t ZeDMD::GetPanelDriver() @@ -294,7 +328,11 @@ uint8_t ZeDMD::GetTransport() { return m_pZeDMDComm->GetTransport(); } - return m_pZeDMDWiFi->GetTransport(); + else + { + return m_pZeDMDWiFi->GetTransport(); + } + return 0; } uint8_t ZeDMD::GetUdpDelay() @@ -303,7 +341,11 @@ uint8_t ZeDMD::GetUdpDelay() { return m_pZeDMDComm->GetUdpDelay(); } - return m_pZeDMDWiFi->GetUdpDelay(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetUdpDelay(); + } + return 0; } uint16_t ZeDMD::GetUsbPackageSize() @@ -312,7 +354,11 @@ uint16_t ZeDMD::GetUsbPackageSize() { return m_pZeDMDComm->GetUsbPackageSize(); } - return m_pZeDMDWiFi->GetUsbPackageSize(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetUsbPackageSize(); + } + return 0; } uint8_t ZeDMD::GetYOffset() @@ -321,7 +367,11 @@ uint8_t ZeDMD::GetYOffset() { return m_pZeDMDComm->GetYOffset(); } - return m_pZeDMDWiFi->GetYOffset(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetYOffset(); + } + return 0; } void ZeDMD::LedTest() @@ -336,6 +386,10 @@ void ZeDMD::LedTest() m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::LEDTest); m_pZeDMDWiFi->DisableKeepAlive(); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::LEDTest); + } std::this_thread::sleep_for(std::chrono::milliseconds(8000)); @@ -359,6 +413,10 @@ void ZeDMD::EnableDebug() { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::EnableDebug); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::EnableDebug); + } } void ZeDMD::DisableDebug() @@ -371,6 +429,10 @@ void ZeDMD::DisableDebug() { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); + } } void ZeDMD::EnableVerbose() @@ -378,6 +440,7 @@ void ZeDMD::EnableVerbose() m_verbose = true; m_pZeDMDComm->SetVerbose(true); m_pZeDMDWiFi->SetVerbose(true); + m_pZeDMDSpi->SetVerbose(true); } void ZeDMD::DisableVerbose() @@ -385,6 +448,7 @@ void ZeDMD::DisableVerbose() m_verbose = false; m_pZeDMDComm->SetVerbose(false); m_pZeDMDWiFi->SetVerbose(false); + m_pZeDMDSpi->SetVerbose(false); } void ZeDMD::SetRGBOrder(uint8_t rgbOrder) @@ -399,6 +463,10 @@ void ZeDMD::SetRGBOrder(uint8_t rgbOrder) { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::RGBOrder, rgbOrder); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::RGBOrder, rgbOrder); + } } void ZeDMD::SetBrightness(uint8_t brightness) @@ -413,6 +481,10 @@ void ZeDMD::SetBrightness(uint8_t brightness) { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::Brightness, brightness); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::Brightness, brightness); + } } void ZeDMD::SetPanelClockPhase(uint8_t clockPhase) @@ -427,6 +499,10 @@ void ZeDMD::SetPanelClockPhase(uint8_t clockPhase) { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetClkphase, clockPhase); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetClkphase, clockPhase); + } } void ZeDMD::SetPanelI2sSpeed(uint8_t i2sSpeed) @@ -441,6 +517,10 @@ void ZeDMD::SetPanelI2sSpeed(uint8_t i2sSpeed) { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetI2sspeed, i2sSpeed); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetI2sspeed, i2sSpeed); + } } void ZeDMD::SetPanelLatchBlanking(uint8_t latchBlanking) @@ -455,6 +535,10 @@ void ZeDMD::SetPanelLatchBlanking(uint8_t latchBlanking) { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetLatchBlanking, latchBlanking); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetLatchBlanking, latchBlanking); + } } void ZeDMD::SetPanelMinRefreshRate(uint8_t minRefreshRate) @@ -469,6 +553,10 @@ void ZeDMD::SetPanelMinRefreshRate(uint8_t minRefreshRate) { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetMinRefreshRate, minRefreshRate); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetMinRefreshRate, minRefreshRate); + } } void ZeDMD::SetPanelDriver(uint8_t driver) @@ -483,6 +571,10 @@ void ZeDMD::SetPanelDriver(uint8_t driver) { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetDriver, driver); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetDriver, driver); + } } void ZeDMD::SetTransport(uint8_t transport) @@ -540,6 +632,10 @@ void ZeDMD::SetYOffset(uint8_t yOffset) { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetYOffset, yOffset); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); + } } void ZeDMD::SaveSettings() @@ -558,6 +654,10 @@ void ZeDMD::SaveSettings() // Avoid that client resets the device before settings are saved. m_pZeDMDWiFi->Flush(); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); + } } void ZeDMD::EnableUpscaling() @@ -695,6 +795,24 @@ bool ZeDMD::Open(uint16_t width, uint16_t height) return m_usb; } +bool ZeDMD::OpenSpi(uint16_t width, uint16_t height); +{ + m_spi = m_pZeDMDSpi->Connect(); + + if (m_spi && !m_usb && !m_wifi) + { + m_hd = (width == 256); + + m_pFrameBuffer = (uint8_t*)malloc(ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); + m_pScaledFrameBuffer = (uint8_t*)malloc(ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); + m_pRgb565Buffer = (uint8_t*)malloc(width * height * 2); + + m_pZeDMDSpi->Run(); + } + + return m_spi; +} + void ZeDMD::ClearScreen() { if (m_verbose) m_pZeDMDComm->Log("ZeDMD::ClearScreen"); @@ -707,6 +825,10 @@ void ZeDMD::ClearScreen() { m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); } + else if (m_spi) + { + m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); + } // "Blank" the frame buffer. memset(m_pFrameBuffer, 0, ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); } @@ -717,7 +839,7 @@ void ZeDMD::RenderRgb888(uint8_t* pFrame) { if (m_verbose) m_pZeDMDComm->Log("ZeDMD::RenderRgb888"); - if (!(m_usb || m_wifi) || !UpdateFrameBuffer888(pFrame)) + if (!(m_usb || m_wifi || m_spi) || !UpdateFrameBuffer888(pFrame)) { return; } @@ -734,6 +856,10 @@ void ZeDMD::RenderRgb888(uint8_t* pFrame) { m_pZeDMDComm->QueueFrame(m_pScaledFrameBuffer, bufferSize, true); } + else if (m_spi) + { + m_pZeDMDSpi->QueueFrame(m_pScaledFrameBuffer, bufferSize, true); + } } else { @@ -755,6 +881,10 @@ void ZeDMD::RenderRgb888(uint8_t* pFrame) { m_pZeDMDComm->QueueFrame(m_pRgb565Buffer, rgb565Size * 2); } + else if (m_spi) + { + m_pZeDMDSpi->QueueFrame(m_pRgb565Buffer, rgb565Size * 2); + } } } @@ -777,6 +907,10 @@ void ZeDMD::RenderRgb565(uint16_t* pFrame) { m_pZeDMDComm->QueueFrame(m_pScaledFrameBuffer, size); } + else if (m_spi) + { + m_pZeDMDSpi->QueueFrame(m_pScaledFrameBuffer, size); + } } bool ZeDMD::UpdateFrameBuffer888(uint8_t* pFrame) diff --git a/src/ZeDMD.h b/src/ZeDMD.h index a2a463e..8994e7e 100644 --- a/src/ZeDMD.h +++ b/src/ZeDMD.h @@ -132,6 +132,8 @@ class ZEDMDAPI ZeDMD */ bool OpenDefaultWiFi(); + bool OpenSpi(uint16_t width, uint16_t height); + /** @brief Close connection to ZeDMD * * Close connection to ZeDMD. @@ -641,6 +643,7 @@ class ZEDMDAPI ZeDMD int Scale565(uint8_t* pScaledFrame, uint16_t* pFrame, bool bigEndian); ZeDMDComm* m_pZeDMDComm; + ZeDMDApi* m_pZeDMDSpi; ZeDMDWiFi* m_pZeDMDWiFi; uint16_t m_romWidth; diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp new file mode 100644 index 0000000..ac8dc9c --- /dev/null +++ b/src/ZeDMDSpi.cpp @@ -0,0 +1,192 @@ +#include "ZeDMDSpi.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#if !defined(__linux__) +#error "ZeDMDSpi is only supported on Linux (Raspberry Pi)." +#endif + +namespace +{ +constexpr uint8_t kSpiBitsPerWord = 8; +constexpr uint8_t kSpiMode = SPI_MODE_0; +constexpr unsigned int kCsGpio = 25; // GPIO25 on Raspberry Pi +constexpr const char kGpioConsumer[] = "ZeDMDSpi"; +} // namespace + +bool ZeDMDSpi::IsSupportedPlatform() const +{ +#if defined(__linux__) && (defined(__arm__) || defined(__aarch64__)) + std::ifstream model("/proc/device-tree/model"); + if (!model.is_open()) + { + return false; + } + std::string line; + std::getline(model, line); + return line.find("Raspberry Pi") != std::string::npos; +#else + return false; +#endif +} + +bool ZeDMDSpi::Connect() +{ + if (!IsSupportedPlatform()) + { + Log("ZeDMDSpi: unsupported platform. This transport only runs on Raspberry Pi with Linux."); + return false; + } + + if (m_connected) + { + return true; + } + + m_fileDescriptor = open(SPI_DEVICE, O_RDWR | O_CLOEXEC); + if (m_fileDescriptor < 0) + { + Log("ZeDMDSpi: couldn't open SPI device %s: %s", SPI_DEVICE, strerror(errno)); + return false; + } + + if (ioctl(m_fileDescriptor, SPI_IOC_WR_MODE, &kSpiMode) < 0) + { + Log("ZeDMDSpi: couldn't set SPI mode: %s", strerror(errno)); + Disconnect(); + return false; + } + // Disable kernel chip-select handling; we drive CS manually on GPIO25. +#if defined(SPI_NO_CS) && defined(SPI_IOC_WR_MODE32) + uint32_t mode32 = kSpiMode | SPI_NO_CS; + if (ioctl(m_fileDescriptor, SPI_IOC_WR_MODE32, &mode32) < 0) + { + Log("ZeDMDSpi: warning - couldn't set SPI_NO_CS: %s", strerror(errno)); + } +#endif + + uint8_t bitsPerWord = kSpiBitsPerWord; + if (ioctl(m_fileDescriptor, SPI_IOC_WR_BITS_PER_WORD, &bitsPerWord) < 0) + { + Log("ZeDMDSpi: couldn't set SPI bits/word: %s", strerror(errno)); + Disconnect(); + return false; + } + + if (ioctl(m_fileDescriptor, SPI_IOC_WR_MAX_SPEED_HZ, &m_speed) < 0) + { + Log("ZeDMDSpi: couldn't set SPI speed: %s", strerror(errno)); + Disconnect(); + return false; + } + + m_gpioChip = gpiod_chip_open(GPIO_CHIP); + if (!m_gpioChip) + { + Log("ZeDMDSpi: couldn't open gpio chip %s: %s", GPIO_CHIP, strerror(errno)); + Disconnect(); + return false; + } + + m_csLine = gpiod_chip_get_line(m_gpioChip, kCsGpio); + if (!m_csLine) + { + Log("ZeDMDSpi: couldn't get CS gpio %d: %s", kCsGpio, strerror(errno)); + Disconnect(); + return false; + } + + if (gpiod_line_request_output(m_csLine, kGpioConsumer, 1) < 0) + { + Log("ZeDMDSpi: couldn't request CS gpio %d as output: %s", kCsGpio, strerror(errno)); + Disconnect(); + return false; + } + + // Keep CS high when idle. + gpiod_line_set_value(m_csLine, 1); + + m_connected = true; + return true; +} + +void ZeDMDSpi::Disconnect() +{ + if (m_csLine) + { + gpiod_line_set_value(m_csLine, 1); + gpiod_line_release(m_csLine); + m_csLine = nullptr; + } + if (m_gpioChip) + { + gpiod_chip_close(m_gpioChip); + m_gpioChip = nullptr; + } + if (m_fileDescriptor >= 0) + { + close(m_fileDescriptor); + m_fileDescriptor = -1; + } + m_connected = false; +} + +bool ZeDMDSpi::IsConnected() { return m_connected; } + +void ZeDMDSpi::Reset() {} + +bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) +{ + if (!m_connected || m_fileDescriptor < 0) + { + Log("ZeDMDSpi: device not connected"); + return false; + } + + uint32_t remaining = size; + uint8_t* cursor = pData; + + if (m_csLine && gpiod_line_set_value(m_csLine, 0) < 0) + { + Log("ZeDMDSpi: failed to pull CS low: %s", strerror(errno)); + return false; + } + + while (remaining > 0) + { + uint32_t chunkSize = std::min(remaining, spi_kernel_bufsize); + struct spi_ioc_transfer transfer; + memset(&transfer, 0, sizeof(transfer)); + transfer.tx_buf = reinterpret_cast<__u64>(cursor); + transfer.len = chunkSize; + transfer.speed_hz = m_speed; + transfer.bits_per_word = kSpiBitsPerWord; + + int res = ioctl(m_fileDescriptor, SPI_IOC_MESSAGE(1), &transfer); + if (res < 0) + { + Log("ZeDMDSpi: SPI write failed: %s", strerror(errno)); + if (m_csLine) gpiod_line_set_value(m_csLine, 1); + return false; + } + + cursor += chunkSize; + remaining -= chunkSize; + } + + if (m_csLine && gpiod_line_set_value(m_csLine, 1) < 0) + { + Log("ZeDMDSpi: failed to release CS: %s", strerror(errno)); + return false; + } + + return true; +} diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h new file mode 100644 index 0000000..dcf05e5 --- /dev/null +++ b/src/ZeDMDSpi.h @@ -0,0 +1,42 @@ +#pragma once + +#include "ZeDMDComm.h" + +#if defined(__linux__) +#include +#include +#endif + +#include + +#define GPIO_CHIP "/dev/gpiochip0" +#define SPI_DEVICE "/dev/spidev1.0" + +// can be read from /sys/module/spidev/parameters/bufsiz, but hardcoded now for simplicity +constexpr int spi_kernel_bufsize = 4096; +constexpr uint32_t spi_default_speed_hz = 12000000; + +class ZeDMDSpi : public ZeDMDComm +{ + public: + ZeDMDSpi() : ZeDMDComm() {} + ~ZeDMDSpi() { Disconnect(); } + + bool Connect() override; + void Disconnect() override; + bool IsConnected() override; + + protected: + bool SendChunks(uint8_t* pData, uint16_t size) override; + void Reset() override; + + private: + bool IsSupportedPlatform() const; + + uint32_t m_speed = spi_default_speed_hz; + int m_fileDescriptor = -1; + gpiod_chip* m_gpioChip = nullptr; + gpiod_line* m_csLine = nullptr; + bool m_connected = false; + bool m_keepAlive = false; +}; From 1b4e138983bd408ee7dbcae76ad51012303d3f70 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 14:17:10 +0100 Subject: [PATCH 02/42] delay --- src/ZeDMDComm.cpp | 2 +- src/ZeDMDSpi.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index 231fcfe..f2fed97 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -879,7 +879,7 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) { ZeDMDFrameData frameData = *it; - if (pFrame->command != ZEDMD_COMM_COMMAND::RGB565ZonesStream) + if (pFrame->command != ZEDMD_COMM_COMMAND::RGB565ZonesStream && pFrame->command != ZEDMD_COMM_COMMAND::RGB888ZonesStream) { memcpy(&payload[pos], CTRL_CHARS_HEADER, CTRL_CHARS_HEADER_SIZE); pos += CTRL_CHARS_HEADER_SIZE; diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index ac8dc9c..bf191ee 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -160,6 +160,8 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) return false; } + std::this_thread::sleep_for(std::chrono::microseconds(10)); + while (remaining > 0) { uint32_t chunkSize = std::min(remaining, spi_kernel_bufsize); From cfb509ef22a60f24470fba124924dd71d739fd7f Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 15:20:59 +0100 Subject: [PATCH 03/42] set width and height --- src/ZeDMD.cpp | 4 +++- src/ZeDMDSpi.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 9ed6d3d..befc000 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -795,7 +795,7 @@ bool ZeDMD::Open(uint16_t width, uint16_t height) return m_usb; } -bool ZeDMD::OpenSpi(uint16_t width, uint16_t height); +bool ZeDMD::OpenSpi(uint16_t width, uint16_t height) { m_spi = m_pZeDMDSpi->Connect(); @@ -807,6 +807,8 @@ bool ZeDMD::OpenSpi(uint16_t width, uint16_t height); m_pScaledFrameBuffer = (uint8_t*)malloc(ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); m_pRgb565Buffer = (uint8_t*)malloc(width * height * 2); + m_pZeDMDSpi->SetWidth(width); + m_pZeDMDSpi->SetHeight(height); m_pZeDMDSpi->Run(); } diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index dcf05e5..7e0340f 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -26,6 +26,9 @@ class ZeDMDSpi : public ZeDMDComm void Disconnect() override; bool IsConnected() override; + void SetWidth(uint16_t width) { m_width = width; } + void SetHeight(uint16_t height) { m_height = height; } + protected: bool SendChunks(uint8_t* pData, uint16_t size) override; void Reset() override; From a5074f4f7f626a59bb3ff0d3784c53a62ca9f253 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 15:59:02 +0100 Subject: [PATCH 04/42] compile switch --- src/ZeDMDComm.cpp | 10 +++++----- src/ZeDMDSpi.cpp | 27 +++++++++++++++++++++++---- src/ZeDMDSpi.h | 10 ++++++++-- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index f2fed97..b351a71 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -184,9 +184,7 @@ void ZeDMDComm::QueueCommand(char command, uint8_t value) { QueueCommand(command void ZeDMDComm::QueueCommand(char command) { QueueCommand(command, nullptr, 0); } -void ZeDMDComm::QueueFrame(uint8_t* data, int size) { - QueueFrame(uint8_t* data, int size, false); -} +void ZeDMDComm::QueueFrame(uint8_t* data, int size) { QueueFrame(uint8_t* data, int size, false); } void ZeDMDComm::QueueFrame(uint8_t* data, int size, bool rgb888) { @@ -879,7 +877,8 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) { ZeDMDFrameData frameData = *it; - if (pFrame->command != ZEDMD_COMM_COMMAND::RGB565ZonesStream && pFrame->command != ZEDMD_COMM_COMMAND::RGB888ZonesStream) + if (pFrame->command != ZEDMD_COMM_COMMAND::RGB565ZonesStream && + pFrame->command != ZEDMD_COMM_COMMAND::RGB888ZonesStream) { memcpy(&payload[pos], CTRL_CHARS_HEADER, CTRL_CHARS_HEADER_SIZE); pos += CTRL_CHARS_HEADER_SIZE; @@ -926,7 +925,8 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) } } - if (pFrame->command == ZEDMD_COMM_COMMAND::RGB565ZonesStream || pFrame->command == ZEDMD_COMM_COMMAND::RGB888ZonesStream) + if (pFrame->command == ZEDMD_COMM_COMMAND::RGB565ZonesStream || + pFrame->command == ZEDMD_COMM_COMMAND::RGB888ZonesStream) { memcpy(&payload[pos], CTRL_CHARS_HEADER, CTRL_CHARS_HEADER_SIZE); pos += CTRL_CHARS_HEADER_SIZE; diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index bf191ee..0e406e8 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -1,18 +1,17 @@ #include "ZeDMDSpi.h" +#if defined(__linux__) #include #include #include #include #include +#include #include #include #include - -#if !defined(__linux__) -#error "ZeDMDSpi is only supported on Linux (Raspberry Pi)." -#endif +#include namespace { @@ -192,3 +191,23 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) return true; } + +#else // non-Linux stub + +bool ZeDMDSpi::IsSupportedPlatform() const { return false; } + +bool ZeDMDSpi::Connect() +{ + Log("ZeDMDSpi: unsupported platform. This transport only runs on Raspberry Pi with Linux."); + return false; +} + +void ZeDMDSpi::Disconnect() {} + +bool ZeDMDSpi::IsConnected() { return false; } + +void ZeDMDSpi::Reset() {} + +bool ZeDMDSpi::SendChunks(uint8_t*, uint16_t) { return false; } + +#endif // __linux__ diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 7e0340f..fc606ae 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -1,14 +1,18 @@ #pragma once +#include + #include "ZeDMDComm.h" #if defined(__linux__) #include #include +#else +// Forward declarations so non-Linux builds can compile the stub implementation. +struct gpiod_chip; +struct gpiod_line; #endif -#include - #define GPIO_CHIP "/dev/gpiochip0" #define SPI_DEVICE "/dev/spidev1.0" @@ -38,8 +42,10 @@ class ZeDMDSpi : public ZeDMDComm uint32_t m_speed = spi_default_speed_hz; int m_fileDescriptor = -1; +#if defined(__linux__) gpiod_chip* m_gpioChip = nullptr; gpiod_line* m_csLine = nullptr; +#endif bool m_connected = false; bool m_keepAlive = false; }; From 556805f92b7792290da11b5803c2a87f45e8cfab Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 17:02:57 +0100 Subject: [PATCH 05/42] fixed typos --- src/ZeDMD.cpp | 2 +- src/ZeDMD.h | 2 +- src/ZeDMDComm.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index befc000..0b681af 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -310,7 +310,7 @@ uint8_t ZeDMD::GetPanelMinRefreshRate() { return m_pZeDMDWiFi->GetPanelMinRefreshRate(); } - retrun 0; + return 0; } uint8_t ZeDMD::GetPanelDriver() diff --git a/src/ZeDMD.h b/src/ZeDMD.h index 8994e7e..1644659 100644 --- a/src/ZeDMD.h +++ b/src/ZeDMD.h @@ -643,7 +643,7 @@ class ZEDMDAPI ZeDMD int Scale565(uint8_t* pScaledFrame, uint16_t* pFrame, bool bigEndian); ZeDMDComm* m_pZeDMDComm; - ZeDMDApi* m_pZeDMDSpi; + ZeDMDSpi* m_pZeDMDSpi; ZeDMDWiFi* m_pZeDMDWiFi; uint16_t m_romWidth; diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index b351a71..718f869 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -184,7 +184,7 @@ void ZeDMDComm::QueueCommand(char command, uint8_t value) { QueueCommand(command void ZeDMDComm::QueueCommand(char command) { QueueCommand(command, nullptr, 0); } -void ZeDMDComm::QueueFrame(uint8_t* data, int size) { QueueFrame(uint8_t* data, int size, false); } +void ZeDMDComm::QueueFrame(uint8_t* data, int size) { QueueFrame(data, size, false); } void ZeDMDComm::QueueFrame(uint8_t* data, int size, bool rgb888) { From 6276bb32ac134219100026bbf4f117b82aa1e839 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 17:09:58 +0100 Subject: [PATCH 06/42] ZeDMDSpi --- src/ZeDMD.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 0b681af..98c89c7 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -5,6 +5,7 @@ #include "FrameUtil.h" #include "ZeDMDComm.h" #include "ZeDMDWiFi.h" +#include "ZeDMDSpi.h" const int endian_check = 1; #define is_bigendian() ((*(char*)&endian_check) == 0) @@ -20,12 +21,14 @@ ZeDMD::ZeDMD() m_pZeDMDComm = new ZeDMDComm(); m_pZeDMDWiFi = new ZeDMDWiFi(); + m_pZeDMDSpi = new ZeDMDSpi(); } ZeDMD::~ZeDMD() { delete m_pZeDMDComm; delete m_pZeDMDWiFi; + delete m_pZeDMDSpi; if (m_pFrameBuffer) { @@ -47,6 +50,7 @@ void ZeDMD::SetLogCallback(ZeDMD_LogCallback callback, const void* userData) { m_pZeDMDComm->SetLogCallback(callback, userData); m_pZeDMDWiFi->SetLogCallback(callback, userData); + m_pZeDMDSpi->SetLogCallback(callback, userData); } void ZeDMD::Close() From 6cbf47d11cb7ccf38ca439d999132276a4f61c7c Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 17:13:03 +0100 Subject: [PATCH 07/42] added forward declaration --- src/ZeDMD.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ZeDMD.h b/src/ZeDMD.h index 1644659..3b29dc3 100644 --- a/src/ZeDMD.h +++ b/src/ZeDMD.h @@ -37,6 +37,7 @@ typedef void(ZEDMDCALLBACK* ZeDMD_LogCallback)(const char* format, va_list args, class ZeDMDComm; class ZeDMDWiFi; +class ZeDMDSpi; class ZEDMDAPI ZeDMD { From b2287025e2b064af2d025348bf28ac51ebb5db3d Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 17:38:07 +0100 Subject: [PATCH 08/42] gpio --- .github/workflows/libzedmd.yml | 4 ++++ CMakeLists.txt | 19 +++++++++++++++---- src/ZeDMDSpi.cpp | 6 +++--- src/ZeDMDSpi.h | 4 ++-- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/workflows/libzedmd.yml b/.github/workflows/libzedmd.yml index 45a9b71..8659a15 100644 --- a/.github/workflows/libzedmd.yml +++ b/.github/workflows/libzedmd.yml @@ -63,6 +63,10 @@ jobs: name: Add autoconf and automake (mac runner) run: | brew install autoconf automake libtool + - if: (matrix.platform == 'linux' && matrix.arch == 'aarch64') + run: | + sudo apt-get update + sudo apt-get install -y libgpiod-dev - name: Build libzedmd-${{ matrix.platform }}-${{ matrix.arch }} run: | ./platforms/${{ matrix.platform }}/${{ matrix.arch }}/external.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 604ce19..4b172f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,10 +83,21 @@ set(ZEDMD_SOURCES third-party/include/miniz/miniz.c ) -set(ZEDMD_INCLUDE_DIRS - src - third-party/include -) +if (ARCH STREQUAL "aarch64") + find_package(PkgConfig REQUIRED) + pkg_check_modules(GPIOD REQUIRED libgpiod) + + set(ZEDMD_INCLUDE_DIRS + src + third-party/include + ${GPIOD_LIBRARIES} + ) +else() + set(ZEDMD_INCLUDE_DIRS + src + third-party/include + ) +endif() if(BUILD_SHARED) add_library(zedmd_shared SHARED ${ZEDMD_SOURCES}) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 0e406e8..d605c89 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -1,6 +1,6 @@ #include "ZeDMDSpi.h" -#if defined(__linux__) +#if defined(__linux__) && defined(__aarch64__) #include #include #include @@ -23,7 +23,7 @@ constexpr const char kGpioConsumer[] = "ZeDMDSpi"; bool ZeDMDSpi::IsSupportedPlatform() const { -#if defined(__linux__) && (defined(__arm__) || defined(__aarch64__)) +#if defined(__linux__) && defined(__aarch64__) std::ifstream model("/proc/device-tree/model"); if (!model.is_open()) { @@ -192,7 +192,7 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) return true; } -#else // non-Linux stub +#else // non-Linux or non-aarch64 stub bool ZeDMDSpi::IsSupportedPlatform() const { return false; } diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index fc606ae..9c3aa30 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -4,7 +4,7 @@ #include "ZeDMDComm.h" -#if defined(__linux__) +#if defined(__linux__) && defined(__aarch64__) #include #include #else @@ -42,7 +42,7 @@ class ZeDMDSpi : public ZeDMDComm uint32_t m_speed = spi_default_speed_hz; int m_fileDescriptor = -1; -#if defined(__linux__) +#if defined(__linux__) && defined(__aarch64__) gpiod_chip* m_gpioChip = nullptr; gpiod_line* m_csLine = nullptr; #endif From cf0d4dae39d71c7c2c6634458546215280a45180 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 17:56:20 +0100 Subject: [PATCH 09/42] fixed linker --- CMakeLists.txt | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b172f6..05698aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,19 +86,13 @@ set(ZEDMD_SOURCES if (ARCH STREQUAL "aarch64") find_package(PkgConfig REQUIRED) pkg_check_modules(GPIOD REQUIRED libgpiod) - - set(ZEDMD_INCLUDE_DIRS - src - third-party/include - ${GPIOD_LIBRARIES} - ) -else() - set(ZEDMD_INCLUDE_DIRS - src - third-party/include - ) endif() +set(ZEDMD_INCLUDE_DIRS + src + third-party/include +) + if(BUILD_SHARED) add_library(zedmd_shared SHARED ${ZEDMD_SOURCES}) @@ -123,7 +117,11 @@ if(BUILD_SHARED) target_link_directories(zedmd_shared PUBLIC third-party/runtime-libs/${PLATFORM}/${ARCH} ) - target_link_libraries(zedmd_shared PUBLIC serialport sockpp) + if (ARCH STREQUAL "aarch64") + target_link_libraries(zedmd_shared PUBLIC serialport sockpp ${GPIOD_LIBRARIES}) + else() + target_link_libraries(zedmd_shared PUBLIC serialport sockpp) + endif() elseif(PLATFORM STREQUAL "ios" OR PLATFORM STREQUAL "ios-simulator" OR PLATFORM STREQUAL "tvos") target_link_directories(zedmd_shared PUBLIC third-party/build-libs/${PLATFORM}/${ARCH} From 0beba1e6998c854a65ceb9251deab4ba6edd122a Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 19:16:28 +0100 Subject: [PATCH 10/42] fixed linker --- CMakeLists.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05698aa..b170287 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -226,7 +226,11 @@ if(BUILD_STATIC) target_link_directories(zedmd-test-portable PUBLIC third-party/runtime-libs/${PLATFORM}/${ARCH} ) - target_link_libraries(zedmd-test-portable PUBLIC zedmd_static serialport sockpp) + if (ARCH STREQUAL "aarch64") + target_link_libraries(zedmd-test-portable PUBLIC zedmd_static serialport sockpp ${GPIOD_LIBRARIES}) + else() + target_link_libraries(zedmd-test-portable PUBLIC zedmd_static serialport sockpp) + endif() endif() if(POST_BUILD_COPY_EXT_LIBS) @@ -257,7 +261,11 @@ if(BUILD_STATIC) target_link_directories(zedmd-client-portable PUBLIC third-party/runtime-libs/${PLATFORM}/${ARCH} ) - target_link_libraries(zedmd-client-portable PUBLIC zedmd_static cargs serialport sockpp) + if (ARCH STREQUAL "aarch64") + target_link_libraries(zedmd-client-portable PUBLIC zedmd_static cargs serialport sockpp ${GPIOD_LIBRARIES}) + else() + target_link_libraries(zedmd-client-portable PUBLIC zedmd_static cargs serialport sockpp) + endif() endif() if(POST_BUILD_COPY_EXT_LIBS) From 0778f3dcb35dbe588fe74cc4821c9adef2bfb306 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 19:21:09 +0100 Subject: [PATCH 11/42] fixed android --- src/ZeDMDSpi.cpp | 4 ++-- src/ZeDMDSpi.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index d605c89..f68ec14 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -1,6 +1,6 @@ #include "ZeDMDSpi.h" -#if defined(__linux__) && defined(__aarch64__) +#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) #include #include #include @@ -23,7 +23,7 @@ constexpr const char kGpioConsumer[] = "ZeDMDSpi"; bool ZeDMDSpi::IsSupportedPlatform() const { -#if defined(__linux__) && defined(__aarch64__) +#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) std::ifstream model("/proc/device-tree/model"); if (!model.is_open()) { diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 9c3aa30..325d30b 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -4,7 +4,7 @@ #include "ZeDMDComm.h" -#if defined(__linux__) && defined(__aarch64__) +#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) #include #include #else @@ -42,7 +42,7 @@ class ZeDMDSpi : public ZeDMDComm uint32_t m_speed = spi_default_speed_hz; int m_fileDescriptor = -1; -#if defined(__linux__) && defined(__aarch64__) +#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) gpiod_chip* m_gpioChip = nullptr; gpiod_line* m_csLine = nullptr; #endif From 66b950555d5636995b7adb5001054371f8ae7a4f Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 20:35:24 +0100 Subject: [PATCH 12/42] Create a rising edge to switch ZeDMD from loopback to SPI mode. --- src/ZeDMDSpi.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index f68ec14..1064272 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -110,7 +110,10 @@ bool ZeDMDSpi::Connect() return false; } + // Create a rising edge to switch ZeDMD from loopback to SPI mode. // Keep CS high when idle. + gpiod_line_set_value(m_csLine, 0); + std::this_thread::sleep_for(std::chrono::microseconds(100)); gpiod_line_set_value(m_csLine, 1); m_connected = true; From 71f7c1d30539ce1d98742fea449f37fc2078c0b5 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 22:43:30 +0100 Subject: [PATCH 13/42] removed redundant code --- src/ZeDMDSpi.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 1064272..166c539 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -23,7 +23,6 @@ constexpr const char kGpioConsumer[] = "ZeDMDSpi"; bool ZeDMDSpi::IsSupportedPlatform() const { -#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) std::ifstream model("/proc/device-tree/model"); if (!model.is_open()) { @@ -32,9 +31,6 @@ bool ZeDMDSpi::IsSupportedPlatform() const std::string line; std::getline(model, line); return line.find("Raspberry Pi") != std::string::npos; -#else - return false; -#endif } bool ZeDMDSpi::Connect() @@ -201,7 +197,7 @@ bool ZeDMDSpi::IsSupportedPlatform() const { return false; } bool ZeDMDSpi::Connect() { - Log("ZeDMDSpi: unsupported platform. This transport only runs on Raspberry Pi with Linux."); + Log("ZeDMDSpi: unsupported platform. SPI only runs on Raspberry Pi with Linux."); return false; } From 5b4e8e293e7c649ec0e725f4808959f179a100a7 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 23:29:31 +0100 Subject: [PATCH 14/42] changed compile switch --- src/ZeDMDSpi.cpp | 2 +- src/ZeDMDSpi.h | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 166c539..98b9de8 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -1,6 +1,6 @@ #include "ZeDMDSpi.h" -#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) +#if defined(RASPI) #include #include #include diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 325d30b..568cd33 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -1,10 +1,14 @@ #pragma once +#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) +#define RASPI 1 +#endif + #include #include "ZeDMDComm.h" -#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) +#if defined(RASPI) #include #include #else @@ -42,7 +46,7 @@ class ZeDMDSpi : public ZeDMDComm uint32_t m_speed = spi_default_speed_hz; int m_fileDescriptor = -1; -#if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) +#if defined(RASPI) gpiod_chip* m_gpioChip = nullptr; gpiod_line* m_csLine = nullptr; #endif From e9e2e484a2dcae711dc42c246be1ea70800d72f4 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 16 Dec 2025 23:58:34 +0100 Subject: [PATCH 15/42] SPI_SUPPORT --- CMakeLists.txt | 2 ++ src/ZeDMDSpi.cpp | 2 +- src/ZeDMDSpi.h | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b170287..0472b59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ set(ARCH "x64" CACHE STRING "Arch") option(BUILD_SHARED "Option to build shared library" ON) option(BUILD_STATIC "Option to build static library" ON) option(POST_BUILD_COPY_EXT_LIBS "Option to copy external libraries to build directory" ON) +option(SPI_SUPPORT "SPI support" OFF) +add_compile_definitions($<$:SPI_SUPPORT>) message(STATUS "PLATFORM: ${PLATFORM}") message(STATUS "ARCH: ${ARCH}") diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 98b9de8..56ea069 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -1,6 +1,6 @@ #include "ZeDMDSpi.h" -#if defined(RASPI) +#if defined(SPI_SUPPORT) #include #include #include diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 568cd33..25a5abc 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -1,14 +1,14 @@ #pragma once #if defined(__linux__) && defined(__aarch64__) && !defined(__ANDROID__) -#define RASPI 1 +#define SPI_SUPPORT 1 #endif #include #include "ZeDMDComm.h" -#if defined(RASPI) +#if defined(SPI_SUPPORT) #include #include #else @@ -46,7 +46,7 @@ class ZeDMDSpi : public ZeDMDComm uint32_t m_speed = spi_default_speed_hz; int m_fileDescriptor = -1; -#if defined(RASPI) +#if defined(SPI_SUPPORT) gpiod_chip* m_gpioChip = nullptr; gpiod_line* m_csLine = nullptr; #endif From f0f4ec3d172080f385c711507be9f33bd59db66f Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Wed, 17 Dec 2025 13:53:27 +0100 Subject: [PATCH 16/42] added logging --- src/ZeDMDSpi.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 56ea069..1829e22 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -52,6 +52,7 @@ bool ZeDMDSpi::Connect() Log("ZeDMDSpi: couldn't open SPI device %s: %s", SPI_DEVICE, strerror(errno)); return false; } + Log("ZeDMDSpi: opened SPI device %s", SPI_DEVICE); if (ioctl(m_fileDescriptor, SPI_IOC_WR_MODE, &kSpiMode) < 0) { @@ -82,6 +83,7 @@ bool ZeDMDSpi::Connect() Disconnect(); return false; } + Log("ZeDMDSpi: set SPI speed %d", m_speed); m_gpioChip = gpiod_chip_open(GPIO_CHIP); if (!m_gpioChip) @@ -112,6 +114,8 @@ bool ZeDMDSpi::Connect() std::this_thread::sleep_for(std::chrono::microseconds(100)); gpiod_line_set_value(m_csLine, 1); + Log("ZeDMDSpi: signaling via GPIO %d established", kCsGpio); + m_connected = true; return true; } From d9a8f7ef460528ad3fa5d42691810290286fa917 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sat, 20 Dec 2025 10:29:32 +0100 Subject: [PATCH 17/42] added raw streams --- src/ZeDMDComm.cpp | 34 +++++++++++++++++++++++++++++----- src/ZeDMDComm.h | 9 +++++++-- src/ZeDMDSpi.cpp | 2 ++ src/ZeDMDSpi.h | 6 +++++- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index 718f869..572505b 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -188,6 +188,17 @@ void ZeDMDComm::QueueFrame(uint8_t* data, int size) { QueueFrame(data, size, fal void ZeDMDComm::QueueFrame(uint8_t* data, int size, bool rgb888) { + if (!m_zoneStream) + { + ZeDMDFrame frame(rgb888 ? ZEDMD_COMM_COMMAND::RGB888Stream : ZEDMD_COMM_COMMAND::RGB565Stream, data, size); + + m_frameQueueMutex.lock(); + m_frames.push(std::move(frame)); + m_frameQueueMutex.unlock(); + + return; + } + if (m_fullFrameFlag.load(std::memory_order_relaxed)) { m_fullFrameFlag.store(false, std::memory_order_release); @@ -221,7 +232,7 @@ void ZeDMDComm::QueueFrame(uint8_t* data, int size, bool rgb888) uint8_t idx = 0; uint8_t bitsPerPixel = rgb888 ? 3 : 2; - uint16_t zonesBytesLimit = (m_s3 && !m_cdc) ? ZEDMD_S3_ZONES_BYTE_LIMIT : ZEDMD_ZONES_BYTE_LIMIT; + uint16_t zonesBytesLimit = (rgb888) ? ZEDMD_ZONES_BYTE_LIMIT_RGB888 : ZEDMD_ZONES_BYTE_LIMIT_RGB565; const uint16_t zoneBytes = m_zoneWidth * m_zoneHeight * bitsPerPixel; const uint16_t zoneBytesTotal = zoneBytes + 1; uint8_t* zone = (uint8_t*)malloc(zoneBytes); @@ -864,8 +875,21 @@ void ZeDMDComm::RebootToBootloader(bool reenableKeepAive) bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) { - static uint8_t payload[36864] = {0}; - memset(payload, 0, 36864); + if (pFrame->command == ZEDMD_COMM_COMMAND::RGB565Stream || pFrame->command == ZEDMD_COMM_COMMAND::RGB888Stream) + { + for (auto it = pFrame->data.rbegin(); it != pFrame->data.rend(); ++it) + { + ZeDMDFrameData frameData = *it; + if (!SendChunks(frameData.data, frameData.size)) return false; + + m_lastKeepAlive = std::chrono::steady_clock::now(); + } + return true; + } + + // 256*64*3 (RGB888) = 49152 + headers + static uint8_t payload[50176] = {0}; + memset(payload, 0, 50176); memcpy(payload, FRAME_HEADER, FRAME_HEADER_SIZE); uint16_t pos = FRAME_HEADER_SIZE; @@ -877,8 +901,8 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) { ZeDMDFrameData frameData = *it; - if (pFrame->command != ZEDMD_COMM_COMMAND::RGB565ZonesStream && - pFrame->command != ZEDMD_COMM_COMMAND::RGB888ZonesStream) + if (!m_compression || (pFrame->command != ZEDMD_COMM_COMMAND::RGB565ZonesStream && + pFrame->command != ZEDMD_COMM_COMMAND::RGB888ZonesStream)) { memcpy(&payload[pos], CTRL_CHARS_HEADER, CTRL_CHARS_HEADER_SIZE); pos += CTRL_CHARS_HEADER_SIZE; diff --git a/src/ZeDMDComm.h b/src/ZeDMDComm.h index 8fa6b93..ccdc956 100644 --- a/src/ZeDMDComm.h +++ b/src/ZeDMDComm.h @@ -41,8 +41,8 @@ #define ZEDMD_COMM_FRAME_QUEUE_SIZE_MAX 8 -#define ZEDMD_ZONES_BYTE_LIMIT (128 * 4 * 2 + 16) -#define ZEDMD_S3_ZONES_BYTE_LIMIT (ZEDMD_ZONES_BYTE_LIMIT) +#define ZEDMD_ZONES_BYTE_LIMIT_RGB565 (128 * 4 * 2 + 16) +#define ZEDMD_ZONES_BYTE_LIMIT_RGB888 (128 * 4 * 3 + 16) typedef enum { @@ -94,6 +94,8 @@ typedef enum RGB888ZonesStream = 0x04, RGB565ZonesStream = 0x05, RenderFrame = 0x06, + RGB888Stream = 0x07, + RGB565Stream = 0x08, ClearScreen = 0x0a, @@ -295,6 +297,9 @@ class ZeDMDComm uint8_t m_currentCommand = 0; + bool m_compression = true; + bool m_zoneStream = true; + ZeDMD_DeviceType m_deviceType = ZeDMD_DeviceType::ESP32; private: diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 1829e22..b56a828 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -192,6 +192,8 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) return false; } + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + return true; } diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 25a5abc..45b5ce7 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -27,7 +27,11 @@ constexpr uint32_t spi_default_speed_hz = 12000000; class ZeDMDSpi : public ZeDMDComm { public: - ZeDMDSpi() : ZeDMDComm() {} + ZeDMDSpi() : ZeDMDComm() + { + m_compression = false; + m_zoneStream = false; + } ~ZeDMDSpi() { Disconnect(); } bool Connect() override; From 3b2a36619915f4d26a684f863254237049864828 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sat, 20 Dec 2025 12:28:22 +0100 Subject: [PATCH 18/42] don't send commands when streaming raw --- src/ZeDMDComm.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index 572505b..7826070 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -875,15 +875,21 @@ void ZeDMDComm::RebootToBootloader(bool reenableKeepAive) bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) { - if (pFrame->command == ZEDMD_COMM_COMMAND::RGB565Stream || pFrame->command == ZEDMD_COMM_COMMAND::RGB888Stream) + if (!m_zoneStream && !m_compression) { - for (auto it = pFrame->data.rbegin(); it != pFrame->data.rend(); ++it) + // Direct stream without compression and zones. + if (pFrame->command == ZEDMD_COMM_COMMAND::RGB565Stream || pFrame->command == ZEDMD_COMM_COMMAND::RGB888Stream) { - ZeDMDFrameData frameData = *it; - if (!SendChunks(frameData.data, frameData.size)) return false; + for (auto it = pFrame->data.rbegin(); it != pFrame->data.rend(); ++it) + { + ZeDMDFrameData frameData = *it; + if (!SendChunks(frameData.data, frameData.size)) return false; - m_lastKeepAlive = std::chrono::steady_clock::now(); + m_lastKeepAlive = std::chrono::steady_clock::now(); + } } + + m_lastKeepAlive = std::chrono::steady_clock::now(); return true; } From 827acfe1e481e90b2dabdce07e024da53c18b048 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sat, 20 Dec 2025 14:12:18 +0100 Subject: [PATCH 19/42] debug --- src/ZeDMDComm.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index 7826070..6493dac 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -883,6 +883,8 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) for (auto it = pFrame->data.rbegin(); it != pFrame->data.rend(); ++it) { ZeDMDFrameData frameData = *it; + Log("StreamBytes, command %02X, length %d", pFrame->command, frameData.size); + if (!SendChunks(frameData.data, frameData.size)) return false; m_lastKeepAlive = std::chrono::steady_clock::now(); @@ -893,6 +895,8 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) return true; } + Log("StreamBytes, command %02X, length %d", pFrame->command); + // 256*64*3 (RGB888) = 49152 + headers static uint8_t payload[50176] = {0}; memset(payload, 0, 50176); From a6fff4bee51f6a48ab7e8046ebfb1212dba2734e Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sat, 20 Dec 2025 14:47:29 +0100 Subject: [PATCH 20/42] debug --- src/ZeDMDSpi.cpp | 2 +- src/ZeDMDSpi.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index b56a828..5356d40 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -181,7 +181,7 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) if (m_csLine) gpiod_line_set_value(m_csLine, 1); return false; } - + Log("ZeDMDSpi: send chunk of %d bytes", chunkSize); cursor += chunkSize; remaining -= chunkSize; } diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 45b5ce7..3ba6763 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -22,7 +22,7 @@ struct gpiod_line; // can be read from /sys/module/spidev/parameters/bufsiz, but hardcoded now for simplicity constexpr int spi_kernel_bufsize = 4096; -constexpr uint32_t spi_default_speed_hz = 12000000; +constexpr uint32_t spi_default_speed_hz = 4000000; class ZeDMDSpi : public ZeDMDComm { From 0ec167be93c8da8ade50f259832b4478cd547508 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sat, 20 Dec 2025 17:16:54 +0100 Subject: [PATCH 21/42] 64MHz --- src/ZeDMDSpi.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 3ba6763..62d8818 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -22,7 +22,7 @@ struct gpiod_line; // can be read from /sys/module/spidev/parameters/bufsiz, but hardcoded now for simplicity constexpr int spi_kernel_bufsize = 4096; -constexpr uint32_t spi_default_speed_hz = 4000000; +constexpr uint32_t spi_default_speed_hz = 64000000; class ZeDMDSpi : public ZeDMDComm { From 960b17cd99dbf71ba33ff108b7ba2f9edeb80e13 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sat, 20 Dec 2025 18:06:22 +0100 Subject: [PATCH 22/42] fixed RGB565 over SPI --- src/ZeDMD.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 98c89c7..6e3cf92 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -898,7 +898,7 @@ void ZeDMD::RenderRgb565(uint16_t* pFrame) { if (m_verbose) m_pZeDMDComm->Log("ZeDMD::RenderRgb565"); - if (!(m_usb || m_wifi) || !UpdateFrameBuffer565(pFrame)) + if (!(m_usb || m_wifi || m_spi) || !UpdateFrameBuffer565(pFrame)) { return; } From 57d25f545c9c4fafdf89769703ff8512a5e73618 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 21 Dec 2025 12:54:07 +0100 Subject: [PATCH 23/42] configurable speed --- src/ZeDMD.cpp | 5 +++-- src/ZeDMD.h | 2 +- src/ZeDMDSpi.h | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 6e3cf92..7b8e179 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -4,8 +4,8 @@ #include "FrameUtil.h" #include "ZeDMDComm.h" -#include "ZeDMDWiFi.h" #include "ZeDMDSpi.h" +#include "ZeDMDWiFi.h" const int endian_check = 1; #define is_bigendian() ((*(char*)&endian_check) == 0) @@ -799,8 +799,9 @@ bool ZeDMD::Open(uint16_t width, uint16_t height) return m_usb; } -bool ZeDMD::OpenSpi(uint16_t width, uint16_t height) +bool ZeDMD::OpenSpi(uint32_t speed, uint16_t width, uint16_t height) { + m_pZeDMDSpi->SetSpeed(speed); m_spi = m_pZeDMDSpi->Connect(); if (m_spi && !m_usb && !m_wifi) diff --git a/src/ZeDMD.h b/src/ZeDMD.h index 3b29dc3..be1eb23 100644 --- a/src/ZeDMD.h +++ b/src/ZeDMD.h @@ -133,7 +133,7 @@ class ZEDMDAPI ZeDMD */ bool OpenDefaultWiFi(); - bool OpenSpi(uint16_t width, uint16_t height); + bool OpenSpi(uint32_t speed, uint16_t width, uint16_t height); /** @brief Close connection to ZeDMD * diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 62d8818..b7604ad 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -22,7 +22,6 @@ struct gpiod_line; // can be read from /sys/module/spidev/parameters/bufsiz, but hardcoded now for simplicity constexpr int spi_kernel_bufsize = 4096; -constexpr uint32_t spi_default_speed_hz = 64000000; class ZeDMDSpi : public ZeDMDComm { @@ -38,6 +37,7 @@ class ZeDMDSpi : public ZeDMDComm void Disconnect() override; bool IsConnected() override; + void SetSpeed(uint32_t speed) { m_speed = speed; } void SetWidth(uint16_t width) { m_width = width; } void SetHeight(uint16_t height) { m_height = height; } @@ -48,7 +48,7 @@ class ZeDMDSpi : public ZeDMDComm private: bool IsSupportedPlatform() const; - uint32_t m_speed = spi_default_speed_hz; + uint32_t m_speed = 24000000; // 24 MHz int m_fileDescriptor = -1; #if defined(SPI_SUPPORT) gpiod_chip* m_gpioChip = nullptr; From eadc9b32a6d5225fc815aee9061c926514dfcb9c Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 21 Dec 2025 12:59:34 +0100 Subject: [PATCH 24/42] removed log --- src/ZeDMDSpi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 5356d40..800d4b3 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -181,7 +181,7 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) if (m_csLine) gpiod_line_set_value(m_csLine, 1); return false; } - Log("ZeDMDSpi: send chunk of %d bytes", chunkSize); + // Log("ZeDMDSpi: send chunk of %d bytes", chunkSize); cursor += chunkSize; remaining -= chunkSize; } From 78a8ff76e4560d29e9eb76d02913a7601f324872 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 21 Dec 2025 14:28:32 +0100 Subject: [PATCH 25/42] reduced wait between frames --- src/ZeDMDSpi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 800d4b3..61ab0b0 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -192,7 +192,7 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) return false; } - std::this_thread::sleep_for(std::chrono::milliseconds(2)); + std::this_thread::sleep_for(std::chrono::microseconds(100)); return true; } From acf98a66026bcf4507741a6f73b47fa78362d436 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 21 Dec 2025 16:57:10 +0100 Subject: [PATCH 26/42] no keep alive --- src/ZeDMDSpi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index b7604ad..dc248aa 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -30,6 +30,7 @@ class ZeDMDSpi : public ZeDMDComm { m_compression = false; m_zoneStream = false; + m_keepAlive = false; } ~ZeDMDSpi() { Disconnect(); } From f3360d0a2d4085b3f09508bafa8a18e998085a67 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 21 Dec 2025 18:04:51 +0100 Subject: [PATCH 27/42] keep alive not supported on SPI --- src/ZeDMDComm.cpp | 2 +- src/ZeDMDComm.h | 1 + src/ZeDMDSpi.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index 6493dac..af0671b 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -1110,7 +1110,7 @@ void ZeDMDComm::KeepAlive() { auto now = std::chrono::steady_clock::now(); - if (!m_keepAlive) + if (!m_keepAlive || m_keepAliveNotSupported) { m_lastKeepAlive = now; return; diff --git a/src/ZeDMDComm.h b/src/ZeDMDComm.h index ccdc956..142b3ff 100644 --- a/src/ZeDMDComm.h +++ b/src/ZeDMDComm.h @@ -328,6 +328,7 @@ class ZeDMDComm std::mutex m_delayedFrameMutex; bool m_delayedFrameReady = false; bool m_keepAlive = true; + bool m_keepAliveNotSupported = false; std::chrono::steady_clock::time_point m_lastKeepAlive; bool m_autoDetect = true; diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index dc248aa..617f33e 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -31,6 +31,7 @@ class ZeDMDSpi : public ZeDMDComm m_compression = false; m_zoneStream = false; m_keepAlive = false; + m_keepAliveNotSupported = true; } ~ZeDMDSpi() { Disconnect(); } From b1482c877c9406bbf0cdd32559f385aeefb240bf Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 21 Dec 2025 18:16:27 +0100 Subject: [PATCH 28/42] fixed keep alive not supported --- src/ZeDMDComm.h | 2 +- src/ZeDMDSpi.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ZeDMDComm.h b/src/ZeDMDComm.h index 142b3ff..f4291e2 100644 --- a/src/ZeDMDComm.h +++ b/src/ZeDMDComm.h @@ -299,6 +299,7 @@ class ZeDMDComm bool m_compression = true; bool m_zoneStream = true; + bool m_keepAliveNotSupported = false; ZeDMD_DeviceType m_deviceType = ZeDMD_DeviceType::ESP32; @@ -328,7 +329,6 @@ class ZeDMDComm std::mutex m_delayedFrameMutex; bool m_delayedFrameReady = false; bool m_keepAlive = true; - bool m_keepAliveNotSupported = false; std::chrono::steady_clock::time_point m_lastKeepAlive; bool m_autoDetect = true; diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 617f33e..1cfe835 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -30,7 +30,6 @@ class ZeDMDSpi : public ZeDMDComm { m_compression = false; m_zoneStream = false; - m_keepAlive = false; m_keepAliveNotSupported = true; } ~ZeDMDSpi() { Disconnect(); } @@ -57,5 +56,4 @@ class ZeDMDSpi : public ZeDMDComm gpiod_line* m_csLine = nullptr; #endif bool m_connected = false; - bool m_keepAlive = false; }; From 1a79ba79b176abe7770e6bccbeee28440604c9f7 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 21 Dec 2025 21:32:11 +0100 Subject: [PATCH 29/42] fixed dimension getters --- src/ZeDMD.cpp | 60 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 7b8e179..eb36afc 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -112,20 +112,34 @@ void ZeDMD::SetFrameSize(uint16_t width, uint16_t height) uint16_t const ZeDMD::GetWidth() { - if (m_wifi) + if (m_usb) + { + return m_pZeDMDComm->GetWidth(); + } + else if (m_wifi) { return m_pZeDMDWiFi->GetWidth(); } - return m_pZeDMDComm->GetWidth(); + else if (m_spi) + { + return m_pZeDMDSpi->GetWidth(); + } } uint16_t const ZeDMD::GetHeight() { - if (m_wifi) + if (m_usb) + { + return m_pZeDMDComm->GetHeight(); + } + else if (m_wifi) { return m_pZeDMDWiFi->GetHeight(); } - return m_pZeDMDComm->GetHeight(); + else if (m_spi) + { + return m_pZeDMDSpi->GetHeight(); + } } uint16_t const ZeDMD::GetPanelWidth() { return GetWidth(); } @@ -134,13 +148,17 @@ uint16_t const ZeDMD::GetPanelHeight() { bool half = false; - if (m_wifi) + if (m_usb) { - half = m_pZeDMDWiFi->IsHalf(); + return m_pZeDMDComm->IsHalf(); } - else + else if (m_wifi) { - half = m_pZeDMDComm->IsHalf(); + return m_pZeDMDWiFi->IsHalf(); + } + else if (m_spi) + { + return m_pZeDMDSpi->IsHalf(); } return (half ? (GetHeight() * 2) : GetHeight()); @@ -148,11 +166,18 @@ uint16_t const ZeDMD::GetPanelHeight() bool const ZeDMD::IsS3() { + if (m_usb) + { + return m_pZeDMDComm->IsS3(); + } if (m_wifi) { return m_pZeDMDWiFi->IsS3(); } - return m_pZeDMDComm->IsS3(); + else if (m_spi) + { + return m_pZeDMDSpi->IsS3(); + } } const char* ZeDMD::GetVersion() { return ZEDMD_VERSION; } @@ -288,7 +313,11 @@ uint8_t ZeDMD::GetPanelI2sSpeed() { return m_pZeDMDComm->GetPanelI2sSpeed(); } - return m_pZeDMDWiFi->GetPanelI2sSpeed(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetPanelI2sSpeed(); + } + return 0; } uint8_t ZeDMD::GetPanelLatchBlanking() @@ -323,7 +352,11 @@ uint8_t ZeDMD::GetPanelDriver() { return m_pZeDMDComm->GetPanelDriver(); } - return m_pZeDMDWiFi->GetPanelDriver(); + else if (m_wifi) + { + return m_pZeDMDWiFi->GetPanelDriver(); + } + return 0; } uint8_t ZeDMD::GetTransport() @@ -802,6 +835,9 @@ bool ZeDMD::Open(uint16_t width, uint16_t height) bool ZeDMD::OpenSpi(uint32_t speed, uint16_t width, uint16_t height) { m_pZeDMDSpi->SetSpeed(speed); + m_pZeDMDSpi->SetWidth(width); + m_pZeDMDSpi->SetHeight(height); + m_spi = m_pZeDMDSpi->Connect(); if (m_spi && !m_usb && !m_wifi) @@ -812,8 +848,6 @@ bool ZeDMD::OpenSpi(uint32_t speed, uint16_t width, uint16_t height) m_pScaledFrameBuffer = (uint8_t*)malloc(ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); m_pRgb565Buffer = (uint8_t*)malloc(width * height * 2); - m_pZeDMDSpi->SetWidth(width); - m_pZeDMDSpi->SetHeight(height); m_pZeDMDSpi->Run(); } From bc2e9469f1cd5a5b83abc739cf4d3caf678c11af Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Thu, 25 Dec 2025 14:48:38 +0100 Subject: [PATCH 30/42] dynamically read spi buffer size --- src/ZeDMDSpi.cpp | 22 ++++++++++++++++++++++ src/ZeDMDSpi.h | 9 +++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 61ab0b0..1179e73 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -19,6 +19,27 @@ constexpr uint8_t kSpiBitsPerWord = 8; constexpr uint8_t kSpiMode = SPI_MODE_0; constexpr unsigned int kCsGpio = 25; // GPIO25 on Raspberry Pi constexpr const char kGpioConsumer[] = "ZeDMDSpi"; +constexpr const char kSpiBufSizePath[] = "/sys/module/spidev/parameters/bufsiz"; + +uint32_t GetSpiKernelBufSize() +{ + static uint32_t cachedBufSize = 0; + if (cachedBufSize != 0) + { + return cachedBufSize; + } + + std::ifstream bufFile(kSpiBufSizePath); + uint32_t parsed = 0; + if (bufFile.is_open() && (bufFile >> parsed) && parsed > 0) + { + cachedBufSize = parsed; + return cachedBufSize; + } + + cachedBufSize = 4096; // fallback if sysfs entry is unavailable + return cachedBufSize; +} } // namespace bool ZeDMDSpi::IsSupportedPlatform() const @@ -163,6 +184,7 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) } std::this_thread::sleep_for(std::chrono::microseconds(10)); + const uint32_t spi_kernel_bufsize = GetSpiKernelBufSize(); while (remaining > 0) { diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 1cfe835..78024b6 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -11,18 +11,15 @@ #if defined(SPI_SUPPORT) #include #include + +#define GPIO_CHIP "/dev/gpiochip0" +#define SPI_DEVICE "/dev/spidev1.0" #else // Forward declarations so non-Linux builds can compile the stub implementation. struct gpiod_chip; struct gpiod_line; #endif -#define GPIO_CHIP "/dev/gpiochip0" -#define SPI_DEVICE "/dev/spidev1.0" - -// can be read from /sys/module/spidev/parameters/bufsiz, but hardcoded now for simplicity -constexpr int spi_kernel_bufsize = 4096; - class ZeDMDSpi : public ZeDMDComm { public: From f700097904689fe89d88407b151aa605e7b5951c Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sat, 27 Dec 2025 20:22:05 +0100 Subject: [PATCH 31/42] removed 12ms threshold for HD --- src/ZeDMD.cpp | 12 ------------ src/ZeDMDWiFi.cpp | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index eb36afc..1fb08ad 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -956,18 +956,6 @@ void ZeDMD::RenderRgb565(uint16_t* pFrame) bool ZeDMD::UpdateFrameBuffer888(uint8_t* pFrame) { - static auto lastExecutionTime = std::chrono::steady_clock::now() - std::chrono::milliseconds(100); - - auto currentTime = std::chrono::steady_clock::now(); - - // 12ms means 83Hz. Drop frames of that and higher frame rates because ZeDMD HD can't handle them. - if (std::chrono::duration_cast(currentTime - lastExecutionTime).count() < 12) - { - return false; - } - - lastExecutionTime = currentTime; - if (0 == memcmp(m_pFrameBuffer, pFrame, m_romWidth * m_romHeight * 3)) { return false; diff --git a/src/ZeDMDWiFi.cpp b/src/ZeDMDWiFi.cpp index a1efa7d..497d461 100644 --- a/src/ZeDMDWiFi.cpp +++ b/src/ZeDMDWiFi.cpp @@ -272,7 +272,7 @@ bool ZeDMDWiFi::DoConnect(const char* ip) } else { - Log("ZeDMD UDP delay could not be detected, falling back to 5ms deafult"); + Log("ZeDMD UDP delay could not be detected, falling back to 5ms default"); } } From b18a3cee2d936c47ca022d4313a4f2a5ba5dd4c5 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 28 Dec 2025 13:37:59 +0100 Subject: [PATCH 32/42] Don't send commands via SPI, more logging --- src/ZeDMDComm.h | 4 ++-- src/ZeDMDSpi.cpp | 33 ++++++++++++++++++++++++++++++--- src/ZeDMDSpi.h | 1 + 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/ZeDMDComm.h b/src/ZeDMDComm.h index f4291e2..fc59ad9 100644 --- a/src/ZeDMDComm.h +++ b/src/ZeDMDComm.h @@ -234,7 +234,7 @@ class ZeDMDComm void Flush(bool reenableKeepAive = true); void QueueFrame(uint8_t* buffer, int size); void QueueFrame(uint8_t* buffer, int size, bool rgb888); - void QueueCommand(char command, uint8_t* buffer, int size); + virtual void QueueCommand(char command, uint8_t* buffer, int size); void QueueCommand(char command); void QueueCommand(char command, uint8_t value); bool FillDelayed(); @@ -294,6 +294,7 @@ class ZeDMDComm uint8_t m_panelMinRefreshRate = 30; uint8_t m_udpDelay = 5; uint16_t m_writeAtOnce = ZEDMD_COMM_DEFAULT_SERIAL_WRITE_AT_ONCE; + const uint8_t m_allBlack[32768] = {0}; uint8_t m_currentCommand = 0; @@ -312,7 +313,6 @@ class ZeDMDComm ZeDMD_LogCallback m_logCallback = nullptr; const void* m_logUserData = nullptr; uint64_t m_zoneHashes[128] = {0}; - const uint8_t m_allBlack[32768] = {0}; char m_ignoredDevices[10][32] = {0}; uint8_t m_ignoredDevicesCounter = 0; diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 1179e73..7ffd527 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -166,6 +166,21 @@ bool ZeDMDSpi::IsConnected() { return m_connected; } void ZeDMDSpi::Reset() {} +void ZeDMDSpi::QueueCommand(char command, uint8_t* buffer, int size) +{ + switch (command) + { + case ZEDMD_COMM_COMMAND::ClearScreen: + SendChunks(m_allBlack, getWidth() * getHeight() * 2); // RGB565 + break; + + default: + // ZeDMDComm::QueueCommand(command, buffer, size); + // Drop commands other than ClearScreen for SPI transport for now. + break; + } +} + bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) { if (!m_connected || m_fileDescriptor < 0) @@ -203,9 +218,19 @@ bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) if (m_csLine) gpiod_line_set_value(m_csLine, 1); return false; } - // Log("ZeDMDSpi: send chunk of %d bytes", chunkSize); - cursor += chunkSize; - remaining -= chunkSize; + const uint32_t bytesTransferred = static_cast(res); + if (bytesTransferred != chunkSize) + { + Log("ZeDMDSpi: partial SPI write (%u/%u bytes)", bytesTransferred, chunkSize); + if (bytesTransferred == 0) + { + if (m_csLine) gpiod_line_set_value(m_csLine, 1); + return false; + } + } + + cursor += bytesTransferred; + remaining -= bytesTransferred; } if (m_csLine && gpiod_line_set_value(m_csLine, 1) < 0) @@ -235,6 +260,8 @@ bool ZeDMDSpi::IsConnected() { return false; } void ZeDMDSpi::Reset() {} +void ZeDMDSpi::QueueCommand(char command, uint8_t* buffer, int size) { ZeDMDComm::QueueCommand(command, buffer, size); } + bool ZeDMDSpi::SendChunks(uint8_t*, uint16_t) { return false; } #endif // __linux__ diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 78024b6..4058ce6 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -34,6 +34,7 @@ class ZeDMDSpi : public ZeDMDComm bool Connect() override; void Disconnect() override; bool IsConnected() override; + void QueueCommand(char command, uint8_t* buffer, int size) override; void SetSpeed(uint32_t speed) { m_speed = speed; } void SetWidth(uint16_t width) { m_width = width; } From 65c5eb912bd955a398c983d3c4b6e28142a38a5a Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 28 Dec 2025 14:54:24 +0100 Subject: [PATCH 33/42] fixed getters --- src/ZeDMDSpi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 7ffd527..d658639 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -171,7 +171,7 @@ void ZeDMDSpi::QueueCommand(char command, uint8_t* buffer, int size) switch (command) { case ZEDMD_COMM_COMMAND::ClearScreen: - SendChunks(m_allBlack, getWidth() * getHeight() * 2); // RGB565 + SendChunks(m_allBlack, m_width * m_height * 2); // RGB565 break; default: From cc7a2748bbb6ad2d93097801a8a8d1a9900bad5b Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 28 Dec 2025 14:57:57 +0100 Subject: [PATCH 34/42] firxed overloading --- src/ZeDMDSpi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 4058ce6..839e5bc 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -34,6 +34,7 @@ class ZeDMDSpi : public ZeDMDComm bool Connect() override; void Disconnect() override; bool IsConnected() override; + using ZeDMDComm::QueueCommand; void QueueCommand(char command, uint8_t* buffer, int size) override; void SetSpeed(uint32_t speed) { m_speed = speed; } From 0906c3bfe101b6e68a57e922b65ced7161e396b1 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 28 Dec 2025 15:06:10 +0100 Subject: [PATCH 35/42] fixed some warnings --- src/ZeDMD.cpp | 12 +++++++++--- src/ZeDMDComm.h | 2 +- src/ZeDMDSpi.cpp | 6 +++--- src/ZeDMDSpi.h | 2 +- src/ZeDMDWiFi.cpp | 2 +- src/ZeDMDWiFi.h | 2 +- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 1fb08ad..b1171a5 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -124,6 +124,8 @@ uint16_t const ZeDMD::GetWidth() { return m_pZeDMDSpi->GetWidth(); } + + return 0; } uint16_t const ZeDMD::GetHeight() @@ -140,6 +142,8 @@ uint16_t const ZeDMD::GetHeight() { return m_pZeDMDSpi->GetHeight(); } + + return 0; } uint16_t const ZeDMD::GetPanelWidth() { return GetWidth(); } @@ -150,15 +154,15 @@ uint16_t const ZeDMD::GetPanelHeight() if (m_usb) { - return m_pZeDMDComm->IsHalf(); + half = m_pZeDMDComm->IsHalf(); } else if (m_wifi) { - return m_pZeDMDWiFi->IsHalf(); + half = m_pZeDMDWiFi->IsHalf(); } else if (m_spi) { - return m_pZeDMDSpi->IsHalf(); + half = m_pZeDMDSpi->IsHalf(); } return (half ? (GetHeight() * 2) : GetHeight()); @@ -178,6 +182,8 @@ bool const ZeDMD::IsS3() { return m_pZeDMDSpi->IsS3(); } + + return false; } const char* ZeDMD::GetVersion() { return ZEDMD_VERSION; } diff --git a/src/ZeDMDComm.h b/src/ZeDMDComm.h index fc59ad9..b9d0d1f 100644 --- a/src/ZeDMDComm.h +++ b/src/ZeDMDComm.h @@ -265,7 +265,7 @@ class ZeDMDComm void Log(const char* format, ...); protected: - virtual bool SendChunks(uint8_t* pData, uint16_t size); + virtual bool SendChunks(const uint8_t* pData, uint16_t size); virtual void Reset(); void ClearFrames(); bool IsQueueEmpty(); diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index d658639..6ad9c1f 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -171,7 +171,7 @@ void ZeDMDSpi::QueueCommand(char command, uint8_t* buffer, int size) switch (command) { case ZEDMD_COMM_COMMAND::ClearScreen: - SendChunks(m_allBlack, m_width * m_height * 2); // RGB565 + SendChunks(m_allBlack, GetWidth() * GetHeight() * 2); // RGB565 break; default: @@ -181,7 +181,7 @@ void ZeDMDSpi::QueueCommand(char command, uint8_t* buffer, int size) } } -bool ZeDMDSpi::SendChunks(uint8_t* pData, uint16_t size) +bool ZeDMDSpi::SendChunks(const uint8_t* pData, uint16_t size) { if (!m_connected || m_fileDescriptor < 0) { @@ -262,6 +262,6 @@ void ZeDMDSpi::Reset() {} void ZeDMDSpi::QueueCommand(char command, uint8_t* buffer, int size) { ZeDMDComm::QueueCommand(command, buffer, size); } -bool ZeDMDSpi::SendChunks(uint8_t*, uint16_t) { return false; } +bool ZeDMDSpi::SendChunks(const uint8_t*, uint16_t) { return false; } #endif // __linux__ diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index 839e5bc..cf6388b 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -42,7 +42,7 @@ class ZeDMDSpi : public ZeDMDComm void SetHeight(uint16_t height) { m_height = height; } protected: - bool SendChunks(uint8_t* pData, uint16_t size) override; + bool SendChunks(const uint8_t* pData, uint16_t size) override; void Reset() override; private: diff --git a/src/ZeDMDWiFi.cpp b/src/ZeDMDWiFi.cpp index 497d461..3cee0b5 100644 --- a/src/ZeDMDWiFi.cpp +++ b/src/ZeDMDWiFi.cpp @@ -617,7 +617,7 @@ bool ZeDMDWiFi::IsConnected() { return m_connected; } void ZeDMDWiFi::Reset() {} -bool ZeDMDWiFi::SendChunks(uint8_t* pData, uint16_t size) +bool ZeDMDWiFi::SendChunks(const uint8_t* pData, uint16_t size) { if (m_tcp) { diff --git a/src/ZeDMDWiFi.h b/src/ZeDMDWiFi.h index c9e3cbc..64de3cb 100644 --- a/src/ZeDMDWiFi.h +++ b/src/ZeDMDWiFi.h @@ -34,7 +34,7 @@ class ZeDMDWiFi : public ZeDMDComm protected: bool DoConnect(const char* ip); - virtual bool SendChunks(uint8_t* pData, uint16_t size); + virtual bool SendChunks(const uint8_t* pData, uint16_t size); virtual void Reset(); bool openTcpConnection(); bool SendGetRequest(const std::string& path); From 30a065b602f275b3389889a98943d8ad46ebfc0e Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 28 Dec 2025 15:08:24 +0100 Subject: [PATCH 36/42] ifirxed error --- src/ZeDMDComm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index af0671b..2dd6210 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -977,7 +977,7 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) return true; } -bool ZeDMDComm::SendChunks(uint8_t* pData, uint16_t size) +bool ZeDMDComm::SendChunks(const uint8_t* pData, uint16_t size) { #if !( \ (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ From cf1afc7b4ff87a1c3bb45094afc578741fc955b2 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 28 Dec 2025 15:12:21 +0100 Subject: [PATCH 37/42] fixed pointer --- src/ZeDMDSpi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 6ad9c1f..9fafad6 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -190,7 +190,7 @@ bool ZeDMDSpi::SendChunks(const uint8_t* pData, uint16_t size) } uint32_t remaining = size; - uint8_t* cursor = pData; + uint8_t* cursor = (uint8_t*)pData; if (m_csLine && gpiod_line_set_value(m_csLine, 0) < 0) { From bb64fb22296525af7d809980ce3620f4038b76f2 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 28 Dec 2025 16:37:36 +0100 Subject: [PATCH 38/42] more logging --- src/ZeDMDComm.cpp | 10 ++++++---- src/ZeDMDSpi.cpp | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index 2dd6210..88a203b 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -883,9 +883,13 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) for (auto it = pFrame->data.rbegin(); it != pFrame->data.rend(); ++it) { ZeDMDFrameData frameData = *it; - Log("StreamBytes, command %02X, length %d", pFrame->command, frameData.size); + if (m_verbose) Log("StreamBytes, command %02X, length %d", pFrame->command, frameData.size); - if (!SendChunks(frameData.data, frameData.size)) return false; + if (!SendChunks(frameData.data, frameData.size)) + { + Log("StreamBytes failed"); + return false; + } m_lastKeepAlive = std::chrono::steady_clock::now(); } @@ -895,8 +899,6 @@ bool ZeDMDComm::StreamBytes(ZeDMDFrame* pFrame) return true; } - Log("StreamBytes, command %02X, length %d", pFrame->command); - // 256*64*3 (RGB888) = 49152 + headers static uint8_t payload[50176] = {0}; memset(payload, 0, 50176); diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 9fafad6..073e327 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -216,6 +216,7 @@ bool ZeDMDSpi::SendChunks(const uint8_t* pData, uint16_t size) { Log("ZeDMDSpi: SPI write failed: %s", strerror(errno)); if (m_csLine) gpiod_line_set_value(m_csLine, 1); + std::this_thread::sleep_for(std::chrono::microseconds(100)); return false; } const uint32_t bytesTransferred = static_cast(res); @@ -225,12 +226,15 @@ bool ZeDMDSpi::SendChunks(const uint8_t* pData, uint16_t size) if (bytesTransferred == 0) { if (m_csLine) gpiod_line_set_value(m_csLine, 1); + std::this_thread::sleep_for(std::chrono::microseconds(100)); return false; } } cursor += bytesTransferred; remaining -= bytesTransferred; + + if (m_verbose) Log("SendChunks, transferred %d, remaining %d", bytesTransferred, remaining); } if (m_csLine && gpiod_line_set_value(m_csLine, 1) < 0) From fd639f5708fdf269ea007c5504d442f05f92aaae Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Sun, 28 Dec 2025 17:14:20 +0100 Subject: [PATCH 39/42] use ZeDMD pointer --- src/ZeDMD.cpp | 623 +++++++++++++++++----------------------------- src/ZeDMD.h | 5 + src/ZeDMDComm.cpp | 7 +- 3 files changed, 237 insertions(+), 398 deletions(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index b1171a5..876116a 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -46,6 +46,34 @@ ZeDMD::~ZeDMD() } } +void ZeDMD::SetActiveZeDMD(ZeDMDComm* pActive, bool usb, bool wifi, bool spi) +{ + m_pActiveZeDMD = pActive; + m_usb = usb; + m_wifi = wifi; + m_spi = spi; +} + +ZeDMDComm* ZeDMD::GetActiveZeDMD() const { return m_pActiveZeDMD; } + +ZeDMDWiFi* ZeDMD::GetActiveZeDMDWiFi() const +{ + if (m_wifi) + { + return m_pZeDMDWiFi; + } + return nullptr; +} + +ZeDMDSpi* ZeDMD::GetActiveZeDMDSpi() const +{ + if (m_spi) + { + return m_pZeDMDSpi; + } + return nullptr; +} + void ZeDMD::SetLogCallback(ZeDMD_LogCallback callback, const void* userData) { m_pZeDMDComm->SetLogCallback(callback, userData); @@ -55,48 +83,54 @@ void ZeDMD::SetLogCallback(ZeDMD_LogCallback callback, const void* userData) void ZeDMD::Close() { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::Close"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::Close"); + + if (!pActive) + { + return; + } if (m_usb) { - m_pZeDMDComm->SoftReset(false); - m_pZeDMDComm->Disconnect(); + pActive->SoftReset(false); + pActive->Disconnect(); } - else if (m_wifi) + else if (ZeDMDWiFi* pWiFi = GetActiveZeDMDWiFi()) { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); - m_pZeDMDWiFi->Flush(false); - m_pZeDMDWiFi->Disconnect(); + pWiFi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); + pWiFi->Flush(false); + pWiFi->Disconnect(); } - else if (m_spi) + else if (ZeDMDSpi* pSpi = GetActiveZeDMDSpi()) { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); - m_pZeDMDSpi->Flush(false); - m_pZeDMDSpi->Disconnect(); + pSpi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); + pSpi->Flush(false); + pSpi->Disconnect(); } + + SetActiveZeDMD(nullptr, false, false, false); } void ZeDMD::Reset() { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::Reset"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::Reset"); - if (m_usb) + if (pActive && !m_spi) { - m_pZeDMDComm->SoftReset(); - } - else if (m_wifi) - { - m_pZeDMDWiFi->SoftReset(); + pActive->SoftReset(); } } void ZeDMD::RebootToBootloader() { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::RebootToBootloader"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::RebootToBootloader"); - if (m_usb) + if (m_usb && pActive) { - m_pZeDMDComm->RebootToBootloader(); + pActive->RebootToBootloader(); } } @@ -112,17 +146,10 @@ void ZeDMD::SetFrameSize(uint16_t width, uint16_t height) uint16_t const ZeDMD::GetWidth() { - if (m_usb) - { - return m_pZeDMDComm->GetWidth(); - } - else if (m_wifi) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive) { - return m_pZeDMDWiFi->GetWidth(); - } - else if (m_spi) - { - return m_pZeDMDSpi->GetWidth(); + return pActive->GetWidth(); } return 0; @@ -130,17 +157,10 @@ uint16_t const ZeDMD::GetWidth() uint16_t const ZeDMD::GetHeight() { - if (m_usb) - { - return m_pZeDMDComm->GetHeight(); - } - else if (m_wifi) - { - return m_pZeDMDWiFi->GetHeight(); - } - else if (m_spi) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive) { - return m_pZeDMDSpi->GetHeight(); + return pActive->GetHeight(); } return 0; @@ -152,17 +172,9 @@ uint16_t const ZeDMD::GetPanelHeight() { bool half = false; - if (m_usb) - { - half = m_pZeDMDComm->IsHalf(); - } - else if (m_wifi) + if (ZeDMDComm* pActive = GetActiveZeDMD()) { - half = m_pZeDMDWiFi->IsHalf(); - } - else if (m_spi) - { - half = m_pZeDMDSpi->IsHalf(); + half = pActive->IsHalf(); } return (half ? (GetHeight() * 2) : GetHeight()); @@ -170,17 +182,9 @@ uint16_t const ZeDMD::GetPanelHeight() bool const ZeDMD::IsS3() { - if (m_usb) + if (ZeDMDComm* pActive = GetActiveZeDMD()) { - return m_pZeDMDComm->IsS3(); - } - if (m_wifi) - { - return m_pZeDMDWiFi->IsS3(); - } - else if (m_spi) - { - return m_pZeDMDSpi->IsS3(); + return pActive->IsS3(); } return false; @@ -190,26 +194,20 @@ const char* ZeDMD::GetVersion() { return ZEDMD_VERSION; } const char* ZeDMD::GetFirmwareVersion() { - if (m_usb) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDComm->GetFirmwareVersion(); - } - else if (m_wifi) - { - return m_pZeDMDWiFi->GetFirmwareVersion(); + return pActive->GetFirmwareVersion(); } return "SPI"; } uint16_t const ZeDMD::GetId() { - if (m_usb) - { - return m_pZeDMDComm->GetId(); - } - else if (m_wifi) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDWiFi->GetId(); + return pActive->GetId(); } return 0; } @@ -223,258 +221,201 @@ const char* ZeDMD::GetIdString() const char* ZeDMD::GetWiFiSSID() { - if (m_wifi) + if (ZeDMDWiFi* pWiFi = GetActiveZeDMDWiFi()) { - return m_pZeDMDWiFi->GetWiFiSSID(); + return pWiFi->GetWiFiSSID(); } return ""; } const char* ZeDMD::GetIp() { - if (m_wifi) + if (ZeDMDWiFi* pWiFi = GetActiveZeDMDWiFi()) { - return m_pZeDMDWiFi->GetIp(); + return pWiFi->GetIp(); } return ""; } const char* ZeDMD::GetDevice() { - if (m_usb) + if (m_usb && GetActiveZeDMD()) { - return m_pZeDMDComm->GetDevice(); + return GetActiveZeDMD()->GetDevice(); } return ""; } int ZeDMD::GetWiFiPort() { - if (m_wifi) + if (ZeDMDWiFi* pWiFi = GetActiveZeDMDWiFi()) { - return m_pZeDMDWiFi->GetWiFiPort(); + return pWiFi->GetWiFiPort(); } return 0; } uint8_t ZeDMD::GetWiFiPower() { - if (m_wifi) + if (ZeDMDWiFi* pWiFi = GetActiveZeDMDWiFi()) { - return m_pZeDMDWiFi->GetWiFiPower(); + return pWiFi->GetWiFiPower(); } return 0; } void ZeDMD::StoreWiFiPassword() { - if (m_wifi) + if (ZeDMDWiFi* pWiFi = GetActiveZeDMDWiFi()) { - return m_pZeDMDWiFi->StoreWiFiPassword(); + return pWiFi->StoreWiFiPassword(); } } uint8_t ZeDMD::GetRGBOrder() { - if (m_usb) - { - return m_pZeDMDComm->GetRGBOrder(); - } - else if (m_wifi) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDWiFi->GetRGBOrder(); + return pActive->GetRGBOrder(); } return 0; } uint8_t ZeDMD::GetBrightness() { - if (m_usb) - { - return m_pZeDMDComm->GetBrightness(); - } - else if (m_wifi) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDWiFi->GetBrightness(); + return pActive->GetBrightness(); } return 0; } uint8_t ZeDMD::GetPanelClockPhase() { - if (m_usb) - { - return m_pZeDMDComm->GetPanelClockPhase(); - } - else if (m_wifi) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDWiFi->GetPanelClockPhase(); + return pActive->GetPanelClockPhase(); } return 0; } uint8_t ZeDMD::GetPanelI2sSpeed() { - if (m_usb) - { - return m_pZeDMDComm->GetPanelI2sSpeed(); - } - else if (m_wifi) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDWiFi->GetPanelI2sSpeed(); + return pActive->GetPanelI2sSpeed(); } return 0; } uint8_t ZeDMD::GetPanelLatchBlanking() { - if (m_usb) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDComm->GetPanelLatchBlanking(); - } - else if (m_wifi) - { - return m_pZeDMDWiFi->GetPanelLatchBlanking(); + return pActive->GetPanelLatchBlanking(); } return 0; } uint8_t ZeDMD::GetPanelMinRefreshRate() { - if (m_usb) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDComm->GetPanelMinRefreshRate(); - } - else if (m_wifi) - { - return m_pZeDMDWiFi->GetPanelMinRefreshRate(); + return pActive->GetPanelMinRefreshRate(); } return 0; } uint8_t ZeDMD::GetPanelDriver() { - if (m_usb) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDComm->GetPanelDriver(); - } - else if (m_wifi) - { - return m_pZeDMDWiFi->GetPanelDriver(); + return pActive->GetPanelDriver(); } return 0; } uint8_t ZeDMD::GetTransport() { - if (m_usb) + if (ZeDMDComm* pActive = GetActiveZeDMD()) { - return m_pZeDMDComm->GetTransport(); - } - else - { - return m_pZeDMDWiFi->GetTransport(); + return pActive->GetTransport(); } return 0; } uint8_t ZeDMD::GetUdpDelay() { - if (m_usb) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDComm->GetUdpDelay(); - } - else if (m_wifi) - { - return m_pZeDMDWiFi->GetUdpDelay(); + return pActive->GetUdpDelay(); } return 0; } uint16_t ZeDMD::GetUsbPackageSize() { - if (m_usb) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDComm->GetUsbPackageSize(); - } - else if (m_wifi) - { - return m_pZeDMDWiFi->GetUsbPackageSize(); + return pActive->GetUsbPackageSize(); } return 0; } uint8_t ZeDMD::GetYOffset() { - if (m_usb) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (pActive && !m_spi) { - return m_pZeDMDComm->GetYOffset(); - } - else if (m_wifi) - { - return m_pZeDMDWiFi->GetYOffset(); + return pActive->GetYOffset(); } return 0; } void ZeDMD::LedTest() { - if (m_usb) + ZeDMDComm* pActive = GetActiveZeDMD(); + if (!pActive) { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::LEDTest); - m_pZeDMDComm->DisableKeepAlive(); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::LEDTest); - m_pZeDMDWiFi->DisableKeepAlive(); + return; } - else if (m_spi) + + pActive->QueueCommand(ZEDMD_COMM_COMMAND::LEDTest); + if (!m_spi) { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::LEDTest); + pActive->DisableKeepAlive(); } std::this_thread::sleep_for(std::chrono::milliseconds(8000)); - if (m_usb) - { - m_pZeDMDComm->EnableKeepAlive(); - } - else if (m_wifi) + if (!m_spi) { - m_pZeDMDWiFi->EnableKeepAlive(); + pActive->EnableKeepAlive(); } } void ZeDMD::EnableDebug() { - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::EnableDebug); - } - else if (m_wifi) + if (ZeDMDComm* pActive = GetActiveZeDMD()) { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::EnableDebug); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::EnableDebug); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::EnableDebug); } } void ZeDMD::DisableDebug() { - if (m_usb) + if (ZeDMDComm* pActive = GetActiveZeDMD()) { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); } } @@ -496,211 +437,152 @@ void ZeDMD::DisableVerbose() void ZeDMD::SetRGBOrder(uint8_t rgbOrder) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetRGBOrder %d", rgbOrder); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetRGBOrder %d", rgbOrder); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::RGBOrder, rgbOrder); - } - else if (m_wifi) + if (pActive) { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::RGBOrder, rgbOrder); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::RGBOrder, rgbOrder); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::RGBOrder, rgbOrder); } } void ZeDMD::SetBrightness(uint8_t brightness) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetBrightness %d", brightness); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetBrightness %d", brightness); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::Brightness, brightness); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::Brightness, brightness); - } - else if (m_spi) + if (pActive) { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::Brightness, brightness); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::Brightness, brightness); } } void ZeDMD::SetPanelClockPhase(uint8_t clockPhase) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetPanelClockPhase %d", clockPhase); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetPanelClockPhase %d", clockPhase); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetClkphase, clockPhase); - } - else if (m_wifi) + if (pActive) { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetClkphase, clockPhase); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetClkphase, clockPhase); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetClkphase, clockPhase); } } void ZeDMD::SetPanelI2sSpeed(uint8_t i2sSpeed) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetPanelI2sSpeed %d", i2sSpeed); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetPanelI2sSpeed %d", i2sSpeed); - if (m_usb) + if (pActive) { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetI2sspeed, i2sSpeed); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetI2sspeed, i2sSpeed); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetI2sspeed, i2sSpeed); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetI2sspeed, i2sSpeed); } } void ZeDMD::SetPanelLatchBlanking(uint8_t latchBlanking) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetPanelLatchBlanking %d", latchBlanking); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetPanelLatchBlanking %d", latchBlanking); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetLatchBlanking, latchBlanking); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetLatchBlanking, latchBlanking); - } - else if (m_spi) + if (pActive) { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetLatchBlanking, latchBlanking); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetLatchBlanking, latchBlanking); } } void ZeDMD::SetPanelMinRefreshRate(uint8_t minRefreshRate) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetPanelMinRefreshRate %d", minRefreshRate); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetPanelMinRefreshRate %d", minRefreshRate); - if (m_usb) + if (pActive) { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetMinRefreshRate, minRefreshRate); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetMinRefreshRate, minRefreshRate); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetMinRefreshRate, minRefreshRate); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetMinRefreshRate, minRefreshRate); } } void ZeDMD::SetPanelDriver(uint8_t driver) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetPanelDriver %d", driver); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetPanelDriver %d", driver); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetDriver, driver); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetDriver, driver); - } - else if (m_spi) + if (pActive) { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::SetDriver, driver); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetDriver, driver); } } void ZeDMD::SetTransport(uint8_t transport) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetTransport %d", transport); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetTransport %d", transport); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetTransport, transport); - } - else if (m_wifi) + if (pActive && !m_spi) { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetTransport, transport); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetTransport, transport); } } void ZeDMD::SetUdpDelay(uint8_t udpDelay) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetUdpDelay %d", udpDelay); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetUdpDelay %d", udpDelay); - if (m_usb) + if (pActive && !m_spi) { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetUdpDelay, udpDelay); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetUdpDelay, udpDelay); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetUdpDelay, udpDelay); } } void ZeDMD::SetUsbPackageSize(uint16_t usbPackageSize) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetUsbPackageSize %d", usbPackageSize); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetUsbPackageSize %d", usbPackageSize); uint8_t multiplier = (uint8_t)(usbPackageSize / 32); - if (m_usb) + if (pActive && !m_spi) { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetUsbPackageSizeMultiplier, multiplier); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetUsbPackageSizeMultiplier, multiplier); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetUsbPackageSizeMultiplier, multiplier); } } void ZeDMD::SetYOffset(uint8_t yOffset) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetYOffset %d", yOffset); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetYOffset %d", yOffset); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetYOffset, yOffset); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetYOffset, yOffset); - } - else if (m_spi) + if (pActive) { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); + if (m_spi) + { + pActive->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); + } + else + { + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetYOffset, yOffset); + } } } void ZeDMD::SaveSettings() { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SaveSettings"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SaveSettings"); - if (m_usb) + if (!pActive) { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SaveSettings); - // Avoid that client resets the device before settings are saved. - m_pZeDMDComm->Flush(); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SaveSettings); - // Avoid that client resets the device before settings are saved. - m_pZeDMDWiFi->Flush(); + return; } - else if (m_spi) + + if (m_spi) { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::DisableDebug); + return; } + + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SaveSettings); + // Avoid that client resets the device before settings are saved. + pActive->Flush(); } void ZeDMD::EnableUpscaling() @@ -713,74 +595,56 @@ void ZeDMD::DisableUpscaling() { m_upscaling = false; } void ZeDMD::SetWiFiSSID(const char* const ssid) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetWiFiSSID"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetWiFiSSID"); int size = strlen(ssid); - if (size <= 32) + if (size <= 32 && pActive && !m_spi) { uint8_t data[32] = {0}; memcpy(data, (uint8_t*)ssid, size); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiSSID, data, size); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiSSID, data, size); - } + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiSSID, data, size); } } void ZeDMD::SetWiFiPassword(const char* const password) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetWiFiPassword"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetWiFiPassword"); int size = strlen(password); - if (size <= 32) + if (size <= 32 && pActive && !m_spi) { uint8_t data[32] = {0}; memcpy(data, (uint8_t*)password, size); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPassword, data, size); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPassword, data, size); - } + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPassword, data, size); } } void ZeDMD::SetWiFiPort(int port) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetWiFiPort %d", port); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetWiFiPort %d", port); uint8_t data[2]; data[0] = (uint8_t)(port >> 8 & 0xFF); data[1] = (uint8_t)(port & 0xFF); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPort, data, 2); - } - else if (m_wifi) + if (pActive && !m_spi) { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPort, data, 2); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPort, data, 2); } } void ZeDMD::SetWiFiPower(uint8_t power) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::SetWiFiPower %d", power); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::SetWiFiPower %d", power); uint8_t data[1]; data[0] = power; - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPower, data, 1); - } - else if (m_wifi) + if (pActive && !m_spi) { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPower, data, 1); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::SetWiFiPower, data, 1); } } @@ -790,8 +654,9 @@ bool ZeDMD::OpenWiFi(const char* ip) m_wifi = m_pZeDMDWiFi->Connect(ip); - if (m_wifi && !m_usb) + if (m_wifi) { + SetActiveZeDMD(m_pZeDMDWiFi, false, true, false); uint16_t width = m_pZeDMDWiFi->GetWidth(); uint16_t height = m_pZeDMDWiFi->GetHeight(); m_hd = (width == 256); @@ -812,8 +677,9 @@ bool ZeDMD::Open() { m_usb = m_pZeDMDComm->Connect(); - if (m_usb && !m_wifi) + if (m_usb) { + SetActiveZeDMD(m_pZeDMDComm, true, false, false); uint16_t width = m_pZeDMDComm->GetWidth(); uint16_t height = m_pZeDMDComm->GetHeight(); m_hd = (width == 256); @@ -846,8 +712,9 @@ bool ZeDMD::OpenSpi(uint32_t speed, uint16_t width, uint16_t height) m_spi = m_pZeDMDSpi->Connect(); - if (m_spi && !m_usb && !m_wifi) + if (m_spi) { + SetActiveZeDMD(m_pZeDMDSpi, false, false, true); m_hd = (width == 256); m_pFrameBuffer = (uint8_t*)malloc(ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); @@ -862,19 +729,12 @@ bool ZeDMD::OpenSpi(uint32_t speed, uint16_t width, uint16_t height) void ZeDMD::ClearScreen() { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::ClearScreen"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::ClearScreen"); - if (m_usb) - { - m_pZeDMDComm->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); - } - else if (m_wifi) - { - m_pZeDMDWiFi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); - } - else if (m_spi) + if (pActive) { - m_pZeDMDSpi->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); + pActive->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); } // "Blank" the frame buffer. memset(m_pFrameBuffer, 0, ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); @@ -884,9 +744,10 @@ void ZeDMD::EnableTrueRgb888(bool enable) { m_rgb888 = enable; } void ZeDMD::RenderRgb888(uint8_t* pFrame) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::RenderRgb888"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::RenderRgb888"); - if (!(m_usb || m_wifi || m_spi) || !UpdateFrameBuffer888(pFrame)) + if (!pActive || !UpdateFrameBuffer888(pFrame)) { return; } @@ -895,18 +756,7 @@ void ZeDMD::RenderRgb888(uint8_t* pFrame) if (m_rgb888) { - if (m_wifi) - { - m_pZeDMDWiFi->QueueFrame(m_pScaledFrameBuffer, bufferSize, true); - } - else if (m_usb) - { - m_pZeDMDComm->QueueFrame(m_pScaledFrameBuffer, bufferSize, true); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueFrame(m_pScaledFrameBuffer, bufferSize, true); - } + pActive->QueueFrame(m_pScaledFrameBuffer, bufferSize, true); } else { @@ -920,44 +770,23 @@ void ZeDMD::RenderRgb888(uint8_t* pFrame) m_pRgb565Buffer[i * 2] = tmp & 0xFF; } - if (m_wifi) - { - m_pZeDMDWiFi->QueueFrame(m_pRgb565Buffer, rgb565Size * 2); - } - else if (m_usb) - { - m_pZeDMDComm->QueueFrame(m_pRgb565Buffer, rgb565Size * 2); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueFrame(m_pRgb565Buffer, rgb565Size * 2); - } + pActive->QueueFrame(m_pRgb565Buffer, rgb565Size * 2); } } void ZeDMD::RenderRgb565(uint16_t* pFrame) { - if (m_verbose) m_pZeDMDComm->Log("ZeDMD::RenderRgb565"); + ZeDMDComm* pActive = GetActiveZeDMD(); + if (m_verbose && pActive) pActive->Log("ZeDMD::RenderRgb565"); - if (!(m_usb || m_wifi || m_spi) || !UpdateFrameBuffer565(pFrame)) + if (!pActive || !UpdateFrameBuffer565(pFrame)) { return; } int size = Scale565(m_pScaledFrameBuffer, pFrame, is_bigendian()); - if (m_wifi) - { - m_pZeDMDWiFi->QueueFrame(m_pScaledFrameBuffer, size); - } - else if (m_usb) - { - m_pZeDMDComm->QueueFrame(m_pScaledFrameBuffer, size); - } - else if (m_spi) - { - m_pZeDMDSpi->QueueFrame(m_pScaledFrameBuffer, size); - } + pActive->QueueFrame(m_pScaledFrameBuffer, size); } bool ZeDMD::UpdateFrameBuffer888(uint8_t* pFrame) diff --git a/src/ZeDMD.h b/src/ZeDMD.h index be1eb23..ea688c6 100644 --- a/src/ZeDMD.h +++ b/src/ZeDMD.h @@ -642,10 +642,15 @@ class ZEDMDAPI ZeDMD uint8_t GetScaleMode(uint16_t frameWidth, uint16_t frameHeight, uint8_t* pXOffset, uint8_t* pYOffset); int Scale888(uint8_t* pScaledFrame, uint8_t* pFrame, uint8_t bytes); int Scale565(uint8_t* pScaledFrame, uint16_t* pFrame, bool bigEndian); + void SetActiveZeDMD(ZeDMDComm* pActive, bool usb, bool wifi, bool spi); + ZeDMDComm* GetActiveZeDMD() const; + ZeDMDWiFi* GetActiveZeDMDWiFi() const; + ZeDMDSpi* GetActiveZeDMDSpi() const; ZeDMDComm* m_pZeDMDComm; ZeDMDSpi* m_pZeDMDSpi; ZeDMDWiFi* m_pZeDMDWiFi; + ZeDMDComm* m_pActiveZeDMD = nullptr; uint16_t m_romWidth; uint16_t m_romHeight; diff --git a/src/ZeDMDComm.cpp b/src/ZeDMDComm.cpp index 88a203b..167f5f5 100644 --- a/src/ZeDMDComm.cpp +++ b/src/ZeDMDComm.cpp @@ -87,6 +87,7 @@ void ZeDMDComm::Run() // All frames are sent, move delayed frame into the frames queue. if (m_delayedFrameReady) { + if (m_verbose) Log("libzedmd queuing dealyed command %02X", m_delayedFrame.command); m_frames.push(std::move(m_delayedFrame)); m_delayedFrameReady = false; m_delayedFrameMutex.unlock(); @@ -98,7 +99,7 @@ void ZeDMDComm::Run() m_frameQueueMutex.unlock(); KeepAlive(); - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + std::this_thread::sleep_for(std::chrono::microseconds(10)); continue; } @@ -192,6 +193,8 @@ void ZeDMDComm::QueueFrame(uint8_t* data, int size, bool rgb888) { ZeDMDFrame frame(rgb888 ? ZEDMD_COMM_COMMAND::RGB888Stream : ZEDMD_COMM_COMMAND::RGB565Stream, data, size); + if (m_verbose) Log("libzedmd queuing command %02X", frame.command); + m_frameQueueMutex.lock(); m_frames.push(std::move(frame)); m_frameQueueMutex.unlock(); @@ -1123,6 +1126,8 @@ void ZeDMDComm::KeepAlive() m_lastKeepAlive = now; try { + if (m_verbose) Log("Keep alive ZeDMD connection"); + SendChunks(s_keepAliveData.get(), s_keepAliveSize); } catch (const std::exception& e) From 29c9b1cf3da1a76555dfbe552aa8330cd4b5830d Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Tue, 30 Dec 2025 01:22:07 +0100 Subject: [PATCH 40/42] avoid some memory issueavoid some memory issues --- src/ZeDMD.cpp | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 876116a..63fd037 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -1,6 +1,7 @@ #include "ZeDMD.h" #include +#include #include "FrameUtil.h" #include "ZeDMDComm.h" @@ -32,17 +33,20 @@ ZeDMD::~ZeDMD() if (m_pFrameBuffer) { - delete m_pFrameBuffer; + free(m_pFrameBuffer); + m_pFrameBuffer = nullptr; } if (m_pScaledFrameBuffer) { - delete m_pScaledFrameBuffer; + free(m_pScaledFrameBuffer); + m_pScaledFrameBuffer = nullptr; } if (m_pRgb565Buffer) { - delete m_pRgb565Buffer; + free(m_pRgb565Buffer); + m_pRgb565Buffer = nullptr; } } @@ -737,7 +741,10 @@ void ZeDMD::ClearScreen() pActive->QueueCommand(ZEDMD_COMM_COMMAND::ClearScreen); } // "Blank" the frame buffer. - memset(m_pFrameBuffer, 0, ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); + if (m_pFrameBuffer) + { + memset(m_pFrameBuffer, 0, ZEDMD_MAX_WIDTH * ZEDMD_MAX_HEIGHT * 3); + } } void ZeDMD::EnableTrueRgb888(bool enable) { m_rgb888 = enable; } @@ -791,6 +798,11 @@ void ZeDMD::RenderRgb565(uint16_t* pFrame) bool ZeDMD::UpdateFrameBuffer888(uint8_t* pFrame) { + if (!m_pFrameBuffer) + { + return false; + } + if (0 == memcmp(m_pFrameBuffer, pFrame, m_romWidth * m_romHeight * 3)) { return false; @@ -802,6 +814,11 @@ bool ZeDMD::UpdateFrameBuffer888(uint8_t* pFrame) bool ZeDMD::UpdateFrameBuffer565(uint16_t* pFrame) { + if (!m_pFrameBuffer) + { + return false; + } + if (0 == memcmp(m_pFrameBuffer, pFrame, m_romWidth * m_romHeight * 2)) { return false; From 12f9419343ec80a7b6498570dd64d367bc436794 Mon Sep 17 00:00:00 2001 From: pastor <142336369+PastorL69@users.noreply.github.com> Date: Wed, 31 Dec 2025 09:20:36 +0100 Subject: [PATCH 41/42] change to 2ms minimum between SPI transfers (#43) --- src/ZeDMDSpi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 073e327..4341f9d 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -243,7 +243,7 @@ bool ZeDMDSpi::SendChunks(const uint8_t* pData, uint16_t size) return false; } - std::this_thread::sleep_for(std::chrono::microseconds(100)); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); return true; } From bc5c4e70bed0facb76b8636c565ce818a507e2c0 Mon Sep 17 00:00:00 2001 From: Markus Kalkbrenner Date: Wed, 31 Dec 2025 09:36:04 +0100 Subject: [PATCH 42/42] configureable frame pause --- src/ZeDMD.cpp | 5 +++-- src/ZeDMD.h | 2 +- src/ZeDMDSpi.cpp | 9 ++++++++- src/ZeDMDSpi.h | 4 +++- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ZeDMD.cpp b/src/ZeDMD.cpp index 63fd037..2aac4e5 100644 --- a/src/ZeDMD.cpp +++ b/src/ZeDMD.cpp @@ -1,7 +1,7 @@ #include "ZeDMD.h" -#include #include +#include #include "FrameUtil.h" #include "ZeDMDComm.h" @@ -708,9 +708,10 @@ bool ZeDMD::Open(uint16_t width, uint16_t height) return m_usb; } -bool ZeDMD::OpenSpi(uint32_t speed, uint16_t width, uint16_t height) +bool ZeDMD::OpenSpi(uint32_t speed, uint8_t framePause, uint16_t width, uint16_t height) { m_pZeDMDSpi->SetSpeed(speed); + m_pZeDMDSpi->SetFramePause(framePause); m_pZeDMDSpi->SetWidth(width); m_pZeDMDSpi->SetHeight(height); diff --git a/src/ZeDMD.h b/src/ZeDMD.h index ea688c6..ed55220 100644 --- a/src/ZeDMD.h +++ b/src/ZeDMD.h @@ -133,7 +133,7 @@ class ZEDMDAPI ZeDMD */ bool OpenDefaultWiFi(); - bool OpenSpi(uint32_t speed, uint16_t width, uint16_t height); + bool OpenSpi(uint32_t speed, uint8_t framePause, uint16_t width, uint16_t height); /** @brief Close connection to ZeDMD * diff --git a/src/ZeDMDSpi.cpp b/src/ZeDMDSpi.cpp index 4341f9d..22bc2c3 100644 --- a/src/ZeDMDSpi.cpp +++ b/src/ZeDMDSpi.cpp @@ -243,7 +243,14 @@ bool ZeDMDSpi::SendChunks(const uint8_t* pData, uint16_t size) return false; } - std::this_thread::sleep_for(std::chrono::milliseconds(2)); + if (m_framePause > 0) + { + std::this_thread::sleep_for(std::chrono::milliseconds(m_framePause)); + } + else + { + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } return true; } diff --git a/src/ZeDMDSpi.h b/src/ZeDMDSpi.h index cf6388b..3781f1b 100644 --- a/src/ZeDMDSpi.h +++ b/src/ZeDMDSpi.h @@ -38,6 +38,7 @@ class ZeDMDSpi : public ZeDMDComm void QueueCommand(char command, uint8_t* buffer, int size) override; void SetSpeed(uint32_t speed) { m_speed = speed; } + void SetFramePause(uint8_t pause) { m_framePause = pause; } void SetWidth(uint16_t width) { m_width = width; } void SetHeight(uint16_t height) { m_height = height; } @@ -48,7 +49,8 @@ class ZeDMDSpi : public ZeDMDComm private: bool IsSupportedPlatform() const; - uint32_t m_speed = 24000000; // 24 MHz + uint32_t m_speed = 72000000; // 72MHz + uint8_t m_framePause = 2; // 2ms int m_fileDescriptor = -1; #if defined(SPI_SUPPORT) gpiod_chip* m_gpioChip = nullptr;