#include "wledclient.h" #include #include #include #include // DDP Port for WLED const ushort WLED_DDP_PORT = 4048; // DNRGB Protocol Type const quint8 DDP_PROTOCOL_DNRGB = 4; // Max UDP payload size (approx 508 bytes for safe transmission) // 4 bytes for DDP header, so 504 bytes for LED data const int MAX_LED_DATA_PER_PACKET = 504; // 504 bytes / 3 bytes per LED = 168 LEDs WledClient::WledClient(QString host, ushort port, QString colorOrder, QObject *parent) : QObject(parent), _wledHost(host), _wledPort(port), _colorOrder(colorOrder) { _udpSocket = new QUdpSocket(this); qDebug() << "WledClient initialized for host:" << _wledHost.toString() << "port:" << _wledPort << "color order:" << _colorOrder; } WledClient::~WledClient() { _udpSocket->close(); qDebug() << "WledClient destroyed."; } void WledClient::sendImage(const QImage &image) { if (image.isNull()) { qWarning() << "WledClient: Cannot send null image."; return; } // Ensure image is in RGB888 format for direct byte access QImage rgbImage = image.convertToFormat(QImage::Format_RGB888); int totalLeds = rgbImage.width() * rgbImage.height(); int ledsPerPacket = MAX_LED_DATA_PER_PACKET / 3; // 3 bytes per LED (RGB) qDebug() << "WledClient: sendImage - totalLeds:" << totalLeds << "ledsPerPacket:" << ledsPerPacket; for (int i = 0; i < totalLeds; i += ledsPerPacket) { QByteArray datagram; datagram.reserve(4 + (ledsPerPacket * 3)); // Header + max LED data // Byte 0: Protocol type (DNRGB) datagram.append(DDP_PROTOCOL_DNRGB); // Byte 1: Timeout (5 seconds) datagram.append(5); // Bytes 2 & 3: Starting LED index (low byte, then high byte) quint16 startIndex = i; datagram.append(startIndex & 0xFF); // Low byte datagram.append((startIndex >> 8) & 0xFF); // High byte int currentLedsInPacket = qMin(ledsPerPacket, totalLeds - i); qDebug() << "WledClient: Sending packet (sendImage) - i:" << i << "startIndex:" << startIndex << "currentLedsInPacket:" << currentLedsInPacket; for (int j = 0; j < currentLedsInPacket; ++j) { int pixelIndex = i + j; int x = pixelIndex % rgbImage.width(); int y = pixelIndex / rgbImage.width(); QRgb pixel = rgbImage.pixel(x, y); // Swap R and G for GRB order datagram.append(qRed(pixel)); datagram.append(qGreen(pixel)); datagram.append(qBlue(pixel)); } qint64 bytesSent = _udpSocket->writeDatagram(datagram, _wledHost, _wledPort); if (bytesSent == -1) { qWarning() << "WledClient: Failed to send datagram:" << _udpSocket->errorString(); emit error(_udpSocket->errorString()); } else if (bytesSent != datagram.size()) { qWarning() << "WledClient: Sent fewer bytes than expected. Expected:" << datagram.size() << "Sent:" << bytesSent; } } } void WledClient::setLedsColor(const QVector &colors, int timeout) { if (colors.isEmpty()) { return; } int totalLeds = colors.size(); int ledsPerPacket = MAX_LED_DATA_PER_PACKET / 3; // 3 bytes per LED (RGB) qDebug() << "WledClient: setLedsColor - totalLeds:" << totalLeds << "ledsPerPacket:" << ledsPerPacket; for (int i = 0; i < totalLeds; i += ledsPerPacket) { QByteArray datagram; datagram.reserve(4 + (ledsPerPacket * 3)); // Header + max LED data // Byte 0: Protocol type (DNRGB) datagram.append(DDP_PROTOCOL_DNRGB); // Byte 1: Timeout (in seconds) datagram.append(timeout); // Bytes 2 & 3: Starting LED index (low byte, then high byte) quint16 startIndex = i; datagram.append(startIndex & 0xFF); // Low byte datagram.append((startIndex >> 8) & 0xFF); // High byte int currentLedsInPacket = qMin(ledsPerPacket, totalLeds - i); qDebug() << "WledClient: Sending packet (setLedsColor) - i:" << i << "startIndex:" << startIndex << "currentLedsInPacket:" << currentLedsInPacket; for (int j = 0; j < currentLedsInPacket; ++j) { const QColor &color = colors.at(i + j); // Swap R and G for GRB order datagram.append(color.red()); datagram.append(color.green()); datagram.append(color.blue()); } qint64 bytesSent = _udpSocket->writeDatagram(datagram, _wledHost, _wledPort); if (bytesSent == -1) { qWarning() << "WledClient: Failed to send datagram for setLedsColor:" << _udpSocket->errorString(); emit error(_udpSocket->errorString()); } else if (bytesSent != datagram.size()) { qWarning() << "WledClient: Sent fewer bytes than expected for setLedsColor. Expected:" << datagram.size() << "Sent:" << bytesSent; } } } void WledClient::flashLeds(int startIndex, int count, QColor color) { if (count <= 0) { qWarning() << "WledClient: Count must be positive for flashLeds."; return; } // Max UDP payload size (approx 508 bytes for safe transmission) // 4 bytes for DDP header, so 504 bytes for LED data const int MAX_LED_DATA_PER_PACKET = 504; // 504 bytes / 3 bytes per LED (RGB) int ledsPerPacket = MAX_LED_DATA_PER_PACKET / 3; // 3 bytes per LED (RGB) for (int i = 0; i < count; i += ledsPerPacket) { QByteArray datagram; datagram.reserve(4 + (ledsPerPacket * 3)); // Header + max LED data // Byte 0: Protocol type (DNRGB) datagram.append(DDP_PROTOCOL_DNRGB); // Byte 1: Timeout (5 seconds) datagram.append(5); // Bytes 2 & 3: Starting LED index (low byte, then high byte) quint16 currentStartIndex = startIndex + i; datagram.append(currentStartIndex & 0xFF); // Low byte datagram.append((currentStartIndex >> 8) & 0xFF); // High byte int currentLedsInPacket = qMin(ledsPerPacket, count - i); for (int j = 0; j < currentLedsInPacket; ++j) { // Swap R and G for GRB order datagram.append(color.red()); datagram.append(color.green()); datagram.append(color.blue()); } qDebug() << "WledClient: Sending flashLeds datagram (hex):" << datagram.toHex(); qDebug() << "WledClient: Datagram size:" << datagram.size(); qint64 bytesSent = _udpSocket->writeDatagram(datagram, _wledHost, _wledPort); if (bytesSent == -1) { qWarning() << "WledClient: Failed to send datagram for flashLeds:" << _udpSocket->errorString(); emit error(_udpSocket->errorString()); } else if (bytesSent != datagram.size()) { qWarning() << "WledClient: Sent fewer bytes than expected for flashLeds. Expected:" << datagram.size() << "Sent:" << bytesSent; } } }