From 5f3331e1e9ff814ddb6e93544de0d3879370c7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 17 Feb 2021 13:15:06 +0100 Subject: [PATCH] Update ws2812fx behaviour and test firmware --- ws2812fx/integrationpluginws2812fx.cpp | 386 +++++++++++++----------- ws2812fx/integrationpluginws2812fx.json | 4 +- ws2812fx/nymealight.cpp | 88 +++++- ws2812fx/nymealight.h | 3 + ws2812fx/nymealightinterface.h | 16 +- ws2812fx/nymealightserialinterface.cpp | 13 +- ws2812fx/nymealightserialinterface.h | 1 + 7 files changed, 313 insertions(+), 198 deletions(-) diff --git a/ws2812fx/integrationpluginws2812fx.cpp b/ws2812fx/integrationpluginws2812fx.cpp index 93ae7515..61620e0a 100644 --- a/ws2812fx/integrationpluginws2812fx.cpp +++ b/ws2812fx/integrationpluginws2812fx.cpp @@ -45,6 +45,8 @@ s \quotefile plugins/deviceplugins/ws2812fx/devicepluginws2812fx.json */ #include +#include + #include "integrationpluginws2812fx.h" #include "plugininfo.h" @@ -120,7 +122,28 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info) return; } - if (action.actionTypeId() == ws2812fxColorActionTypeId) { + if (action.actionTypeId() == ws2812fxPowerActionTypeId) { + bool power = action.param(ws2812fxPowerActionPowerParamTypeId).value().toBool(); + quint8 brightness = 0; + if (power) { + quint8 brightnessPercentage = thing->stateValue(ws2812fxBrightnessStateTypeId).toUInt(); + brightness = qRound(255.0 * brightnessPercentage / 100); + } + + qCDebug(dcWs2812fx()) << "Set power" << power; + NymeaLightInterfaceReply *reply = light->setBrightness(brightness, 500); + connect(info, &ThingActionInfo::aborted, reply, &NymeaLightInterfaceReply::finished); + connect(reply, &NymeaLightInterfaceReply::finished, this, [=](){ + if (reply->status() != NymeaLightInterface::StatusSuccess) { + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + qCDebug(dcWs2812fx()) << "Set power finished successfully" << power; + thing->setStateValue(ws2812fxPowerStateTypeId, power); + info->finish(Thing::ThingErrorNoError); + }); + } else if (action.actionTypeId() == ws2812fxColorActionTypeId) { QColor color = action.param(ws2812fxColorActionColorParamTypeId).value().value(); qCDebug(dcWs2812fx()) << "Set color to" << color.name(QColor::HexRgb); NymeaLightInterfaceReply *reply = light->setColor(color); @@ -134,182 +157,193 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info) thing->setStateValue(ws2812fxColorStateTypeId, color); info->finish(Thing::ThingErrorNoError); }); + } else if (action.actionTypeId() == ws2812fxColorTemperatureActionTypeId) { + // minValue 153, maxValue 500 + uint colorTemperature = action.param(ws2812fxColorTemperatureActionColorTemperatureParamTypeId).value().toDouble(); + QColor color; + color.setRgb(255, 255, qRound((255.00 - (((colorTemperature - 153.00) / 347.00)) * 255.00))); + + qCDebug(dcWs2812fx()) << "Set color temperature" << colorTemperature << color.name(QColor::HexRgb); + NymeaLightInterfaceReply *reply = light->setColor(color); + connect(info, &ThingActionInfo::aborted, reply, &NymeaLightInterfaceReply::finished); + connect(reply, &NymeaLightInterfaceReply::finished, this, [=](){ + if (reply->status() != NymeaLightInterface::StatusSuccess) { + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + qCDebug(dcWs2812fx()) << "Set color temperature finished successfully" << colorTemperature; + thing->setStateValue(ws2812fxColorTemperatureStateTypeId, colorTemperature); + info->finish(Thing::ThingErrorNoError); + }); + } else if (action.actionTypeId() == ws2812fxBrightnessActionTypeId) { + quint8 brightnessPercentage = action.param(ws2812fxBrightnessActionBrightnessParamTypeId).value().toUInt(); + quint8 brightness = qRound(255.0 * brightnessPercentage / 100); + qCDebug(dcWs2812fx()) << "Set brightness to" << brightnessPercentage << brightness; + NymeaLightInterfaceReply *reply = light->setBrightness(brightness, 1000); + connect(info, &ThingActionInfo::aborted, reply, &NymeaLightInterfaceReply::finished); + connect(reply, &NymeaLightInterfaceReply::finished, this, [=](){ + if (reply->status() != NymeaLightInterface::StatusSuccess) { + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + qCDebug(dcWs2812fx()) << "Set brightness finished successfully" << brightness; + thing->setStateValue(ws2812fxBrightnessStateTypeId, brightnessPercentage); + info->finish(Thing::ThingErrorNoError); + }); + } else if (action.actionTypeId() == ws2812fxSpeedActionTypeId) { + quint16 speedPercentage = action.param(ws2812fxSpeedActionSpeedParamTypeId).value().toUInt(); + quint16 speed = 2000 - (speedPercentage * 20); + + qCDebug(dcWs2812fx()) << "Set speed" << speedPercentage << "%" << speed << "ms"; + NymeaLightInterfaceReply *reply = light->setSpeed(speed); + connect(info, &ThingActionInfo::aborted, reply, &NymeaLightInterfaceReply::finished); + connect(reply, &NymeaLightInterfaceReply::finished, this, [=](){ + if (reply->status() != NymeaLightInterface::StatusSuccess) { + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + qCDebug(dcWs2812fx()) << "Set speed finished successfully" << speedPercentage << "%" << speed << "ms"; + thing->setStateValue(ws2812fxSpeedStateTypeId, speedPercentage); + info->finish(Thing::ThingErrorNoError); + }); + } else if (action.actionTypeId() == ws2812fxEffectModeActionTypeId) { + QString effectMode = action.param(ws2812fxEffectModeActionEffectModeParamTypeId).value().toString(); + quint8 mode = FX_MODE_STATIC; + if (effectMode == "Static") { + mode = FX_MODE_STATIC; + } else if (effectMode == "Blink") { + mode = FX_MODE_BLINK; + } else if (effectMode == "Color Wipe") { + mode = FX_MODE_COLOR_WIPE; + } else if (effectMode == "Color Wipe Inverse") { + mode = FX_MODE_COLOR_WIPE_INV; + } else if (effectMode == "Color Wipe Reverse") { + mode = FX_MODE_COLOR_WIPE_REV; + } else if (effectMode == "Color Wipe Reverse Inverse") { + mode = FX_MODE_COLOR_WIPE_REV_INV; + } else if (effectMode == "Color Wipe Random") { + mode = FX_MODE_COLOR_WIPE_RANDOM; + } else if (effectMode == "Random Color") { + mode = FX_MODE_RANDOM_COLOR; + } else if (effectMode == "Single Dynamic") { + mode = FX_MODE_SINGLE_DYNAMIC; + } else if (effectMode == "Multi Dynamic") { + mode = FX_MODE_MULTI_DYNAMIC; + } else if (effectMode == "Rainbow") { + mode = FX_MODE_RAINBOW; + } else if (effectMode == "Rainbow Cycle") { + mode = FX_MODE_RAINBOW_CYCLE; + } else if (effectMode == "Scan") { + mode = FX_MODE_SCAN; + } else if (effectMode == "Dual Scan") { + mode = FX_MODE_DUAL_SCAN; + } else if (effectMode == "Fade") { + mode = FX_MODE_FADE; + } else if (effectMode == "Theater Chase") { + mode = FX_MODE_THEATER_CHASE; + } else if (effectMode == "Theater Chase Rainbow") { + mode = FX_MODE_THEATER_CHASE_RAINBOW; + } else if (effectMode == "Running Lights") { + mode = FX_MODE_RUNNING_LIGHTS; + } else if (effectMode == "Twinkle") { + mode = FX_MODE_TWINKLE; + } else if (effectMode == "Twinkle Random") { + mode = FX_MODE_TWINKLE_RANDOM; + } else if (effectMode == "Twinkle Fade") { + mode = FX_MODE_TWINKLE_FADE; + } else if (effectMode == "Twinkle Fade Random") { + mode = FX_MODE_TWINKLE_FADE_RANDOM; + } else if (effectMode == "Sparkle") { + mode = FX_MODE_SPARKLE; + } else if (effectMode == "Flash Sparkle") { + mode = FX_MODE_FLASH_SPARKLE; + } else if (effectMode == "Hyper Sparkle") { + mode = FX_MODE_HYPER_SPARKLE; + } else if (effectMode == "Strobe") { + mode = FX_MODE_STROBE; + } else if (effectMode == "Strobe Rainbow") { + mode = FX_MODE_STROBE_RAINBOW; + } else if (effectMode == "Multi Strobe") { + mode = FX_MODE_MULTI_STROBE; + } else if (effectMode == "Blink Rainbow") { + mode = FX_MODE_BLINK_RAINBOW; + } else if (effectMode == "Chase White") { + mode = FX_MODE_CHASE_WHITE; + } else if (effectMode == "Chase Color") { + mode = FX_MODE_CHASE_COLOR; + } else if (effectMode == "Chase Random") { + mode = FX_MODE_CHASE_RANDOM; + } else if (effectMode == "Chase Flash") { + mode = FX_MODE_CHASE_FLASH; + } else if (effectMode == "Chase Flash Random") { + mode = FX_MODE_CHASE_FLASH_RANDOM; + } else if (effectMode == "Chase Rainbow White") { + mode = FX_MODE_CHASE_RAINBOW_WHITE; + } else if (effectMode == "Chase Blackout") { + mode = FX_MODE_CHASE_BLACKOUT; + } else if (effectMode == "Chase Blackout Rainbow") { + mode = FX_MODE_CHASE_BLACKOUT_RAINBOW; + } else if (effectMode == "Color Sweep Random") { + mode = FX_MODE_COLOR_SWEEP_RANDOM; + } else if (effectMode == "Running Color") { + mode = FX_MODE_RUNNING_COLOR; + } else if (effectMode == "Running Red Blue") { + mode = FX_MODE_RUNNING_RED_BLUE; + } else if (effectMode == "Running Random") { + mode = FX_MODE_RUNNING_RANDOM; + }else if (effectMode == "Larson Scanner") { + mode = FX_MODE_LARSON_SCANNER; + }else if (effectMode == "Comet") { + mode = FX_MODE_COMET; + }else if (effectMode == "Fireworks") { + mode = FX_MODE_FIREWORKS; + }else if (effectMode == "Fireworks Random") { + mode = FX_MODE_FIREWORKS_RANDOM; + }else if (effectMode == "Merry Christmas") { + mode = FX_MODE_MERRY_CHRISTMAS; + }else if (effectMode == "Fire Flicker") { + mode = FX_MODE_FIRE_FLICKER; + }else if (effectMode == "Fire Flicker (soft)") { + mode = FX_MODE_FIRE_FLICKER_SOFT; + }else if (effectMode == "Fire Flicker (intense)") { + mode = FX_MODE_FIRE_FLICKER_INTENSE; + }else if (effectMode == "Circus Combustus") { + mode = FX_MODE_CIRCUS_COMBUSTUS; + }else if (effectMode == "Halloween") { + mode = FX_MODE_HALLOWEEN; + }else if (effectMode == "Bicolor Chase") { + mode = FX_MODE_BICOLOR_CHASE; + }else if (effectMode == "Tricolor Chase") { + mode = FX_MODE_TRICOLOR_CHASE; + }else if (effectMode == "ICU") { + mode = FX_MODE_ICU; + }else if (effectMode == "Custom 0") { + mode = FX_MODE_CUSTOM_0; + }else if (effectMode == "Custom 1") { + mode = FX_MODE_CUSTOM_1; + }else if (effectMode == "Custom 2") { + mode = FX_MODE_CUSTOM_2; + }else if (effectMode == "Custom 3") { + mode = FX_MODE_CUSTOM_3; + } + + qCDebug(dcWs2812fx()) << "Set mode" << effectMode << mode; + NymeaLightInterfaceReply *reply = light->setEffect(mode); + connect(info, &ThingActionInfo::aborted, reply, &NymeaLightInterfaceReply::finished); + connect(reply, &NymeaLightInterfaceReply::finished, this, [=](){ + if (reply->status() != NymeaLightInterface::StatusSuccess) { + info->finish(Thing::ThingErrorHardwareFailure); + return; + } + + qCDebug(dcWs2812fx()) << "Set mode finished successfully" << effectMode << mode; + thing->setStateValue(ws2812fxEffectModeStateTypeId, effectMode); + info->finish(Thing::ThingErrorNoError); + }); } - -// QByteArray command; -// if (action.actionTypeId() == ws2812fxPowerActionTypeId) { -// command.append("b "); -// if (action.param(ws2812fxPowerActionPowerParamTypeId).value().toBool()) { -// command.append("30"); -// } else { -// command.append("0"); -// } -// command.append("\r\n"); -// return sendCommand(info, command, CommandType::Brightness); -// } - -// if (action.actionTypeId() == ws2812fxBrightnessActionTypeId) { - -// command.append("b "); -// command.append(action.param(ws2812fxBrightnessActionBrightnessParamTypeId).value().toString()); -// command.append("\r\n"); -// return sendCommand(info, command, CommandType::Brightness); -// } - -// if (action.actionTypeId() == ws2812fxSpeedActionTypeId) { - -// command.append("s "); -// command.append(action.param(ws2812fxSpeedActionSpeedParamTypeId).value().toString()); -// command.append("\r\n"); -// return sendCommand(info, command, CommandType::Speed); -// } - -// if (action.actionTypeId() == ws2812fxColorActionTypeId) { - -// QColor color; -// color= action.param(ws2812fxColorActionColorParamTypeId).value().value(); -// command.append("c "); -// command.append(QString(color.name()).remove("#")); -// command.append("\r\n"); -// return sendCommand(info, command, CommandType::Color); -// } - -// if (action.actionTypeId() == ws2812fxColorTemperatureActionTypeId) { - -// // minValue 153, maxValue 500 -// QColor color; -// color.setRgb(255, 255, static_cast((255.00-(((action.param(ws2812fxColorTemperatureActionColorTemperatureParamTypeId).value().toDouble()-153.00)/347.00))*255.00))); -// thing->setStateValue(ws2812fxColorTemperatureStateTypeId, action.param(ws2812fxColorTemperatureActionColorTemperatureParamTypeId).value()); -// command.append("c "); -// command.append(QString(color.name()).remove("#")); -// command.append("\r\n"); -// return sendCommand(info, command, CommandType::Color); -// } - -// if (action.actionTypeId() == ws2812fxEffectModeActionTypeId) { - -// QString effectMode = action.param(ws2812fxEffectModeActionEffectModeParamTypeId).value().toString(); -// command.append("m "); -// if (effectMode == "Static") { -// command.append(QString::number(FX_MODE_STATIC)); -// } else if (effectMode == "Blink") { -// command.append(QString::number(FX_MODE_BLINK)); -// } else if (effectMode == "Color Wipe") { -// command.append(QString::number(FX_MODE_COLOR_WIPE)); -// } else if (effectMode == "Color Wipe Inverse") { -// command.append(QString::number(FX_MODE_COLOR_WIPE_INV)); -// } else if (effectMode == "Color Wipe Reverse") { -// command.append(QString::number(FX_MODE_COLOR_WIPE_REV)); -// } else if (effectMode == "Color Wipe Reverse Inverse") { -// command.append(QString::number(FX_MODE_COLOR_WIPE_REV_INV)); -// } else if (effectMode == "Color Wipe Random") { -// command.append(QString::number(FX_MODE_COLOR_WIPE_RANDOM)); -// } else if (effectMode == "Random Color") { -// command.append(QString::number(FX_MODE_RANDOM_COLOR)); -// } else if (effectMode == "Single Dynamic") { -// command.append(QString::number(FX_MODE_SINGLE_DYNAMIC)); -// } else if (effectMode == "Multi Dynamic") { -// command.append(QString::number(FX_MODE_MULTI_DYNAMIC)); -// } else if (effectMode == "Rainbow") { -// command.append(QString::number(FX_MODE_RAINBOW)); -// } else if (effectMode == "Rainbow Cycle") { -// command.append(QString::number(FX_MODE_RAINBOW_CYCLE)); -// } else if (effectMode == "Scan") { -// command.append(QString::number(FX_MODE_SCAN)); -// } else if (effectMode == "Dual Scan") { -// command.append(QString::number(FX_MODE_DUAL_SCAN)); -// } else if (effectMode == "Fade") { -// command.append(QString::number(FX_MODE_FADE)); -// } else if (effectMode == "Theater Chase") { -// command.append(QString::number(FX_MODE_THEATER_CHASE)); -// } else if (effectMode == "Theater Chase Rainbow") { -// command.append(QString::number(FX_MODE_THEATER_CHASE_RAINBOW)); -// } else if (effectMode == "Running Lights") { -// command.append(QString::number(FX_MODE_RUNNING_LIGHTS)); -// } else if (effectMode == "Twinkle") { -// command.append(QString::number(FX_MODE_TWINKLE)); -// } else if (effectMode == "Twinkle Random") { -// command.append(QString::number(FX_MODE_TWINKLE_RANDOM)); -// } else if (effectMode == "Twinkle Fade") { -// command.append(QString::number(FX_MODE_TWINKLE_FADE)); -// } else if (effectMode == "Twinkle Fade Random") { -// command.append(QString::number(FX_MODE_TWINKLE_FADE_RANDOM)); -// } else if (effectMode == "Sparkle") { -// command.append(QString::number(FX_MODE_SPARKLE)); -// } else if (effectMode == "Flash Sparkle") { -// command.append(QString::number(FX_MODE_FLASH_SPARKLE)); -// } else if (effectMode == "Hyper Sparkle") { -// command.append(QString::number(FX_MODE_HYPER_SPARKLE)); -// } else if (effectMode == "Strobe") { -// command.append(QString::number(FX_MODE_STROBE)); -// } else if (effectMode == "Strobe Rainbow") { -// command.append(QString::number(FX_MODE_STROBE_RAINBOW)); -// } else if (effectMode == "Multi Strobe") { -// command.append(QString::number(FX_MODE_MULTI_STROBE)); -// } else if (effectMode == "Blink Rainbow") { -// command.append(QString::number(FX_MODE_BLINK_RAINBOW)); -// } else if (effectMode == "Chase White") { -// command.append(QString::number(FX_MODE_CHASE_WHITE)); -// } else if (effectMode == "Chase Color") { -// command.append(QString::number(FX_MODE_CHASE_COLOR)); -// } else if (effectMode == "Chase Random") { -// command.append(QString::number(FX_MODE_CHASE_RANDOM)); -// } else if (effectMode == "Chase Flash") { -// command.append(QString::number(FX_MODE_CHASE_FLASH)); -// } else if (effectMode == "Chase Flash Random") { -// command.append(QString::number(FX_MODE_CHASE_FLASH_RANDOM)); -// } else if (effectMode == "Chase Rainbow White") { -// command.append(QString::number(FX_MODE_CHASE_RAINBOW_WHITE)); -// } else if (effectMode == "Chase Blackout") { -// command.append(QString::number(FX_MODE_CHASE_BLACKOUT)); -// } else if (effectMode == "Chase Blackout Rainbow") { -// command.append(QString::number(FX_MODE_CHASE_BLACKOUT_RAINBOW)); -// } else if (effectMode == "Color Sweep Random") { -// command.append(QString::number(FX_MODE_COLOR_SWEEP_RANDOM)); -// } else if (effectMode == "Running Color") { -// command.append(QString::number(FX_MODE_RUNNING_COLOR)); -// } else if (effectMode == "Running Red Blue") { -// command.append(QString::number(FX_MODE_RUNNING_RED_BLUE)); -// } else if (effectMode == "Running Random") { -// command.append(QString::number(FX_MODE_RUNNING_RANDOM)); -// }else if (effectMode == "Larson Scanner") { -// command.append(QString::number(FX_MODE_LARSON_SCANNER)); -// }else if (effectMode == "Comet") { -// command.append(QString::number(FX_MODE_COMET)); -// }else if (effectMode == "Fireworks") { -// command.append(QString::number(FX_MODE_FIREWORKS)); -// }else if (effectMode == "Fireworks Random") { -// command.append(QString::number(FX_MODE_FIREWORKS_RANDOM)); -// }else if (effectMode == "Merry Christmas") { -// command.append(QString::number(FX_MODE_MERRY_CHRISTMAS)); -// }else if (effectMode == "Fire Flicker") { -// command.append(QString::number(FX_MODE_FIRE_FLICKER)); -// }else if (effectMode == "Fire Flicker (soft)") { -// command.append(QString::number(FX_MODE_FIRE_FLICKER_SOFT)); -// }else if (effectMode == "Fire Flicker (intense)") { -// command.append(QString::number(FX_MODE_FIRE_FLICKER_INTENSE)); -// }else if (effectMode == "Circus Combustus") { -// command.append(QString::number(FX_MODE_CIRCUS_COMBUSTUS)); -// }else if (effectMode == "Halloween") { -// command.append(QString::number(FX_MODE_HALLOWEEN)); -// }else if (effectMode == "Bicolor Chase") { -// command.append(QString::number(FX_MODE_BICOLOR_CHASE)); -// }else if (effectMode == "Tricolor Chase") { -// command.append(QString::number(FX_MODE_TRICOLOR_CHASE)); -// }else if (effectMode == "ICU") { -// command.append(QString::number(FX_MODE_ICU)); -// }else if (effectMode == "Custom 0") { -// command.append(QString::number(FX_MODE_CUSTOM_0)); -// }else if (effectMode == "Custom 1") { -// command.append(QString::number(FX_MODE_CUSTOM_1)); -// }else if (effectMode == "Custom 2") { -// command.append(QString::number(FX_MODE_CUSTOM_2)); -// }else if (effectMode == "Custom 3") { -// command.append(QString::number(FX_MODE_CUSTOM_3)); -// } -// command.append("\r\n"); -// return sendCommand(info, command, CommandType::Mode); -// } } diff --git a/ws2812fx/integrationpluginws2812fx.json b/ws2812fx/integrationpluginws2812fx.json index 07cbe7d7..c7de2fd6 100644 --- a/ws2812fx/integrationpluginws2812fx.json +++ b/ws2812fx/integrationpluginws2812fx.json @@ -100,6 +100,7 @@ "displayNameAction": "Set effect mode", "type": "QString", "defaultValue": "Static", + "writable": true, "possibleValues": [ "Static", "Blink", @@ -161,8 +162,7 @@ "Custom 1", "Custom 2", "Custom 3" - ], - "writable": true + ] } ] } diff --git a/ws2812fx/nymealight.cpp b/ws2812fx/nymealight.cpp index 7ccce8e1..a92d11b5 100644 --- a/ws2812fx/nymealight.cpp +++ b/ws2812fx/nymealight.cpp @@ -8,8 +8,8 @@ NymeaLight::NymeaLight(NymeaLightInterface *interface, QObject *parent) : m_interface(interface) { connect(m_interface, &NymeaLightInterface::availableChanged, this, [=](bool available){ - qCDebug(dcWs2812fx()) << "Interface available changed" << available; - emit availableChanged(available); + qCDebug(dcWs2812fx()) << "Interface available changed" << available; + emit availableChanged(available); }); connect(m_interface, &NymeaLightInterface::dataReceived, this, &NymeaLight::onDataReceived); @@ -22,20 +22,61 @@ NymeaLightInterfaceReply *NymeaLight::setColor(const QColor &color, quint16 fade QDataStream stream(&requestData, QIODevice::WriteOnly); stream << static_cast(NymeaLightInterface::CommandSetColor); stream << m_requestId++; - if (fadeDuration > 0) { - stream << static_cast(NymeaLightInterface::ModeFade); - } + stream << fadeDuration; stream << static_cast(color.red()); stream << static_cast(color.green()); stream << static_cast(color.blue()); - if (fadeDuration > 0) { - stream << fadeDuration; - } NymeaLightInterfaceReply *reply = createReply(requestData); m_pendingRequests.enqueue(reply); sendNextRequest(); + return reply; +} +NymeaLightInterfaceReply *NymeaLight::setBrightness(quint8 brightness, quint16 fadeDuration) +{ + // Build the request + QByteArray requestData; + QDataStream stream(&requestData, QIODevice::WriteOnly); + stream << static_cast(NymeaLightInterface::CommandSetBrightness); + stream << m_requestId++; + stream << fadeDuration; + stream << static_cast(brightness); + + NymeaLightInterfaceReply *reply = createReply(requestData); + m_pendingRequests.enqueue(reply); + sendNextRequest(); + return reply; +} + +NymeaLightInterfaceReply *NymeaLight::setSpeed(quint16 speed, quint16 fadeDuration) +{ + // Build the request + QByteArray requestData; + QDataStream stream(&requestData, QIODevice::WriteOnly); + stream << static_cast(NymeaLightInterface::CommandSetSpeed); + stream << m_requestId++; + stream << fadeDuration; + stream << speed; + + NymeaLightInterfaceReply *reply = createReply(requestData); + m_pendingRequests.enqueue(reply); + sendNextRequest(); + return reply; +} + +NymeaLightInterfaceReply *NymeaLight::setEffect(quint8 effect) +{ + // Build the request + QByteArray requestData; + QDataStream stream(&requestData, QIODevice::WriteOnly); + stream << static_cast(NymeaLightInterface::CommandSetEffect); + stream << m_requestId++; + stream << effect; + + NymeaLightInterfaceReply *reply = createReply(requestData); + m_pendingRequests.enqueue(reply); + sendNextRequest(); return reply; } @@ -47,7 +88,13 @@ bool NymeaLight::available() const NymeaLightInterfaceReply *NymeaLight::createReply(const QByteArray &requestData) { NymeaLightInterfaceReply *reply = new NymeaLightInterfaceReply(requestData, this); - connect(reply, &NymeaLightInterfaceReply::finished, reply, &NymeaLightInterfaceReply::deleteLater); + connect(reply, &NymeaLightInterfaceReply::finished, reply, [=](){ + reply->deleteLater(); + if (reply == m_currentReply) { + m_currentReply = nullptr; + } + sendNextRequest(); + }); return reply; } @@ -64,25 +111,36 @@ void NymeaLight::sendNextRequest() m_currentReply = m_pendingRequests.dequeue(); qCDebug(dcWs2812fx()) << "Sending request" << m_currentReply->command() << m_currentReply->requestId() << m_currentReply->requestData().toHex(); m_interface->sendData(m_currentReply->requestData()); + m_currentReply->m_timer->start(); } void NymeaLight::onDataReceived(const QByteArray &data) { - qCDebug(dcWs2812fx()) << "Recived data" << data; Q_ASSERT(data.length() >= 3); - NymeaLightInterface::Command command = static_cast(data.at(0)); + NymeaLightInterface::Command command = static_cast(static_cast((data.at(0)))); quint8 requestId = static_cast(data.at(1)); + //qCDebug(dcWs2812fx()) << "Recived data" << command << requestId << data.toHex(); + + if (command == NymeaLightInterface::CommandDebug) { + qCDebug(dcWs2812fx()) << "Firmware debug:" << QString::fromUtf8(data.right(data.length() - 2)); + return; + } + NymeaLightInterface::Status status = static_cast(data.at(2)); if (m_currentReply) { if (m_currentReply->command() == command && m_currentReply->requestId() == requestId) { + m_currentReply->m_timer->stop(); m_currentReply->m_status = status; - qCDebug(dcWs2812fx()) << "Request finished" << command << m_currentReply->requestId() << status; - emit m_currentReply->finished(); - m_currentReply = nullptr; - sendNextRequest(); + if (status != NymeaLightInterface::StatusSuccess) { + qCWarning(dcWs2812fx()) << "Request finished with error" << command << m_currentReply->requestId() << status; + } else { + qCDebug(dcWs2812fx()) << "Request finished" << command << m_currentReply->requestId() << status; + } + + emit m_currentReply->finished(); } } else { qCWarning(dcWs2812fx()) << "Received unhandled data" << data.toHex(); diff --git a/ws2812fx/nymealight.h b/ws2812fx/nymealight.h index 02e154ee..9f5584c7 100644 --- a/ws2812fx/nymealight.h +++ b/ws2812fx/nymealight.h @@ -16,6 +16,9 @@ public: // Set the color. If fade duration is 0, the color will be set immediatly, // otherwise it will fade to the color with the given fade duration NymeaLightInterfaceReply *setColor(const QColor &color, quint16 fadeDuration = 0); + NymeaLightInterfaceReply *setBrightness(quint8 brightness, quint16 fadeDuration = 0); + NymeaLightInterfaceReply *setSpeed(quint16 speed, quint16 fadeDuration = 0); + NymeaLightInterfaceReply *setEffect(quint8 effect); bool available() const; diff --git a/ws2812fx/nymealightinterface.h b/ws2812fx/nymealightinterface.h index 74c215b3..48dc0399 100644 --- a/ws2812fx/nymealightinterface.h +++ b/ws2812fx/nymealightinterface.h @@ -2,6 +2,7 @@ #define NYMEALIGHTINTERFACE_H #include +#include class NymeaLightInterface : public QObject { @@ -12,6 +13,7 @@ public: CommandSetBrightness = 0x01, CommandSetSpeed = 0x02, CommandSetEffect = 0x03, + CommandDebug = 0xFE, CommandCustom = 0xFF }; Q_ENUM(Command) @@ -21,6 +23,7 @@ public: StatusInvalidProtocol = 0x01, StatusInvalidCommand = 0x02, StatusInvalidPlayload = 0x03, + StatusTimeout = 0xfe, StatusUnknownError = 0xff }; Q_ENUM(Status) @@ -45,6 +48,7 @@ signals: }; + class NymeaLightInterfaceReply : public QObject { Q_OBJECT @@ -62,14 +66,24 @@ signals: private: explicit NymeaLightInterfaceReply(const QByteArray &requestData, QObject *parent = nullptr) : QObject(parent), m_requestData(requestData) { + Q_ASSERT(m_requestData.length() >= 2); m_command = static_cast(m_requestData.at(0)); m_requestId = static_cast(m_requestData.at(1)); + + m_timer = new QTimer(this); + m_timer->setInterval(2000); + m_timer->setSingleShot(true); + connect(m_timer, &QTimer::timeout, this, [=](){ + m_status = NymeaLightInterface::StatusTimeout; + emit finished(); + }); } + QTimer *m_timer = nullptr; QByteArray m_requestData; NymeaLightInterface::Command m_command; - quint8 m_requestId; + quint8 m_requestId = 0; NymeaLightInterface::Status m_status = NymeaLightInterface::StatusUnknownError; }; diff --git a/ws2812fx/nymealightserialinterface.cpp b/ws2812fx/nymealightserialinterface.cpp index ad6126ec..0ab8e928 100644 --- a/ws2812fx/nymealightserialinterface.cpp +++ b/ws2812fx/nymealightserialinterface.cpp @@ -56,10 +56,10 @@ bool NymeaLightSerialInterface::available() void NymeaLightSerialInterface::sendData(const QByteArray &data) { // Stream bytes using SLIP - qCDebug(dcWs2812fx()) << "Write data" << data.toHex(); - QByteArray message; QDataStream stream(&message, QIODevice::WriteOnly); + stream << static_cast(SlipProtocolEnd); + for (int i = 0; i < data.length(); i++) { quint8 dataByte = data.at(i); switch (dataByte) { @@ -76,6 +76,9 @@ void NymeaLightSerialInterface::sendData(const QByteArray &data) break; } } + stream << static_cast(SlipProtocolEnd); + + qCDebug(dcWs2812fx()) << "UART -->" << message.toHex(); m_serialPort->write(message); m_serialPort->flush(); } @@ -83,7 +86,6 @@ void NymeaLightSerialInterface::sendData(const QByteArray &data) void NymeaLightSerialInterface::onReadyRead() { QByteArray data = m_serialPort->readAll(); - qCDebug(dcWs2812fx()) << "Received data" << data.toHex(); for (int i = 0; i < data.length(); i++) { quint8 receivedByte = data.at(i); @@ -106,7 +108,10 @@ void NymeaLightSerialInterface::onReadyRead() switch (receivedByte) { case SlipProtocolEnd: // We are done with this package, process it and reset the buffer - emit dataReceived(m_buffer); + if (!m_buffer.isEmpty() && m_buffer.length() >= 3) { + qCDebug(dcWs2812fx()) << "UART <--" << m_buffer.toHex(); + emit dataReceived(m_buffer); + } m_buffer.clear(); m_protocolEscaping = false; break; diff --git a/ws2812fx/nymealightserialinterface.h b/ws2812fx/nymealightserialinterface.h index d7fe3421..2def124a 100644 --- a/ws2812fx/nymealightserialinterface.h +++ b/ws2812fx/nymealightserialinterface.h @@ -34,6 +34,7 @@ private: private slots: void onReadyRead(); void onSerialError(QSerialPort::SerialPortError error); + }; #endif // NYMEALIGHTSERIALINTERFACE_H