mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-10-25 14:02:37 +01:00
Add nymea light classes
This commit is contained in:
parent
17dc0f7186
commit
274668ed60
92
ws2812fx/nymealight.cpp
Normal file
92
ws2812fx/nymealight.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#include "nymealight.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
NymeaLight::NymeaLight(NymeaLightInterface *interface, QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_interface(interface)
|
||||||
|
{
|
||||||
|
connect(m_interface, &NymeaLightInterface::availableChanged, this, [=](bool available){
|
||||||
|
qCDebug(dcWs2812fx()) << "Interface available changed" << available;
|
||||||
|
emit availableChanged(available);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_interface, &NymeaLightInterface::dataReceived, this, &NymeaLight::onDataReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
NymeaLightInterfaceReply *NymeaLight::setColor(const QColor &color, quint16 fadeDuration)
|
||||||
|
{
|
||||||
|
// Build the request
|
||||||
|
QByteArray requestData;
|
||||||
|
QDataStream stream(&requestData, QIODevice::WriteOnly);
|
||||||
|
stream << static_cast<quint8>(NymeaLightInterface::CommandSetColor);
|
||||||
|
stream << m_requestId++;
|
||||||
|
if (fadeDuration > 0) {
|
||||||
|
stream << static_cast<quint8>(NymeaLightInterface::ModeFade);
|
||||||
|
}
|
||||||
|
stream << static_cast<quint8>(color.red());
|
||||||
|
stream << static_cast<quint8>(color.green());
|
||||||
|
stream << static_cast<quint8>(color.blue());
|
||||||
|
if (fadeDuration > 0) {
|
||||||
|
stream << fadeDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
NymeaLightInterfaceReply *reply = createReply(requestData);
|
||||||
|
m_pendingRequests.enqueue(reply);
|
||||||
|
sendNextRequest();
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NymeaLight::available() const
|
||||||
|
{
|
||||||
|
return m_interface->available();
|
||||||
|
}
|
||||||
|
|
||||||
|
NymeaLightInterfaceReply *NymeaLight::createReply(const QByteArray &requestData)
|
||||||
|
{
|
||||||
|
NymeaLightInterfaceReply *reply = new NymeaLightInterfaceReply(requestData, this);
|
||||||
|
connect(reply, &NymeaLightInterfaceReply::finished, reply, &NymeaLightInterfaceReply::deleteLater);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NymeaLight::sendNextRequest()
|
||||||
|
{
|
||||||
|
if (m_currentReply)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_pendingRequests.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: if not available, finish all replies with unknown error
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
void NymeaLight::onDataReceived(const QByteArray &data)
|
||||||
|
{
|
||||||
|
qCDebug(dcWs2812fx()) << "Recived data" << data;
|
||||||
|
Q_ASSERT(data.length() >= 3);
|
||||||
|
|
||||||
|
NymeaLightInterface::Command command = static_cast<NymeaLightInterface::Command>(data.at(0));
|
||||||
|
quint8 requestId = static_cast<quint8>(data.at(1));
|
||||||
|
NymeaLightInterface::Status status = static_cast<NymeaLightInterface::Status>(data.at(2));
|
||||||
|
|
||||||
|
if (m_currentReply) {
|
||||||
|
if (m_currentReply->command() == command && m_currentReply->requestId() == requestId) {
|
||||||
|
m_currentReply->m_status = status;
|
||||||
|
qCDebug(dcWs2812fx()) << "Request finished" << command << m_currentReply->requestId() << status;
|
||||||
|
emit m_currentReply->finished();
|
||||||
|
|
||||||
|
m_currentReply = nullptr;
|
||||||
|
sendNextRequest();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcWs2812fx()) << "Received unhandled data" << data.toHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
41
ws2812fx/nymealight.h
Normal file
41
ws2812fx/nymealight.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef NYMEALIGHT_H
|
||||||
|
#define NYMEALIGHT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QQueue>
|
||||||
|
|
||||||
|
#include "nymealightinterface.h"
|
||||||
|
|
||||||
|
class NymeaLight : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit NymeaLight(NymeaLightInterface *interface, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
bool available() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
NymeaLightInterface *m_interface = nullptr;
|
||||||
|
quint8 m_requestId = 0;
|
||||||
|
|
||||||
|
NymeaLightInterfaceReply *m_currentReply = nullptr;
|
||||||
|
QQueue<NymeaLightInterfaceReply *> m_pendingRequests;
|
||||||
|
|
||||||
|
NymeaLightInterfaceReply *createReply(const QByteArray &requestData);
|
||||||
|
void sendNextRequest();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onDataReceived(const QByteArray &data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void availableChanged(bool available);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // NYMEALIGHT_H
|
||||||
6
ws2812fx/nymealightinterface.cpp
Normal file
6
ws2812fx/nymealightinterface.cpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#include "nymealightinterface.h"
|
||||||
|
|
||||||
|
NymeaLightInterface::NymeaLightInterface(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
76
ws2812fx/nymealightinterface.h
Normal file
76
ws2812fx/nymealightinterface.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#ifndef NYMEALIGHTINTERFACE_H
|
||||||
|
#define NYMEALIGHTINTERFACE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class NymeaLightInterface : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum Command {
|
||||||
|
CommandSetColor = 0x00,
|
||||||
|
CommandSetBrightness = 0x01,
|
||||||
|
CommandSetSpeed = 0x02,
|
||||||
|
CommandSetEffect = 0x03,
|
||||||
|
CommandCustom = 0xFF
|
||||||
|
};
|
||||||
|
Q_ENUM(Command)
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
StatusSuccess = 0x00,
|
||||||
|
StatusInvalidProtocol = 0x01,
|
||||||
|
StatusInvalidCommand = 0x02,
|
||||||
|
StatusInvalidPlayload = 0x03,
|
||||||
|
StatusUnknownError = 0xff
|
||||||
|
};
|
||||||
|
Q_ENUM(Status)
|
||||||
|
|
||||||
|
enum Mode {
|
||||||
|
ModeDirect = 0x00,
|
||||||
|
ModeFade = 0x01
|
||||||
|
};
|
||||||
|
Q_ENUM(Mode)
|
||||||
|
|
||||||
|
explicit NymeaLightInterface(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
virtual bool open() = 0;
|
||||||
|
virtual void close() = 0;
|
||||||
|
virtual bool available() = 0;
|
||||||
|
|
||||||
|
virtual void sendData(const QByteArray &data) = 0;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void availableChanged(bool available);
|
||||||
|
void dataReceived(const QByteArray &data);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class NymeaLightInterfaceReply : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class NymeaLight;
|
||||||
|
|
||||||
|
public:
|
||||||
|
QByteArray requestData() const { return m_requestData; };
|
||||||
|
NymeaLightInterface::Command command() const { return m_command; };
|
||||||
|
quint8 requestId() const { return m_requestId; };
|
||||||
|
NymeaLightInterface::Status status() const { return m_status; };
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit NymeaLightInterfaceReply(const QByteArray &requestData, QObject *parent = nullptr) : QObject(parent), m_requestData(requestData) {
|
||||||
|
Q_ASSERT(m_requestData.length() >= 2);
|
||||||
|
m_command = static_cast<NymeaLightInterface::Command>(m_requestData.at(0));
|
||||||
|
m_requestId = static_cast<quint8>(m_requestData.at(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray m_requestData;
|
||||||
|
NymeaLightInterface::Command m_command;
|
||||||
|
quint8 m_requestId;
|
||||||
|
NymeaLightInterface::Status m_status = NymeaLightInterface::StatusUnknownError;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NYMEALIGHTINTERFACE_H
|
||||||
133
ws2812fx/nymealightserialinterface.cpp
Normal file
133
ws2812fx/nymealightserialinterface.cpp
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include "nymealightserialinterface.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
|
NymeaLightSerialInterface::NymeaLightSerialInterface(const QString &name, QObject *parent) :
|
||||||
|
NymeaLightInterface(parent)
|
||||||
|
{
|
||||||
|
m_serialPort = new QSerialPort(name, this);
|
||||||
|
m_serialPort->setBaudRate(115200);
|
||||||
|
m_serialPort->setDataBits(QSerialPort::DataBits::Data8);
|
||||||
|
m_serialPort->setParity(QSerialPort::Parity::NoParity);
|
||||||
|
m_serialPort->setStopBits(QSerialPort::StopBits::OneStop);
|
||||||
|
m_serialPort->setFlowControl(QSerialPort::FlowControl::NoFlowControl);
|
||||||
|
|
||||||
|
connect(m_serialPort, &QSerialPort::readyRead, this, &NymeaLightSerialInterface::onReadyRead);
|
||||||
|
connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onSerialError(QSerialPort::SerialPortError)));
|
||||||
|
|
||||||
|
m_reconnectTimer = new QTimer(this);
|
||||||
|
m_reconnectTimer->setInterval(5000);
|
||||||
|
m_reconnectTimer->setSingleShot(false);
|
||||||
|
connect(m_reconnectTimer, &QTimer::timeout, this, [=](){
|
||||||
|
if (m_serialPort->isOpen()) {
|
||||||
|
m_reconnectTimer->stop();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if (open()) {
|
||||||
|
m_reconnectTimer->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NymeaLightSerialInterface::open()
|
||||||
|
{
|
||||||
|
if (!m_serialPort->open(QIODevice::ReadWrite)) {
|
||||||
|
qCWarning(dcWs2812fx()) << "Could not open serial port" << m_serialPort->portName() << m_serialPort->errorString();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit availableChanged(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NymeaLightSerialInterface::close()
|
||||||
|
{
|
||||||
|
m_serialPort->close();
|
||||||
|
emit availableChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NymeaLightSerialInterface::available()
|
||||||
|
{
|
||||||
|
return m_serialPort->isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NymeaLightSerialInterface::sendData(const QByteArray &data)
|
||||||
|
{
|
||||||
|
// Stream bytes using SLIP
|
||||||
|
qCDebug(dcWs2812fx()) << "Write data" << data.toHex();
|
||||||
|
|
||||||
|
QByteArray message;
|
||||||
|
QDataStream stream(&message, QIODevice::WriteOnly);
|
||||||
|
for (int i = 0; i < data.length(); i++) {
|
||||||
|
quint8 dataByte = data.at(i);
|
||||||
|
switch (dataByte) {
|
||||||
|
case SlipProtocolEnd:
|
||||||
|
stream << static_cast<quint8>(SlipProtocolEsc);
|
||||||
|
stream << static_cast<quint8>(SlipProtocolTransposedEnd);
|
||||||
|
break;
|
||||||
|
case SlipProtocolEsc:
|
||||||
|
stream << static_cast<quint8>(SlipProtocolEsc);
|
||||||
|
stream << static_cast<quint8>(SlipProtocolTransposedEsc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
stream << dataByte;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_serialPort->write(message);
|
||||||
|
m_serialPort->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (m_protocolEscaping) {
|
||||||
|
switch (receivedByte) {
|
||||||
|
case SlipProtocolTransposedEnd:
|
||||||
|
m_buffer.append(static_cast<quint8>(SlipProtocolEnd));
|
||||||
|
m_protocolEscaping = false;
|
||||||
|
break;
|
||||||
|
case SlipProtocolTransposedEsc:
|
||||||
|
m_buffer.append(static_cast<quint8>(SlipProtocolEsc));
|
||||||
|
m_protocolEscaping = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// SLIP protocol violation...received escape, but it is not an escaped byte
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (receivedByte) {
|
||||||
|
case SlipProtocolEnd:
|
||||||
|
// We are done with this package, process it and reset the buffer
|
||||||
|
emit dataReceived(m_buffer);
|
||||||
|
m_buffer.clear();
|
||||||
|
m_protocolEscaping = false;
|
||||||
|
break;
|
||||||
|
case SlipProtocolEsc:
|
||||||
|
// The next byte will be escaped, lets wait for it
|
||||||
|
m_protocolEscaping = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Nothing special, just add to buffer
|
||||||
|
m_buffer.append(receivedByte);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NymeaLightSerialInterface::onSerialError(QSerialPort::SerialPortError error)
|
||||||
|
{
|
||||||
|
if (error != QSerialPort::NoError && m_serialPort->isOpen()) {
|
||||||
|
qCCritical(dcWs2812fx()) << "Serial port error:" << error << m_serialPort->errorString();
|
||||||
|
m_reconnectTimer->start();
|
||||||
|
m_serialPort->close();
|
||||||
|
emit availableChanged(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
ws2812fx/nymealightserialinterface.h
Normal file
39
ws2812fx/nymealightserialinterface.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef NYMEALIGHTSERIALINTERFACE_H
|
||||||
|
#define NYMEALIGHTSERIALINTERFACE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSerialPort>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "nymealightinterface.h"
|
||||||
|
|
||||||
|
class NymeaLightSerialInterface : public NymeaLightInterface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit NymeaLightSerialInterface(const QString &name, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
bool open() override;
|
||||||
|
void close() override;
|
||||||
|
bool available() override;
|
||||||
|
void sendData(const QByteArray &data) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum SlipProtocol {
|
||||||
|
SlipProtocolEnd = 0xC0,
|
||||||
|
SlipProtocolEsc = 0xDB,
|
||||||
|
SlipProtocolTransposedEnd = 0xDC,
|
||||||
|
SlipProtocolTransposedEsc = 0xDD
|
||||||
|
};
|
||||||
|
|
||||||
|
QTimer *m_reconnectTimer = nullptr;
|
||||||
|
QSerialPort *m_serialPort = nullptr;
|
||||||
|
QByteArray m_buffer;
|
||||||
|
bool m_protocolEscaping = false;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onReadyRead();
|
||||||
|
void onSerialError(QSerialPort::SerialPortError error);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NYMEALIGHTSERIALINTERFACE_H
|
||||||
Loading…
x
Reference in New Issue
Block a user