mirror of
https://github.com/nymea/nymea-plugins.git
synced 2026-07-18 00:43:48 +02:00
Add firmware and first working version
This commit is contained in:
parent
7f8c6977ce
commit
5b766a406a
39
ws2812fx/firmware/include/README
Normal file
39
ws2812fx/firmware/include/README
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the usual convention is to give header files names that end with `.h'.
|
||||
It is most portable to use only letters, digits, dashes, and underscores in
|
||||
header file names, and at most one dot.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||
1
ws2812fx/firmware/lib/Adafruit_NeoPixel
Submodule
1
ws2812fx/firmware/lib/Adafruit_NeoPixel
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8595ee3f5880d5096d71551c621d7a2d3818f974
|
||||
46
ws2812fx/firmware/lib/README
Normal file
46
ws2812fx/firmware/lib/README
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into executable file.
|
||||
|
||||
The source code of each library should be placed in a an own separate directory
|
||||
("lib/your_library_name/[here are source files]").
|
||||
|
||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
and a contents of `src/main.c`:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||
1
ws2812fx/firmware/lib/WS2812FX
Submodule
1
ws2812fx/firmware/lib/WS2812FX
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 4774866a176eb4ff474f712ce63a06404385e911
|
||||
14
ws2812fx/firmware/platformio.ini
Normal file
14
ws2812fx/firmware/platformio.ini
Normal file
@ -0,0 +1,14 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:uno]
|
||||
platform = atmelavr
|
||||
board = uno
|
||||
framework = arduino
|
||||
20
ws2812fx/firmware/src/main.cpp
Normal file
20
ws2812fx/firmware/src/main.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "nymealight.h"
|
||||
|
||||
// TEST
|
||||
NymeaLight *light = nullptr;
|
||||
|
||||
void setup() {
|
||||
// Get rid of unused warning from lib
|
||||
(void)&_modes;
|
||||
|
||||
light = new NymeaLight(Serial, new WS2812FX(10, 13, NEO_GRB + NEO_KHZ800));
|
||||
light->init();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
light->process();
|
||||
}
|
||||
|
||||
|
||||
330
ws2812fx/firmware/src/nymealight.cpp
Normal file
330
ws2812fx/firmware/src/nymealight.cpp
Normal file
@ -0,0 +1,330 @@
|
||||
#include "nymealight.h"
|
||||
|
||||
NymeaLight::NymeaLight(WS2812FX *strip) :
|
||||
m_strip(strip)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
NymeaLight::NymeaLight(SoftwareSerial *serial, WS2812FX *strip) :
|
||||
m_softwareSerial(serial),
|
||||
m_strip(strip)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
NymeaLight::NymeaLight(HardwareSerial &serial, WS2812FX *strip) :
|
||||
m_hardwareSerial(&serial),
|
||||
m_strip(strip)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
WS2812FX *NymeaLight::strip() const
|
||||
{
|
||||
return m_strip;
|
||||
}
|
||||
|
||||
void NymeaLight::init()
|
||||
{
|
||||
// Get rid of unused warning from lib
|
||||
(void)&_modes;
|
||||
|
||||
// Initialize serial port
|
||||
if (m_hardwareSerial) {
|
||||
m_hardwareSerial->begin(115200);
|
||||
} else if (m_softwareSerial) {
|
||||
m_softwareSerial->begin(115200);
|
||||
}
|
||||
|
||||
// Initialize strip
|
||||
m_strip->init();
|
||||
if (m_power) {
|
||||
m_strip->setBrightness(m_brightness);
|
||||
} else {
|
||||
m_strip->setBrightness(0);
|
||||
}
|
||||
m_strip->setSpeed(m_speed);
|
||||
m_strip->setColor(0xff, 0xff, 0xff);
|
||||
m_strip->setMode(FX_MODE_STATIC);
|
||||
m_strip->start();
|
||||
|
||||
m_previouseTime = millis();
|
||||
sendReadyNotification();
|
||||
}
|
||||
|
||||
void NymeaLight::process()
|
||||
{
|
||||
// Read incoming data if there is any
|
||||
readData();
|
||||
|
||||
//doAnimations();
|
||||
|
||||
// Perform service for WS2812FX
|
||||
m_strip->service();
|
||||
}
|
||||
|
||||
void NymeaLight::doAnimations()
|
||||
{
|
||||
// Perform animation steps every 50 ms
|
||||
uint32_t currentTimestamp = millis();
|
||||
if (m_previouseTime - currentTimestamp > 50000) {
|
||||
//debugPrint("Tick");
|
||||
m_previouseTime = currentTimestamp;
|
||||
|
||||
if (m_brightnessFadeDuration > 0 && m_brightnessTargetValue != m_strip->getBrightness()) {
|
||||
// Calculate step for brightness animation
|
||||
if (m_brightnessStartValue < m_brightnessTargetValue) {
|
||||
m_strip->setBrightness(m_strip->getBrightness() - 1);
|
||||
} else {
|
||||
m_strip->setBrightness(m_strip->getBrightness() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NymeaLight::debugPrint(const char message[])
|
||||
{
|
||||
streamByte(SlipProtocolEnd, true);
|
||||
streamByte(NotificationDebugMessage);
|
||||
streamByte(m_notificationId);
|
||||
m_notificationId++;
|
||||
for (size_t i = 0; i < strlen(message); i++) {
|
||||
streamByte(message[i]);
|
||||
}
|
||||
streamByte(SlipProtocolEnd, true);
|
||||
flushSerial();
|
||||
}
|
||||
|
||||
void NymeaLight::flushSerial()
|
||||
{
|
||||
if (m_hardwareSerial) {
|
||||
m_hardwareSerial->flush();
|
||||
} else if (m_softwareSerial) {
|
||||
m_softwareSerial->flush();
|
||||
}
|
||||
}
|
||||
|
||||
void NymeaLight::sendReadyNotification()
|
||||
{
|
||||
streamByte(SlipProtocolEnd, true);
|
||||
streamByte(NotificationReady);
|
||||
streamByte(m_notificationId);
|
||||
m_notificationId++;
|
||||
streamByte(SlipProtocolEnd, true);
|
||||
flushSerial();
|
||||
}
|
||||
|
||||
void NymeaLight::readData()
|
||||
{
|
||||
if (m_hardwareSerial) {
|
||||
while (m_hardwareSerial->available()) {
|
||||
uint8_t receivedByte = m_hardwareSerial->read();
|
||||
processReceivedByte(receivedByte);
|
||||
}
|
||||
} else if (m_softwareSerial) {
|
||||
while (m_softwareSerial->available()) {
|
||||
uint8_t receivedByte = m_softwareSerial->read();
|
||||
processReceivedByte(receivedByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NymeaLight::processData(uint8_t buffer[], uint8_t length)
|
||||
{
|
||||
uint8_t command = buffer[0];
|
||||
uint8_t requestId = buffer[1];
|
||||
|
||||
switch (command) {
|
||||
case CommandGetStatus: {
|
||||
if (length != 2) {
|
||||
sendResponse(command, requestId, StatusInvalidPlayload);
|
||||
return;
|
||||
}
|
||||
|
||||
sendResponse(command, requestId, StatusSuccess);
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandSetPower: {
|
||||
if (length != 5) {
|
||||
sendResponse(command, requestId, StatusInvalidPlayload);
|
||||
return;
|
||||
}
|
||||
|
||||
// uint16_t fadeDuration = (buffer[2] << 8 ) | buffer[3];
|
||||
uint8_t powerInt = buffer[4];
|
||||
if (powerInt == 0) {
|
||||
m_power = false;
|
||||
} else {
|
||||
m_power = true;
|
||||
}
|
||||
|
||||
if (m_power) {
|
||||
m_strip->setBrightness(m_brightness);
|
||||
} else {
|
||||
m_strip->setBrightness(0);
|
||||
}
|
||||
|
||||
// TODO: animate brightness with fade duration
|
||||
|
||||
sendResponse(command, requestId, StatusSuccess);
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandSetColor: {
|
||||
if (length != 7) {
|
||||
sendResponse(command, requestId, StatusInvalidPlayload);
|
||||
return;
|
||||
}
|
||||
|
||||
//uint32_t currentColor = m_strip->getColor();
|
||||
//uint32_t targetColor = ((uint32_t) << 16) | ((uint32_t)buffer[5] << 8) | buffer[6];
|
||||
// uint16_t fadeDuration = (buffer[2] << 8 ) | buffer[3];
|
||||
uint8_t red = buffer[4];
|
||||
uint8_t green = buffer[5];
|
||||
uint8_t blue = buffer[6];
|
||||
m_strip->setColor(red, green, blue);
|
||||
delayMicroseconds(1000);
|
||||
sendResponse(command, requestId, StatusSuccess);
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandSetBrightness: {
|
||||
if (length != 5) {
|
||||
sendResponse(command, requestId, StatusInvalidPlayload);
|
||||
return;
|
||||
}
|
||||
|
||||
//uint16_t fadeDuration = (buffer[2] << 8 ) | buffer[3];
|
||||
// m_brightnessStartValue = m_strip->getBrightness();
|
||||
|
||||
m_brightness = buffer[4];
|
||||
if (m_power) {
|
||||
m_strip->setBrightness(m_brightness);
|
||||
}
|
||||
|
||||
delayMicroseconds(1000);
|
||||
sendResponse(command, requestId, StatusSuccess);
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandSetSpeed: {
|
||||
if (length != 6) {
|
||||
sendResponse(command, requestId, StatusInvalidPlayload);
|
||||
return;
|
||||
}
|
||||
|
||||
// uint16_t fadeDuration = (buffer[2] << 8 ) | buffer[3];
|
||||
uint16_t speed = (buffer[4] << 8 ) | buffer[5];
|
||||
m_strip->setSpeed(speed);
|
||||
delayMicroseconds(1000);
|
||||
sendResponse(command, requestId, StatusSuccess);
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandSetEffect: {
|
||||
if (length != 3) {
|
||||
sendResponse(command, requestId, StatusInvalidPlayload);
|
||||
return;
|
||||
}
|
||||
|
||||
m_strip->setMode(buffer[2]);
|
||||
delayMicroseconds(1000);
|
||||
sendResponse(command, requestId, StatusSuccess);
|
||||
break;
|
||||
}
|
||||
|
||||
case CommandCustom:
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
sendResponse(command, requestId, StatusInvalidCommand);
|
||||
break;
|
||||
}
|
||||
|
||||
m_strip->show();
|
||||
m_strip->service();
|
||||
}
|
||||
|
||||
void NymeaLight::sendResponse(uint8_t command, uint8_t requestId, Status status)
|
||||
{
|
||||
streamByte(SlipProtocolEnd, true);
|
||||
streamByte(command);
|
||||
streamByte(requestId);
|
||||
streamByte(status);
|
||||
streamByte(SlipProtocolEnd, true);
|
||||
|
||||
flushSerial();
|
||||
}
|
||||
|
||||
void NymeaLight::processReceivedByte(uint8_t receivedByte)
|
||||
{
|
||||
if (m_protocolEscaping) {
|
||||
switch (receivedByte) {
|
||||
case SlipProtocolTransposedEnd:
|
||||
m_buffer[m_bufferIndex++] = SlipProtocolEnd;
|
||||
m_protocolEscaping = false;
|
||||
break;
|
||||
case SlipProtocolTransposedEsc:
|
||||
m_buffer[m_bufferIndex++] = 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
|
||||
if (m_bufferIndex > 0) {
|
||||
processData(m_buffer, m_bufferIndex);
|
||||
}
|
||||
m_bufferIndex = 0;
|
||||
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[m_bufferIndex++] = receivedByte;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NymeaLight::streamByte(uint8_t dataByte, boolean specialCharacter)
|
||||
{
|
||||
// If this is a special character, write it without escaping
|
||||
if (specialCharacter) {
|
||||
writeByte(dataByte);
|
||||
} else {
|
||||
switch (dataByte) {
|
||||
case SlipProtocolEnd:
|
||||
writeByte(SlipProtocolEsc);
|
||||
writeByte(SlipProtocolTransposedEnd);
|
||||
break;
|
||||
case SlipProtocolEsc:
|
||||
writeByte(SlipProtocolEsc);
|
||||
writeByte(SlipProtocolTransposedEsc);
|
||||
break;
|
||||
default:
|
||||
writeByte(dataByte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NymeaLight::writeByte(uint8_t dataByte)
|
||||
{
|
||||
if (m_hardwareSerial) {
|
||||
m_hardwareSerial->write(dataByte);
|
||||
} else if (m_softwareSerial) {
|
||||
m_softwareSerial->write(dataByte);
|
||||
}
|
||||
}
|
||||
166
ws2812fx/firmware/src/nymealight.h
Normal file
166
ws2812fx/firmware/src/nymealight.h
Normal file
@ -0,0 +1,166 @@
|
||||
|
||||
/*
|
||||
SLIP transfere: https://tools.ietf.org/html/rfc1055
|
||||
|
||||
Request package format:
|
||||
--------------------------
|
||||
uint8 : command (< 0xF0)
|
||||
uint8 : requestId
|
||||
uint8[] : payload (dynamic size, max: 253)
|
||||
|
||||
|
||||
Response format:
|
||||
--------------------------
|
||||
uint8 : command (< 0xF0)
|
||||
uint8 : requestId (same as request)
|
||||
uint8 : status
|
||||
uint8[] : payload (optional, max: 253)
|
||||
|
||||
|
||||
Notification package format:
|
||||
--------------------------
|
||||
uint8 : command (>= 0xF0)
|
||||
uint8 : notificationId
|
||||
uint8[] : payload (dynamic size, max: 253)
|
||||
|
||||
|
||||
Commands:
|
||||
--------------------------
|
||||
0x00: get ready status
|
||||
payload: empty
|
||||
|
||||
|
||||
0x01: set power
|
||||
payload:
|
||||
uint16 : fade duration in ms
|
||||
uint8 : power (0x00 = off, 0x01 = on)
|
||||
|
||||
|
||||
0x02: set color
|
||||
payload:
|
||||
uint16 : fade duration in ms
|
||||
uint8 : red
|
||||
uint8 : green
|
||||
uint8 : blue
|
||||
|
||||
|
||||
0x03: set brightness
|
||||
payload:
|
||||
uint16 : fade duration in ms
|
||||
uint8 : brightness (0-255)
|
||||
|
||||
|
||||
0x04: set speed
|
||||
payload:
|
||||
uint16 : fade duration in ms
|
||||
uint16 : speed in ms (big endian)
|
||||
|
||||
|
||||
0x05: set effect
|
||||
payload:
|
||||
uint8 : effect, see effect list from WS2812FX
|
||||
|
||||
|
||||
0xef: custom command
|
||||
The payload can be defined free and custom functionality can be implemented
|
||||
|
||||
|
||||
Notifications:
|
||||
--------------------------
|
||||
0xF0: Status ready notification
|
||||
payload: empty
|
||||
|
||||
|
||||
0xF1: Debug message notification
|
||||
payload: debug message characters
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SoftwareSerial.h>
|
||||
#include <WS2812FX.h>
|
||||
|
||||
class NymeaLight
|
||||
{
|
||||
public:
|
||||
enum Command {
|
||||
CommandGetStatus = 0x00,
|
||||
CommandSetPower = 0x01,
|
||||
CommandSetColor = 0x02,
|
||||
CommandSetBrightness = 0x03,
|
||||
CommandSetSpeed = 0x04,
|
||||
CommandSetEffect = 0x05,
|
||||
CommandCustom = 0xEF
|
||||
};
|
||||
|
||||
enum Notification {
|
||||
NotificationReady = 0xF0,
|
||||
NotificationDebugMessage = 0xF1
|
||||
};
|
||||
|
||||
enum Status {
|
||||
StatusSuccess = 0x00,
|
||||
StatusInvalidProtocol = 0x01,
|
||||
StatusInvalidCommand = 0x02,
|
||||
StatusInvalidPlayload = 0x03,
|
||||
StatusUnknownError = 0xff
|
||||
};
|
||||
|
||||
NymeaLight(WS2812FX *strip);
|
||||
NymeaLight(SoftwareSerial *serial, WS2812FX *strip);
|
||||
NymeaLight(HardwareSerial &serial, WS2812FX *strip);
|
||||
|
||||
WS2812FX *strip() const;
|
||||
|
||||
void init();
|
||||
void process();
|
||||
|
||||
private:
|
||||
enum SlipProtocol {
|
||||
SlipProtocolEnd = 0xC0,
|
||||
SlipProtocolEsc = 0xDB,
|
||||
SlipProtocolTransposedEnd = 0xDC,
|
||||
SlipProtocolTransposedEsc = 0xDD
|
||||
};
|
||||
|
||||
HardwareSerial *m_hardwareSerial = nullptr;
|
||||
SoftwareSerial *m_softwareSerial = nullptr;
|
||||
WS2812FX *m_strip = nullptr;
|
||||
uint8_t m_notificationId = 0;
|
||||
|
||||
// Light states
|
||||
bool m_power = false;
|
||||
uint8_t m_brightness = 0xff;
|
||||
uint32_t m_color = 0x00ffffff;
|
||||
uint8_t m_effect = FX_MODE_STATIC;
|
||||
uint16_t m_speed = 2000;
|
||||
|
||||
// UART read
|
||||
uint8_t m_buffer[255];
|
||||
uint8_t m_bufferIndex = 0;
|
||||
boolean m_protocolEscaping = false;
|
||||
|
||||
// Animations
|
||||
uint32_t m_previouseTime = 0;
|
||||
|
||||
// Brightness animation
|
||||
uint32_t m_brightnessProgress = 0; // us
|
||||
uint8_t m_brightnessStartValue = 0;
|
||||
uint8_t m_brightnessTargetValue = 0;
|
||||
uint8_t m_brightnessFadeDuration = 0; //ms
|
||||
|
||||
void doAnimations();
|
||||
void debugPrint(const char message[]);
|
||||
void flushSerial();
|
||||
void sendReadyNotification();
|
||||
|
||||
protected:
|
||||
virtual void readData();
|
||||
virtual void processReceivedByte(uint8_t receivedByte);
|
||||
virtual void processData(uint8_t buffer[], uint8_t length);
|
||||
virtual void sendResponse(uint8_t command, uint8_t requestId, Status status);
|
||||
virtual void streamByte(uint8_t dataByte, boolean specialCharacter = false);
|
||||
virtual void writeByte(uint8_t dataByte);
|
||||
|
||||
};
|
||||
11
ws2812fx/firmware/test/README
Normal file
11
ws2812fx/firmware/test/README
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Unit Testing and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||
@ -28,22 +28,7 @@
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
/*!
|
||||
\page ws2812fx.html
|
||||
\title WS2812FX Control
|
||||
\brief Plug-In to control WS2812FX over USB
|
||||
|
||||
\ingroup plugins
|
||||
\ingroup nymea-plugins
|
||||
|
||||
\chapter Plugin properties
|
||||
Following JSON file contains the definition and the description of all available \l{ThingClass}{DeviceClasses}
|
||||
and \l{Vendor}{Vendors} of this \l{DevicePlugin}.
|
||||
|
||||
For more details how to read this JSON file please check out the documentation for \l{The plugin JSON File}.
|
||||
s
|
||||
\quotefile plugins/deviceplugins/ws2812fx/devicepluginws2812fx.json
|
||||
*/
|
||||
#include <QColor>
|
||||
#include <QRgb>
|
||||
|
||||
@ -60,34 +45,46 @@ void IntegrationPluginWs2812fx::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
|
||||
QString interface = thing->paramValue(ws2812fxThingSerialPortParamTypeId).toString();
|
||||
if (thing->thingClassId() == nymeaLightSerialThingClassId) {
|
||||
QString interface = thing->paramValue(nymeaLightSerialThingSerialPortParamTypeId).toString();
|
||||
|
||||
if (m_usedInterfaces.contains(interface)) {
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("This serial port is already used."));
|
||||
return;
|
||||
if (m_usedInterfaces.contains(interface)) {
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("This serial port is already in use."));
|
||||
return;
|
||||
}
|
||||
|
||||
NymeaLightSerialInterface *lightInterface = new NymeaLightSerialInterface(interface, thing);
|
||||
NymeaLight *light = new NymeaLight(lightInterface, this);
|
||||
lightInterface->setParent(light);
|
||||
m_usedInterfaces.append(interface);
|
||||
m_lights.insert(thing, light);
|
||||
|
||||
|
||||
connect(light, &NymeaLight::availableChanged, thing, [=](bool available){
|
||||
qCDebug(dcWs2812fx()) << thing << "available changed" << available;
|
||||
thing->setStateValue(nymeaLightSerialConnectedStateTypeId, available);
|
||||
|
||||
if (available) {
|
||||
// Set the light to the current states
|
||||
light->setPower(thing->stateValue(nymeaLightSerialPowerStateTypeId).toBool());
|
||||
light->setBrightness(thing->stateValue(nymeaLightSerialBrightnessStateTypeId).toUInt());
|
||||
light->setColor(thing->stateValue(nymeaLightSerialColorStateTypeId).value<QColor>());
|
||||
light->setEffect(thing->stateValue(nymeaLightSerialEffectModeStateTypeId).toUInt());
|
||||
light->setSpeed(thing->stateValue(nymeaLightSerialSpeedStateTypeId).toUInt());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
NymeaLightSerialInterface *lightInterface = new NymeaLightSerialInterface(interface, thing);
|
||||
NymeaLight *light = new NymeaLight(lightInterface, this);
|
||||
lightInterface->setParent(light);
|
||||
|
||||
if (!lightInterface->open()) {
|
||||
qCWarning(dcWs2812fx()) << "Could not open interface" << interface;
|
||||
light->deleteLater();
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Error opening serial port."));
|
||||
return;
|
||||
void IntegrationPluginWs2812fx::postSetupThing(Thing *thing)
|
||||
{
|
||||
if (thing->thingClassId() == nymeaLightSerialThingClassId) {
|
||||
NymeaLight *light = m_lights.value(thing);
|
||||
light->enable();
|
||||
}
|
||||
|
||||
connect(light, &NymeaLight::availableChanged, thing, [=](bool available){
|
||||
qCDebug(dcWs2812fx()) << thing << "available changed" << available;
|
||||
thing->setStateValue(ws2812fxConnectedStateTypeId, available);
|
||||
});
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Setup successfully serial port" << interface;
|
||||
thing->setStateValue(ws2812fxConnectedStateTypeId, true);
|
||||
m_usedInterfaces.append(interface);
|
||||
m_lights.insert(thing, light);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
|
||||
@ -96,15 +93,14 @@ void IntegrationPluginWs2812fx::discoverThings(ThingDiscoveryInfo *info)
|
||||
// Create the list of available serial interfaces
|
||||
|
||||
Q_FOREACH(QSerialPortInfo port, QSerialPortInfo::availablePorts()) {
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Found serial port:" << port.portName();
|
||||
QString description = port.manufacturer() + " " + port.description();
|
||||
ThingDescriptor descriptor(info->thingClassId(), port.portName(), description);
|
||||
foreach (Thing *existingThing, myThings().filterByParam(ws2812fxThingSerialPortParamTypeId, port.portName())) {
|
||||
QString description = port.systemLocation() + " " + port.manufacturer() + " " + port.description();
|
||||
ThingDescriptor descriptor(info->thingClassId(), QT_TR_NOOP("Nymea light"), description);
|
||||
foreach (Thing *existingThing, myThings().filterByParam(nymeaLightSerialThingSerialPortParamTypeId, port.systemLocation())) {
|
||||
descriptor.setThingId(existingThing->id());
|
||||
}
|
||||
ParamList parameters;
|
||||
parameters.append(Param(ws2812fxThingSerialPortParamTypeId, port.portName()));
|
||||
parameters.append(Param(nymeaLightSerialThingSerialPortParamTypeId, port.systemLocation()));
|
||||
descriptor.setParams(parameters);
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
@ -122,16 +118,11 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info)
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
if (action.actionTypeId() == nymeaLightSerialPowerActionTypeId) {
|
||||
bool power = action.param(nymeaLightSerialPowerActionPowerParamTypeId).value().toBool();
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Set power" << power;
|
||||
NymeaLightInterfaceReply *reply = light->setBrightness(brightness, 500);
|
||||
NymeaLightInterfaceReply *reply = light->setPower(power, 500);
|
||||
connect(info, &ThingActionInfo::aborted, reply, &NymeaLightInterfaceReply::finished);
|
||||
connect(reply, &NymeaLightInterfaceReply::finished, this, [=](){
|
||||
if (reply->status() != NymeaLightInterface::StatusSuccess) {
|
||||
@ -140,11 +131,11 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info)
|
||||
}
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Set power finished successfully" << power;
|
||||
thing->setStateValue(ws2812fxPowerStateTypeId, power);
|
||||
thing->setStateValue(nymeaLightSerialPowerStateTypeId, power);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else if (action.actionTypeId() == ws2812fxColorActionTypeId) {
|
||||
QColor color = action.param(ws2812fxColorActionColorParamTypeId).value().value<QColor>();
|
||||
} else if (action.actionTypeId() == nymeaLightSerialColorActionTypeId) {
|
||||
QColor color = action.param(nymeaLightSerialColorActionColorParamTypeId).value().value<QColor>();
|
||||
qCDebug(dcWs2812fx()) << "Set color to" << color.name(QColor::HexRgb);
|
||||
NymeaLightInterfaceReply *reply = light->setColor(color);
|
||||
connect(info, &ThingActionInfo::aborted, reply, &NymeaLightInterfaceReply::finished);
|
||||
@ -154,12 +145,12 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info)
|
||||
return;
|
||||
}
|
||||
qCDebug(dcWs2812fx()) << "Set color finished successfully" << color.name(QColor::HexRgb);
|
||||
thing->setStateValue(ws2812fxColorStateTypeId, color);
|
||||
thing->setStateValue(nymeaLightSerialColorStateTypeId, color);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else if (action.actionTypeId() == ws2812fxColorTemperatureActionTypeId) {
|
||||
} else if (action.actionTypeId() == nymeaLightSerialColorTemperatureActionTypeId) {
|
||||
// minValue 153, maxValue 500
|
||||
uint colorTemperature = action.param(ws2812fxColorTemperatureActionColorTemperatureParamTypeId).value().toDouble();
|
||||
uint colorTemperature = action.param(nymeaLightSerialColorTemperatureActionColorTemperatureParamTypeId).value().toDouble();
|
||||
QColor color;
|
||||
color.setRgb(255, 255, qRound((255.00 - (((colorTemperature - 153.00) / 347.00)) * 255.00)));
|
||||
|
||||
@ -173,11 +164,11 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info)
|
||||
}
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Set color temperature finished successfully" << colorTemperature;
|
||||
thing->setStateValue(ws2812fxColorTemperatureStateTypeId, colorTemperature);
|
||||
thing->setStateValue(nymeaLightSerialColorTemperatureStateTypeId, colorTemperature);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else if (action.actionTypeId() == ws2812fxBrightnessActionTypeId) {
|
||||
quint8 brightnessPercentage = action.param(ws2812fxBrightnessActionBrightnessParamTypeId).value().toUInt();
|
||||
} else if (action.actionTypeId() == nymeaLightSerialBrightnessActionTypeId) {
|
||||
quint8 brightnessPercentage = action.param(nymeaLightSerialBrightnessActionBrightnessParamTypeId).value().toUInt();
|
||||
quint8 brightness = qRound(255.0 * brightnessPercentage / 100);
|
||||
qCDebug(dcWs2812fx()) << "Set brightness to" << brightnessPercentage << brightness;
|
||||
NymeaLightInterfaceReply *reply = light->setBrightness(brightness, 1000);
|
||||
@ -189,11 +180,11 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info)
|
||||
}
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Set brightness finished successfully" << brightness;
|
||||
thing->setStateValue(ws2812fxBrightnessStateTypeId, brightnessPercentage);
|
||||
thing->setStateValue(nymeaLightSerialBrightnessStateTypeId, brightnessPercentage);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else if (action.actionTypeId() == ws2812fxSpeedActionTypeId) {
|
||||
quint16 speedPercentage = action.param(ws2812fxSpeedActionSpeedParamTypeId).value().toUInt();
|
||||
} else if (action.actionTypeId() == nymeaLightSerialSpeedActionTypeId) {
|
||||
quint16 speedPercentage = action.param(nymeaLightSerialSpeedActionSpeedParamTypeId).value().toUInt();
|
||||
quint16 speed = 2000 - (speedPercentage * 20);
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Set speed" << speedPercentage << "%" << speed << "ms";
|
||||
@ -206,11 +197,11 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info)
|
||||
}
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Set speed finished successfully" << speedPercentage << "%" << speed << "ms";
|
||||
thing->setStateValue(ws2812fxSpeedStateTypeId, speedPercentage);
|
||||
thing->setStateValue(nymeaLightSerialSpeedStateTypeId, speedPercentage);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else if (action.actionTypeId() == ws2812fxEffectModeActionTypeId) {
|
||||
QString effectMode = action.param(ws2812fxEffectModeActionEffectModeParamTypeId).value().toString();
|
||||
} else if (action.actionTypeId() == nymeaLightSerialEffectModeActionTypeId) {
|
||||
QString effectMode = action.param(nymeaLightSerialEffectModeActionEffectModeParamTypeId).value().toString();
|
||||
quint8 mode = FX_MODE_STATIC;
|
||||
if (effectMode == "Static") {
|
||||
mode = FX_MODE_STATIC;
|
||||
@ -340,7 +331,7 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info)
|
||||
}
|
||||
|
||||
qCDebug(dcWs2812fx()) << "Set mode finished successfully" << effectMode << mode;
|
||||
thing->setStateValue(ws2812fxEffectModeStateTypeId, effectMode);
|
||||
thing->setStateValue(nymeaLightSerialEffectModeStateTypeId, effectMode);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
}
|
||||
@ -349,9 +340,8 @@ void IntegrationPluginWs2812fx::executeAction(ThingActionInfo *info)
|
||||
|
||||
void IntegrationPluginWs2812fx::thingRemoved(Thing *thing)
|
||||
{
|
||||
if (thing->thingClassId() == ws2812fxThingClassId) {
|
||||
|
||||
m_usedInterfaces.removeAll(thing->paramValue(ws2812fxThingSerialPortParamTypeId).toString());
|
||||
if (thing->thingClassId() == nymeaLightSerialThingClassId) {
|
||||
m_usedInterfaces.removeAll(thing->paramValue(nymeaLightSerialThingSerialPortParamTypeId).toString());
|
||||
NymeaLight *light = m_lights.take(thing);
|
||||
light->deleteLater();
|
||||
}
|
||||
|
||||
@ -111,6 +111,7 @@ public:
|
||||
explicit IntegrationPluginWs2812fx();
|
||||
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void postSetupThing(Thing *thing) override;
|
||||
void thingRemoved(Thing *thing) override;
|
||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||
void executeAction(ThingActionInfo *info) override;
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
"thingClasses": [
|
||||
{
|
||||
"id": "24364e36-b199-4e35-b468-c44da58c009c",
|
||||
"name": "ws2812fx",
|
||||
"displayName": "WS2812FX",
|
||||
"name": "nymeaLightSerial",
|
||||
"displayName": "Serial nymea light",
|
||||
"createMethods": ["user", "discovery"],
|
||||
"interfaces": ["colorlight", "connectable"],
|
||||
"paramTypes": [
|
||||
@ -21,7 +21,7 @@
|
||||
"displayName": "Serial port",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": "ttyAMC0"
|
||||
"defaultValue": "/dev/ttyAMC0"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
@ -63,7 +63,7 @@
|
||||
"displayNameEvent": "color changed",
|
||||
"displayNameAction": "Set color",
|
||||
"type": "QColor",
|
||||
"defaultValue": "#000000",
|
||||
"defaultValue": "#FFFFFF",
|
||||
"writable": true
|
||||
},
|
||||
{
|
||||
@ -74,7 +74,7 @@
|
||||
"displayNameAction": "Set brigtness",
|
||||
"type": "int",
|
||||
"unit": "Percentage",
|
||||
"defaultValue": 0,
|
||||
"defaultValue": 100,
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"writable": true
|
||||
@ -87,7 +87,7 @@
|
||||
"displayNameAction": "Set speed",
|
||||
"type": "int",
|
||||
"unit": "Percentage",
|
||||
"defaultValue": 0,
|
||||
"defaultValue": 50,
|
||||
"minValue": 0,
|
||||
"maxValue": 100,
|
||||
"writable": true
|
||||
|
||||
@ -7,12 +7,36 @@ NymeaLight::NymeaLight(NymeaLightInterface *interface, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_interface(interface)
|
||||
{
|
||||
connect(m_interface, &NymeaLightInterface::dataReceived, this, &NymeaLight::onDataReceived);
|
||||
connect(m_interface, &NymeaLightInterface::availableChanged, this, [=](bool available){
|
||||
qCDebug(dcWs2812fx()) << "Interface available changed" << available;
|
||||
emit availableChanged(available);
|
||||
m_interfaceAvailable = available;
|
||||
if (m_interfaceAvailable) {
|
||||
qCDebug(dcWs2812fx()) << "Nymea light interface is now available. Start polling status of the light controller...";
|
||||
m_pollStatusRetryCount = 0;
|
||||
pollStatus();
|
||||
} else {
|
||||
m_ready = false;
|
||||
m_requestId = 0;
|
||||
emit availableChanged(false);
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_interface, &NymeaLightInterface::dataReceived, this, &NymeaLight::onDataReceived);
|
||||
}
|
||||
|
||||
NymeaLightInterfaceReply *NymeaLight::setPower(bool power, quint16 fadeDuration)
|
||||
{
|
||||
// Build the request
|
||||
QByteArray requestData;
|
||||
QDataStream stream(&requestData, QIODevice::WriteOnly);
|
||||
stream << static_cast<quint8>(NymeaLightInterface::CommandSetPower);
|
||||
stream << m_requestId++;
|
||||
stream << fadeDuration;
|
||||
stream << static_cast<quint8>(power ? 0x01 : 0x00);
|
||||
|
||||
NymeaLightInterfaceReply *reply = createReply(requestData);
|
||||
m_pendingRequests.enqueue(reply);
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
NymeaLightInterfaceReply *NymeaLight::setColor(const QColor &color, quint16 fadeDuration)
|
||||
@ -82,7 +106,19 @@ NymeaLightInterfaceReply *NymeaLight::setEffect(quint8 effect)
|
||||
|
||||
bool NymeaLight::available() const
|
||||
{
|
||||
return m_interface->available();
|
||||
return m_interfaceAvailable && m_ready;
|
||||
}
|
||||
|
||||
void NymeaLight::enable()
|
||||
{
|
||||
m_interface->open();
|
||||
qCDebug(dcWs2812fx()) << "Nymea light enabled";
|
||||
}
|
||||
|
||||
void NymeaLight::disable()
|
||||
{
|
||||
m_interface->close();
|
||||
qCDebug(dcWs2812fx()) << "Nymea light disabled";
|
||||
}
|
||||
|
||||
NymeaLightInterfaceReply *NymeaLight::createReply(const QByteArray &requestData)
|
||||
@ -114,36 +150,93 @@ void NymeaLight::sendNextRequest()
|
||||
m_currentReply->m_timer->start();
|
||||
}
|
||||
|
||||
void NymeaLight::pollStatus()
|
||||
{
|
||||
// Request ready state from controller
|
||||
NymeaLightInterfaceReply *reply = getStatus();
|
||||
connect(reply, &NymeaLightInterfaceReply::finished, this, [=](){
|
||||
if (reply->status() == NymeaLightInterface::StatusSuccess) {
|
||||
qCDebug(dcWs2812fx()) << "Get status request finished successfully. The firmware is ready to operate.";
|
||||
m_ready = true;
|
||||
m_pollStatusRetryCount = 0;
|
||||
emit availableChanged(true);
|
||||
} else {
|
||||
m_pollStatusRetryCount++;
|
||||
if (m_pollStatusRetryCount >= m_pollStatusRetryLimit) {
|
||||
qCWarning(dcWs2812fx()) << "Firmware did not respond to get status request after" << m_pollStatusRetryCount << "attempts. Giving up.";
|
||||
m_ready = false;
|
||||
} else {
|
||||
if (!m_ready && m_interfaceAvailable) {
|
||||
qCDebug(dcWs2812fx()) << "Get status request finished with error" << reply->status() << "Retry" << m_pollStatusRetryCount << "/" << m_pollStatusRetryLimit;
|
||||
pollStatus();
|
||||
} else {
|
||||
qCDebug(dcWs2812fx()) << "Get status request finished with error, but that's ok since we received the ready notification." << reply->status();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
NymeaLightInterfaceReply *NymeaLight::getStatus()
|
||||
{
|
||||
qCDebug(dcWs2812fx()) << "Request status of nymea light";
|
||||
QByteArray requestData;
|
||||
QDataStream stream(&requestData, QIODevice::WriteOnly);
|
||||
stream << static_cast<quint8>(NymeaLightInterface::CommandGetStatus);
|
||||
stream << m_requestId++;
|
||||
|
||||
NymeaLightInterfaceReply *reply = createReply(requestData);
|
||||
m_pendingRequests.enqueue(reply);
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
void NymeaLight::onDataReceived(const QByteArray &data)
|
||||
{
|
||||
Q_ASSERT(data.length() >= 3);
|
||||
|
||||
NymeaLightInterface::Command command = static_cast<NymeaLightInterface::Command>(static_cast<quint8>((data.at(0))));
|
||||
quint8 commandInt = static_cast<quint8>((data.at(0)));
|
||||
quint8 requestId = static_cast<quint8>(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;
|
||||
}
|
||||
qCDebug(dcWs2812fx()) << "Recived data" << commandInt << requestId << data.toHex();
|
||||
|
||||
NymeaLightInterface::Status status = static_cast<NymeaLightInterface::Status>(data.at(2));
|
||||
// Check if command or notification
|
||||
if (commandInt < 0xF0) {
|
||||
NymeaLightInterface::Command command = static_cast<NymeaLightInterface::Command>(commandInt);
|
||||
|
||||
if (m_currentReply) {
|
||||
if (m_currentReply->command() == command && m_currentReply->requestId() == requestId) {
|
||||
m_currentReply->m_timer->stop();
|
||||
m_currentReply->m_status = status;
|
||||
NymeaLightInterface::Status status = static_cast<NymeaLightInterface::Status>(data.at(2));
|
||||
|
||||
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;
|
||||
if (m_currentReply) {
|
||||
if (m_currentReply->command() == command && m_currentReply->requestId() == requestId) {
|
||||
m_currentReply->m_timer->stop();
|
||||
m_currentReply->m_status = status;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
emit m_currentReply->finished();
|
||||
} else {
|
||||
qCWarning(dcWs2812fx()) << "Received unhandled command response data" << data.toHex();
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcWs2812fx()) << "Received unhandled data" << data.toHex();
|
||||
NymeaLightInterface::Notification notification = static_cast<NymeaLightInterface::Notification>(commandInt);
|
||||
switch (notification) {
|
||||
case NymeaLightInterface::NotificationReady:
|
||||
qCDebug(dcWs2812fx()) << "Controller ready notification received";
|
||||
m_ready = true;
|
||||
emit availableChanged(true);
|
||||
break;
|
||||
case NymeaLightInterface::NotificationDebugMessage:
|
||||
qCDebug(dcWs2812fx()) << "Firmware debug:" << QString::fromUtf8(data.right(data.length() - 2));
|
||||
break;
|
||||
default:
|
||||
qCWarning(dcWs2812fx()) << "Unhandled notification received" << data.toHex();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ 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 *setPower(bool power, quint16 fadeDuration = 0);
|
||||
NymeaLightInterfaceReply *setColor(const QColor &color, quint16 fadeDuration = 0);
|
||||
NymeaLightInterfaceReply *setBrightness(quint8 brightness, quint16 fadeDuration = 0);
|
||||
NymeaLightInterfaceReply *setSpeed(quint16 speed, quint16 fadeDuration = 0);
|
||||
@ -22,9 +23,17 @@ public:
|
||||
|
||||
bool available() const;
|
||||
|
||||
public slots:
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
private:
|
||||
NymeaLightInterface *m_interface = nullptr;
|
||||
quint8 m_requestId = 0;
|
||||
bool m_interfaceAvailable = false;
|
||||
bool m_ready = false;
|
||||
int m_pollStatusRetryCount = 0;
|
||||
int m_pollStatusRetryLimit = 5;
|
||||
|
||||
NymeaLightInterfaceReply *m_currentReply = nullptr;
|
||||
QQueue<NymeaLightInterfaceReply *> m_pendingRequests;
|
||||
@ -32,6 +41,11 @@ private:
|
||||
NymeaLightInterfaceReply *createReply(const QByteArray &requestData);
|
||||
void sendNextRequest();
|
||||
|
||||
|
||||
void pollStatus();
|
||||
NymeaLightInterfaceReply *getStatus();
|
||||
|
||||
|
||||
private slots:
|
||||
void onDataReceived(const QByteArray &data);
|
||||
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
#include "nymealightinterface.h"
|
||||
|
||||
NymeaLightInterface::NymeaLightInterface(QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
@ -9,15 +9,22 @@ class NymeaLightInterface : public QObject
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Command {
|
||||
CommandSetColor = 0x00,
|
||||
CommandSetBrightness = 0x01,
|
||||
CommandSetSpeed = 0x02,
|
||||
CommandSetEffect = 0x03,
|
||||
CommandDebug = 0xFE,
|
||||
CommandCustom = 0xFF
|
||||
CommandGetStatus = 0x00,
|
||||
CommandSetPower = 0x01,
|
||||
CommandSetColor = 0x02,
|
||||
CommandSetBrightness = 0x03,
|
||||
CommandSetSpeed = 0x04,
|
||||
CommandSetEffect = 0x05,
|
||||
CommandCustom = 0xEF
|
||||
};
|
||||
Q_ENUM(Command)
|
||||
|
||||
enum Notification {
|
||||
NotificationReady = 0xF0,
|
||||
NotificationDebugMessage = 0xF1
|
||||
};
|
||||
Q_ENUM(Notification)
|
||||
|
||||
enum Status {
|
||||
StatusSuccess = 0x00,
|
||||
StatusInvalidProtocol = 0x01,
|
||||
@ -34,7 +41,8 @@ public:
|
||||
};
|
||||
Q_ENUM(Mode)
|
||||
|
||||
explicit NymeaLightInterface(QObject *parent = nullptr);
|
||||
inline explicit NymeaLightInterface(QObject *parent = nullptr) : QObject(parent) { };
|
||||
virtual ~NymeaLightInterface() = default;
|
||||
|
||||
virtual bool open() = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
@ -4,9 +4,10 @@
|
||||
#include <QDataStream>
|
||||
|
||||
NymeaLightSerialInterface::NymeaLightSerialInterface(const QString &name, QObject *parent) :
|
||||
NymeaLightInterface(parent)
|
||||
NymeaLightInterface(parent),
|
||||
m_serialPortName(name)
|
||||
{
|
||||
m_serialPort = new QSerialPort(name, this);
|
||||
m_serialPort = new QSerialPort(m_serialPortName, this);
|
||||
m_serialPort->setBaudRate(115200);
|
||||
m_serialPort->setDataBits(QSerialPort::DataBits::Data8);
|
||||
m_serialPort->setParity(QSerialPort::Parity::NoParity);
|
||||
@ -33,8 +34,26 @@ NymeaLightSerialInterface::NymeaLightSerialInterface(const QString &name, QObjec
|
||||
|
||||
bool NymeaLightSerialInterface::open()
|
||||
{
|
||||
bool serialPortFound = false;
|
||||
foreach(const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {
|
||||
if (serialPortInfo.systemLocation() == m_serialPortName) {
|
||||
serialPortFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent repeating warnings...
|
||||
if (!serialPortFound) {
|
||||
if (!m_reconnectTimer->isActive()) {
|
||||
m_reconnectTimer->start();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The serial port is available...lt's try to open it
|
||||
if (!m_serialPort->open(QIODevice::ReadWrite)) {
|
||||
qCWarning(dcWs2812fx()) << "Could not open serial port" << m_serialPort->portName() << m_serialPort->errorString();
|
||||
m_reconnectTimer->start();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -130,7 +149,7 @@ void NymeaLightSerialInterface::onReadyRead()
|
||||
void NymeaLightSerialInterface::onSerialError(QSerialPort::SerialPortError error)
|
||||
{
|
||||
if (error != QSerialPort::NoError && m_serialPort->isOpen()) {
|
||||
qCCritical(dcWs2812fx()) << "Serial port error:" << error << m_serialPort->errorString();
|
||||
qCWarning(dcWs2812fx()) << "Serial port error:" << error << m_serialPort->errorString();
|
||||
m_reconnectTimer->start();
|
||||
m_serialPort->close();
|
||||
emit availableChanged(false);
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
#ifndef NYMEALIGHTSERIALINTERFACE_H
|
||||
#define NYMEALIGHTSERIALINTERFACE_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include <QSerialPort>
|
||||
#include <QTimer>
|
||||
#include <QSerialPortInfo>
|
||||
|
||||
#include "nymealightinterface.h"
|
||||
|
||||
@ -12,10 +13,12 @@ class NymeaLightSerialInterface : public NymeaLightInterface
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit NymeaLightSerialInterface(const QString &name, QObject *parent = nullptr);
|
||||
~NymeaLightSerialInterface() override = default;
|
||||
|
||||
bool open() override;
|
||||
void close() override;
|
||||
bool available() override;
|
||||
|
||||
void sendData(const QByteArray &data) override;
|
||||
|
||||
private:
|
||||
@ -26,6 +29,7 @@ private:
|
||||
SlipProtocolTransposedEsc = 0xDD
|
||||
};
|
||||
|
||||
QString m_serialPortName;
|
||||
QTimer *m_reconnectTimer = nullptr;
|
||||
QSerialPort *m_serialPort = nullptr;
|
||||
QByteArray m_buffer;
|
||||
|
||||
@ -7,7 +7,6 @@ TARGET = $$qtLibraryTarget(nymea_integrationpluginws2812fx)
|
||||
SOURCES += \
|
||||
integrationpluginws2812fx.cpp \
|
||||
nymealight.cpp \
|
||||
nymealightinterface.cpp \
|
||||
nymealightserialinterface.cpp
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user