mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-07-20 01:02:46 +02: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