- Resolved persistent segmentation faults by correcting memory management and simplifying interactive input logic. - Eliminated LED flickering by implementing a longer timeout in DNRGB packets and removing unnecessary refresh timers. - Added an ASCII visualization of the LED layout to the terminal for better user feedback. - Updated lessons_learned.md with detailed explanations of the problems and their solutions.
167 lines
6.1 KiB
C++
167 lines
6.1 KiB
C++
#include "wledclient.h"
|
|
#include <QDebug>
|
|
#include <QColor>
|
|
#include <QByteArray>
|
|
#include <QtGlobal>
|
|
|
|
// 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, QObject *parent) :
|
|
QObject(parent),
|
|
_wledHost(host),
|
|
_wledPort(port)
|
|
{
|
|
_udpSocket = new QUdpSocket(this);
|
|
qDebug() << "WledClient initialized for host:" << _wledHost.toString() << "port:" << _wledPort;
|
|
}
|
|
|
|
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)
|
|
|
|
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 (1 second)
|
|
datagram.append(1);
|
|
|
|
// 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);
|
|
|
|
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);
|
|
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<QColor> &colors, int timeout)
|
|
{
|
|
if (colors.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
int totalLeds = colors.size();
|
|
int ledsPerPacket = MAX_LED_DATA_PER_PACKET / 3; // 3 bytes per LED (RGB)
|
|
|
|
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);
|
|
|
|
for (int j = 0; j < currentLedsInPacket; ++j) {
|
|
const QColor &color = colors.at(i + j);
|
|
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 = 168 LEDs
|
|
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 (1 second)
|
|
datagram.append(1);
|
|
|
|
// 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) {
|
|
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;
|
|
}
|
|
}
|
|
} |