2013-11-11 21:07:24 +01:00
|
|
|
#include "LedDeviceAdalight.h"
|
|
|
|
|
2020-11-14 17:58:56 +01:00
|
|
|
#include <QtEndian>
|
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
// Constants
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Configuration settings
|
|
|
|
const char CONFIG_STREAM_PROTOCOL[] = "streamProtocol";
|
|
|
|
|
|
|
|
const char CONFIG_WHITE_CHANNEL_CALLIBRATION[] = "white_channel_calibration";
|
|
|
|
const char CONFIG_WHITE_CHANNEL_LIMIT[] = "white_channel_limit";
|
|
|
|
const char CONFIG_WHITE_CHANNEL_RED[] = "white_channel_red";
|
|
|
|
const char CONFIG_WHITE_CHANNEL_GREEN[] = "white_channel_green";
|
|
|
|
const char CONFIG_WHITE_CHANNEL_BLUE[] = "white_channel_blue";
|
|
|
|
constexpr int HEADER_SIZE {6};
|
|
|
|
|
|
|
|
} //End of constants
|
2020-08-02 22:32:00 +02:00
|
|
|
|
2016-10-13 21:59:58 +02:00
|
|
|
LedDeviceAdalight::LedDeviceAdalight(const QJsonObject &deviceConfig)
|
2020-08-08 00:21:19 +02:00
|
|
|
: ProviderRs232(deviceConfig)
|
2016-10-08 08:14:36 +02:00
|
|
|
{
|
2013-11-11 21:07:24 +01:00
|
|
|
}
|
|
|
|
|
2016-10-13 21:59:58 +02:00
|
|
|
LedDevice* LedDeviceAdalight::construct(const QJsonObject &deviceConfig)
|
2016-08-23 20:07:12 +02:00
|
|
|
{
|
|
|
|
return new LedDeviceAdalight(deviceConfig);
|
|
|
|
}
|
|
|
|
|
2016-10-13 21:59:58 +02:00
|
|
|
bool LedDeviceAdalight::init(const QJsonObject &deviceConfig)
|
2016-10-08 08:14:36 +02:00
|
|
|
{
|
2020-07-12 20:27:56 +02:00
|
|
|
bool isInitOK = false;
|
2020-02-10 15:21:58 +01:00
|
|
|
|
2020-07-12 20:27:56 +02:00
|
|
|
// Initialise sub-class
|
|
|
|
if ( ProviderRs232::init(deviceConfig) )
|
|
|
|
{
|
2022-09-30 17:02:40 +02:00
|
|
|
_streamProtocol = static_cast<Adalight::PROTOCOLTYPE>(deviceConfig[CONFIG_STREAM_PROTOCOL].toString().toInt());
|
|
|
|
switch (_streamProtocol) {
|
2016-10-08 08:14:36 +02:00
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
case Adalight::AWA:
|
|
|
|
{
|
|
|
|
Debug( _log, "Adalight driver uses Hyperserial protocol");
|
|
|
|
_white_channel_calibration = deviceConfig[CONFIG_WHITE_CHANNEL_CALLIBRATION].toBool(false);
|
|
|
|
double _white_channel_limit_percent = deviceConfig[CONFIG_WHITE_CHANNEL_LIMIT].toDouble(1);
|
|
|
|
_white_channel_limit = static_cast<uint8_t>(qMin(qRound(_white_channel_limit_percent * 255.0 / 100.0), 255));
|
|
|
|
_white_channel_red = static_cast<uint8_t>(qMin(deviceConfig[CONFIG_WHITE_CHANNEL_RED].toInt(255), 255));
|
|
|
|
_white_channel_green = static_cast<uint8_t>(qMin(deviceConfig[CONFIG_WHITE_CHANNEL_GREEN].toInt(255), 255));
|
|
|
|
_white_channel_blue = static_cast<uint8_t>(qMin(deviceConfig[CONFIG_WHITE_CHANNEL_BLUE].toInt(255), 255));
|
|
|
|
|
|
|
|
DebugIf(_white_channel_calibration, _log, "White channel limit: %i (%.2f%), red: %i, green: %i, blue: %i", _white_channel_limit, _white_channel_limit_percent, _white_channel_red, _white_channel_green, _white_channel_blue);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Adalight::LBAPA:
|
|
|
|
Debug( _log, "Adalight driver uses LightBerry APA102 protocol");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Adalight::ADA:
|
|
|
|
Debug( _log, "Adalight driver uses standard Adalight protocol");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Error( _log, "Adalight driver - unsupported protocol");
|
|
|
|
return false;
|
|
|
|
}
|
2016-12-01 16:17:14 +01:00
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
prepareHeader();
|
|
|
|
isInitOK = true;
|
|
|
|
}
|
|
|
|
return isInitOK;
|
|
|
|
}
|
2020-07-12 20:27:56 +02:00
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
void LedDeviceAdalight::prepareHeader()
|
|
|
|
{
|
|
|
|
// create ledBuffer
|
|
|
|
uint totalLedCount = _ledCount;
|
|
|
|
_bufferLength = static_cast<qint64>(HEADER_SIZE + _ledRGBCount);
|
2020-07-12 20:27:56 +02:00
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
switch (_streamProtocol) {
|
|
|
|
case Adalight::LBAPA:
|
|
|
|
{
|
|
|
|
const unsigned int startFrameSize = 4;
|
|
|
|
const unsigned int bytesPerRGBLed = 4;
|
|
|
|
const unsigned int endFrameSize = qMax<unsigned int>(((_ledCount + 15) / 16), bytesPerRGBLed);
|
|
|
|
_bufferLength = HEADER_SIZE + (_ledCount * bytesPerRGBLed) + startFrameSize + endFrameSize;
|
|
|
|
|
|
|
|
_ledBuffer.resize(static_cast<size_t>(_bufferLength), 0x00);
|
|
|
|
|
|
|
|
// init constant data values
|
|
|
|
for (uint iLed=1; iLed <= _ledCount; iLed++)
|
2020-07-12 20:27:56 +02:00
|
|
|
{
|
2022-09-30 17:02:40 +02:00
|
|
|
_ledBuffer[iLed*4+HEADER_SIZE] = 0xFF;
|
2016-12-14 22:45:00 +01:00
|
|
|
}
|
2022-09-30 17:02:40 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Adalight::AWA:
|
2023-02-12 21:20:50 +01:00
|
|
|
_bufferLength += 8;
|
2022-12-19 14:04:35 +01:00
|
|
|
[[fallthrough]];
|
2022-09-30 17:02:40 +02:00
|
|
|
case Adalight::ADA:
|
2022-12-19 14:04:35 +01:00
|
|
|
[[fallthrough]];
|
2022-09-30 17:02:40 +02:00
|
|
|
default:
|
|
|
|
totalLedCount -= 1;
|
|
|
|
_ledBuffer.resize(static_cast<size_t>(_bufferLength), 0x00);
|
|
|
|
break;
|
|
|
|
}
|
2016-12-01 16:17:14 +01:00
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
_ledBuffer[0] = 'A';
|
|
|
|
if (_streamProtocol == Adalight::AWA )
|
|
|
|
{
|
|
|
|
_ledBuffer[1] = 'w';
|
|
|
|
_ledBuffer[2] = _white_channel_calibration ? 'A' : 'a';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-07-12 20:27:56 +02:00
|
|
|
_ledBuffer[1] = 'd';
|
|
|
|
_ledBuffer[2] = 'a';
|
2022-09-30 17:02:40 +02:00
|
|
|
}
|
2016-12-01 16:17:14 +01:00
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
qToBigEndian<quint16>(static_cast<quint16>(totalLedCount), &_ledBuffer[3]);
|
|
|
|
_ledBuffer[5] = _ledBuffer[3] ^ _ledBuffer[4] ^ 0x55; // Checksum
|
2016-10-08 08:14:36 +02:00
|
|
|
|
2022-11-27 15:47:54 +01:00
|
|
|
Debug( _log, "Adalight header for %d leds (size: %d): %c%c%c 0x%02x 0x%02x 0x%02x", _ledCount, _ledBuffer.size(),
|
2022-09-30 17:02:40 +02:00
|
|
|
_ledBuffer[0], _ledBuffer[1], _ledBuffer[2], _ledBuffer[3], _ledBuffer[4], _ledBuffer[5] );
|
2016-10-08 08:14:36 +02:00
|
|
|
}
|
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
|
2013-11-12 07:52:00 +01:00
|
|
|
int LedDeviceAdalight::write(const std::vector<ColorRgb> & ledValues)
|
2013-11-11 21:07:24 +01:00
|
|
|
{
|
2022-09-30 17:02:40 +02:00
|
|
|
if (_ledCount != ledValues.size())
|
|
|
|
{
|
|
|
|
Warning(_log, "Adalight LED count has changed (old: %d, new: %d). Rebuilding header.", _ledCount, ledValues.size());
|
|
|
|
_ledCount = static_cast<uint>(ledValues.size());
|
|
|
|
_ledRGBCount = _ledCount * 3;
|
|
|
|
prepareHeader();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_bufferLength > static_cast<qint64>(_ledBuffer.size()))
|
|
|
|
{
|
|
|
|
Warning(_log, "Adalight buffer's size has changed. Skipping refresh.");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_streamProtocol == Adalight::LBAPA )
|
2016-12-01 16:17:14 +01:00
|
|
|
{
|
2020-02-10 15:21:58 +01:00
|
|
|
for (signed iLed=1; iLed<=static_cast<int>( _ledCount); iLed++)
|
2016-12-01 16:17:14 +01:00
|
|
|
{
|
2022-09-30 17:02:40 +02:00
|
|
|
const ColorRgb& rgb = ledValues[static_cast<size_t>(iLed-1)];
|
|
|
|
_ledBuffer[static_cast<size_t>(iLed*4+7)] = rgb.red;
|
|
|
|
_ledBuffer[static_cast<size_t>(iLed*4+8)] = rgb.green;
|
|
|
|
_ledBuffer[static_cast<size_t>(iLed*4+9)] = rgb.blue;
|
2016-12-01 16:17:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-30 17:02:40 +02:00
|
|
|
uint8_t* writer = _ledBuffer.data() + HEADER_SIZE;
|
|
|
|
uint8_t* hasher = writer;
|
2020-08-02 22:32:00 +02:00
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
memcpy(writer, ledValues.data(), ledValues.size() * sizeof(ColorRgb));
|
|
|
|
writer += ledValues.size() * sizeof(ColorRgb);
|
|
|
|
|
|
|
|
if (_streamProtocol == Adalight::AWA)
|
|
|
|
{
|
|
|
|
whiteChannelExtension(writer);
|
|
|
|
|
2023-02-12 21:20:50 +01:00
|
|
|
uint16_t fletcher1 = 0;
|
|
|
|
uint16_t fletcher2 = 0;
|
|
|
|
uint16_t fletcherExt = 0;
|
|
|
|
uint8_t position = 0;
|
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
while (hasher < writer)
|
|
|
|
{
|
2023-02-12 21:20:50 +01:00
|
|
|
fletcherExt = (fletcherExt + (*(hasher) ^ (position++))) % 255;
|
2022-09-30 17:02:40 +02:00
|
|
|
fletcher1 = (fletcher1 + *(hasher++)) % 255;
|
|
|
|
fletcher2 = (fletcher2 + fletcher1) % 255;
|
|
|
|
}
|
|
|
|
*(writer++) = static_cast<uint8_t>(fletcher1);
|
|
|
|
*(writer++) = static_cast<uint8_t>(fletcher2);
|
2023-02-12 21:20:50 +01:00
|
|
|
*(writer++) = static_cast<uint8_t>((fletcherExt != 0x41) ? fletcherExt : 0xaa);
|
2022-09-30 17:02:40 +02:00
|
|
|
}
|
|
|
|
_bufferLength = writer - _ledBuffer.data();
|
2016-12-01 16:17:14 +01:00
|
|
|
}
|
2013-11-11 21:07:24 +01:00
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
int rc = writeBytes(_bufferLength, _ledBuffer.data());
|
2020-07-12 20:27:56 +02:00
|
|
|
|
|
|
|
return rc;
|
2017-02-09 20:10:57 +01:00
|
|
|
}
|
2022-09-30 17:02:40 +02:00
|
|
|
|
2022-11-27 15:47:54 +01:00
|
|
|
void LedDeviceAdalight::readFeedback()
|
|
|
|
{
|
|
|
|
if (_streamProtocol == Adalight::AWA)
|
|
|
|
{
|
|
|
|
bool continuousLines {true};
|
|
|
|
while ( _rs232Port.canReadLine() )
|
|
|
|
{
|
|
|
|
QByteArray record = _rs232Port.readLine();
|
|
|
|
if (record.startsWith("FPS:"))
|
|
|
|
{
|
|
|
|
if (continuousLines)
|
|
|
|
{
|
|
|
|
continuousLines = false;
|
|
|
|
}
|
|
|
|
Debug(_log, "Statistics %s", record.trimmed().constData());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::cout << record.toStdString() << std::flush;
|
|
|
|
continuousLines = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-30 17:02:40 +02:00
|
|
|
void LedDeviceAdalight::whiteChannelExtension(uint8_t*& writer)
|
|
|
|
{
|
|
|
|
if (_streamProtocol == Adalight::AWA && _white_channel_calibration)
|
|
|
|
{
|
|
|
|
*(writer++) = _white_channel_limit;
|
|
|
|
*(writer++) = _white_channel_red;
|
|
|
|
*(writer++) = _white_channel_green;
|
|
|
|
*(writer++) = _white_channel_blue;
|
|
|
|
}
|
|
|
|
}
|