improve serial hotplug (#389)

* - disable device when error indecates that the problem is not solvable on reconnect
- introduce a preOpenDelay of 2 seconds (currently value is hardcoded)

* rs232:
- make preOpenDelay available via webui
- fix preOpenDelay
- add basic usb serial detection

* - revert 3819ae7
- fix schema files

* make json checks compat with utf8+bom

* make shutdown effect a bit more flexible
This commit is contained in:
redPanther 2017-02-09 20:10:57 +01:00 committed by GitHub
parent 3819ae72ca
commit 170ad4f5db
26 changed files with 712 additions and 294 deletions

View File

@ -4,7 +4,7 @@
```
sudo apt-get update
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev python-simplejson
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev
```
**ATTENTION Win10LinuxSubsystem** we do not (/we can't) support using hyperion in linux subsystem of MS Windows 10, albeit some users tested it with success. Keep in mind to disable

View File

@ -11,6 +11,7 @@
#define INITIAL_LED_TEST_ENABLED true
#define INITIAL_LED_TEST_BRIGHTNESS 32 // 0..255
#define INITIAL_LED_TEST_TIME_MS 500 // 10..
// Number of leds in your strip. set to "1" and ANALOG_OUTPUT_ENABLED to "true" to activate analog only
// As of 26/1/2017:
@ -137,7 +138,6 @@ void setup() {
pinMode(ANALOG_GREEN_PIN, OUTPUT);
}
// Uncomment/edit one of the following lines for your leds arrangement.
int ledCount = MAX_LEDS;
if (ANALOG_MODE == ANALOG_MODE_LAST_LED) {
ledCount--;
@ -155,10 +155,17 @@ void setup() {
// initial RGB flash
#if INITIAL_LED_TEST_ENABLED == true
Serial.println("initial test");
showColor(CRGB(INITIAL_LED_TEST_BRIGHTNESS, 0, 0)); delay(400);
showColor(CRGB(0, INITIAL_LED_TEST_BRIGHTNESS, 0)); delay(400);
showColor(CRGB(0, 0, INITIAL_LED_TEST_BRIGHTNESS )); delay(400);
for (int v=0;v<INITIAL_LED_TEST_BRIGHTNESS;v++)
{
showColor(CRGB(v,v,v);
delay(INITIAL_LED_TEST_TIME_MS/2/INITIAL_LED_TEST_BRIGHTNESS);
}
for (int v=0;v<INITIAL_LED_TEST_BRIGHTNESS;v++)
{
showColor(CRGB(v,v,v);
delay(INITIAL_LED_TEST_TIME_MS/2/INITIAL_LED_TEST_BRIGHTNESS);
}
#endif
showColor(CRGB(0, 0, 0));

View File

@ -1,133 +1,447 @@
/**
* This is a demo implemention how to use tpm2 protovol on arduino
*
* code is taken from: https://github.com/JonasVanGool/TPM2-ARDUINO
*/
#include <pt.h>
#include "FastLED.h"
#define ANALOG_MODE_AVERAGE 0
#define ANALOG_MODE_LAST_LED 1
#include <FastLED.h>
/*==============================================================================*/
/* LED und Arduino Variablen */
/*==============================================================================*/
#define INITIAL_LED_TEST_ENABLED true
#define INITIAL_LED_TEST_BRIGHTNESS 32 // 0..255
#define INITIAL_LED_TEST_TIME_MS 500 // 0..255
#define START_BYTE 0xC9
#define STOP_BYTE 0x36
#define DATA_FRAME 0xDA
#define COMMAND 0xC0
#define REQ_RESP 0xAA
#define MAX_LEDS 400 // Number of LEDs
#define MAX_ARGS 10 // Max Number of command arguments
#define BAUDRATE 460800 // Baudrate
#define SERIAL Serial // Serial port for communication
#define NUM_LEDS_PER_STRIP 20
#define BAUDRATE 115200
// type of your led controller, possible values, see below
#define LED_TYPE WS2812B
#define DATA_PIN 12
#define MAX_NR_LEDS 200
// 3 wire (pwm): NEOPIXEL BTM1829 TM1812 TM1809 TM1804 TM1803 UCS1903 UCS1903B UCS1904 UCS2903 WS2812 WS2852
// S2812B SK6812 SK6822 APA106 PL9823 WS2811 WS2813 APA104 WS2811_40 GW6205 GW6205_40 LPD1886 LPD1886_8BIT
// 4 wire (spi): LPD8806 WS2801 WS2803 SM16716 P9813 APA102 SK9822 DOTSTAR
CRGB leds[MAX_NR_LEDS];
// For 3 wire led stripes line Neopixel/Ws2812, which have a data line, ground, and power, you just need to define DATA_PIN.
// For led chipsets that are SPI based (four wires - data, clock, ground, and power), both defines DATA_PIN and CLOCK_PIN are needed
enum States {
ST_START,
ST_PACKET_TYPE,
ST_PAYLOAD_SIZE,
ST_USER_DATA,
ST_END
// DATA_PIN, or DATA_PIN, CLOCK_PIN
#define LED_PINS 6 // 3 wire leds
//#define LED_PINS 6, 13 // 4 wire leds
#define COLOR_ORDER GRB // colororder of the stripe, set RGB in hyperion
#define OFF_TIMEOUT 15000 // ms to switch off after no data was received, set 0 to deactivate
// analog rgb uni color led stripe - using of hyperion smoothing is recommended
// ATTENTION this pin config is default for atmega328 based arduinos, others might work to
// if you have flickering analog leds this might be caused by unsynced pwm signals
// try other pins is more or less the only thing that helps
#define ANALOG_OUTPUT_ENABLED false
#define ANALOG_MODE ANALOG_MODE_LAST_LED // use ANALOG_MODE_AVERAGE or ANALOG_MODE_LAST_LED
#define ANALOG_GROUND_PIN 8 // additional ground pin to make wiring a bit easier
#define ANALOG_RED_PIN 9
#define ANALOG_GREEN_PIN 10
#define ANALOG_BLUE_PIN 11
// overall color adjustments
#define ANALOG_BRIGHTNESS_RED 255 // maximum brightness for analog 0-255
#define ANALOG_BRIGHTNESS_GREEN 255 // maximum brightness for analog 0-255
#define ANALOG_BRIGHTNESS_BLUE 255 // maximum brightness for analog 0-255
#define BRIGHTNESS 255 // maximum brightness 0-255
#define DITHER_MODE BINARY_DITHER // BINARY_DITHER or DISABLE_DITHER
#define COLOR_TEMPERATURE CRGB(255,255,255) // RGB value describing the color temperature
#define COLOR_CORRECTION TypicalLEDStrip // predefined fastled color correction
//#define COLOR_CORRECTION CRGB(255,255,255) // or RGB value describing the color correction
// Baudrate, higher rate allows faster refresh rate and more LEDs
//#define serialRate 460800 // use 115200 for ftdi based boards
#define serialRate 115200 // use 115200 for ftdi based boards
//#define serialRate 500000 // use 115200 for ftdi based boards
/*==============================================================================*/
/* TPM2 Variablen */
/*==============================================================================*/
enum Protocol
{
// positions
posStart = 0,
posType = 1,
posFsHigh = 2,
posFsLow = 3,
posData = 4,
// bytes
tpm2Start = 0xc9,
tpm2DataFrame = 0xda,
tpm2Command = 0xc0,
tpm2Answer = 0xaa,
tpm2End = 0x36,
tpm2Ack = 0xAC
};
States activeState = ST_START;
uint8_t byteRead = 0;
uint8_t payloadHighByte = 0;
uint8_t payloadLowByte = 0;
int payloadSize = 0;
int bytesRead = 0;
int nrOfLeds = 0;
enum Mode
{
mNone,
mCommunication,
mProgram
};
boolean flag = false;
void setup() {
// Init leds
for (int i = 0; i < MAX_NR_LEDS; i++)
leds[i] = 0;
// Start serial device
Serial.begin(BAUDRATE);
// Set time out for readBytes function
Serial.setTimeout(100);
// init fastLED Library
LEDS.addLeds<WS2812B, DATA_PIN, RGB>(leds, MAX_NR_LEDS);
// setting brightness to 50% brightness
LEDS.setBrightness(128);
// debug led
pinMode(13, OUTPUT);
struct Data
{
int pos;
uint8_t type;
uint16_t fs;
uint16_t maxSize;
uint8_t command;
CRGB rgb[MAX_LEDS];
} data;
byte args[MAX_ARGS];
unsigned long lastDataAt = 0;
int program = 0;
int mode = mNone;
int effectDelay = 100;
static struct pt pt1;
for(;;) {
if (Serial.available() > 0) {
// process TPM2 protocol
switch (activeState) {
//------------------------------//
// START //
//------------------------------//
case ST_START:
// read incomming byte
byteRead = Serial.read();
if (byteRead == START_BYTE) {
activeState = ST_PACKET_TYPE;
}
break;
//------------------------------//
// PACKET_TYPE //
//------------------------------//
case ST_PACKET_TYPE:
// read incomming byte
byteRead = Serial.read();
if (byteRead == DATA_FRAME) {
activeState = ST_PAYLOAD_SIZE;
} else {
activeState = ST_START;
Serial.flush();
}
break;
//------------------------------//
// PAYLOAD_SIZE //
//------------------------------//
case ST_PAYLOAD_SIZE:
payloadHighByte = Serial.read();
while (Serial.available() == 0) {}
payloadLowByte = Serial.read();
payloadSize = (payloadHighByte << 8) + payloadLowByte;
nrOfLeds = payloadSize / 3;
if (nrOfLeds <= MAX_NR_LEDS) {
activeState = ST_USER_DATA;
} else {
activeState = ST_START;
Serial.flush();
}
break;
//------------------------------//
// USER_DATA //
//------------------------------//
case ST_USER_DATA:
bytesRead = Serial.readBytes((char *)leds, payloadSize);
LEDS.show();
if (bytesRead == payloadSize) {
activeState = ST_END;
} else {
activeState = ST_START;
Serial.flush();
}
break;
//------------------------------//
// END //
//------------------------------//
case ST_END:
// read incomming byte
byteRead = Serial.read();
if (byteRead == STOP_BYTE) {
activeState = ST_START;
} else {
activeState = ST_START;
Serial.flush();
}
break;
default: break;
}
// int freeRam()
// {
// extern int __heap_start, *__brkval;
// int v;
// return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
// }
void setup()
{
SERIAL.begin(BAUDRATE);
delay(1000);
int ledCount = MAX_LEDS;
if (ANALOG_MODE == ANALOG_MODE_LAST_LED) {
ledCount--;
}
memset(data.rgb, 0, sizeof(struct CRGB) * ledCount);
FastLED.addLeds<LED_TYPE, LED_PINS, COLOR_ORDER>(data.rgb, ledCount);
// color adjustments
FastLED.setBrightness ( BRIGHTNESS );
FastLED.setTemperature( COLOR_TEMPERATURE );
FastLED.setCorrection ( COLOR_CORRECTION );
FastLED.setDither ( DITHER_MODE );
#ifdef INITIAL_LED_TEST_ENABLED
for (int v=0;v<INITIAL_LED_TEST_BRIGHTNESS;v++)
{
oneColorAll(v,v,v);
delay(INITIAL_LED_TEST_TIME_MS/2/INITIAL_LED_TEST_BRIGHTNESS);
}
for (int v=0;v<INITIAL_LED_TEST_BRIGHTNESS;v++)
{
oneColorAll(INITIAL_LED_TEST_BRIGHTNESS-v,INITIAL_LED_TEST_BRIGHTNESS-v,INITIAL_LED_TEST_BRIGHTNESS-v);
delay(INITIAL_LED_TEST_TIME_MS/2/INITIAL_LED_TEST_BRIGHTNESS);
}
#define BRIGHT 127 //max led intensity (1-500)
#define INHALE 1250 //Inhalation time in milliseconds.
#define PULSE INHALE*1000/BRIGHT
#define REST 1000 //Rest Between Inhalations.
while(true)
{
//ramp increasing intensity, Inhalation:
for (int i=1;i<BRIGHT;i++){
oneColorAll(i,i,i);
delayMicroseconds(PULSE-i*10); // wait
delay(0); //to prevent watchdog firing.
}
//ramp decreasing intensity, Exhalation (half time):
for (int i=BRIGHT-1;i>0;i--){
oneColorAll(i,i,i);
delayMicroseconds(PULSE-i*10); // wait
i--;
delay(0); //to prevent watchdog firing.
}
delay(REST); //take a rest...
}
#endif
oneColorAll(0,0,0);
memset(args, 0, MAX_ARGS);
resetVars();
PT_INIT(&pt1);
while (!SERIAL) {
; // wait for serial port to connect. Needed for native USB
}
}
void loop() {
/*==============================================================================*/
/* Thread für Programm/Effekte
/*==============================================================================*/
static int playProgramThread(struct pt *pt)
{
static unsigned long timestamp = 0;
PT_BEGIN(pt);
while(1)
{
PT_WAIT_UNTIL(pt, millis() - timestamp > effectDelay);
playProgram();
timestamp = millis();
}
PT_END(pt);
}
/*==============================================================================*/
/* loop
/*==============================================================================*/
void loop()
{
while (1)
{
// if data available switch to communication mode
if (SERIAL.available() > 0)
{
if (mode != mCommunication)
{
mode = mCommunication;
resetVars();
}
doCommunication();
}
else
// communication timeout after 0.5 seconds
while (SERIAL.available() == 0 && millis()-lastDataAt > 1000)
{
if (mode != mProgram)
{
mode = mProgram;
resetVars();
}
else
playProgramThread(&pt1);
}
}
}
/*==============================================================================*/
/* do communication
/*==============================================================================*/
void doCommunication()
{
// read ...
while (SERIAL.available() > 0)
{
byte val = SERIAL.read();
lastDataAt = millis();
if (data.pos == posStart && val == tpm2Start) // Startbyte
resetVars();
else if (data.pos == posType && (val == tpm2DataFrame || val == tpm2Command)) // Block-Art
data.type = val;
else if (data.pos == posFsHigh) // Framesize (High Bit)
{
data.fs = (val << 8) & 0xFF00;
data.maxSize = data.fs;
}
else if (data.pos == posFsLow) // Framesize (Low byte)
{
data.fs += val & 0x00FF;
data.maxSize = data.fs;
if (data.fs > MAX_LEDS*3) // framsize too high
{
data.fs = MAX_LEDS*3;
}
}
else if (data.pos == posData + data.maxSize && val == tpm2End) // End Byte
parsePacket();
else if (data.pos >= posData && data.pos < posData+data.fs) // Bytes zwischen Header und Ende lesen
evaluateData(val);
else if (data.pos >= posData && data.pos < posData+data.maxSize) // Bytes zwischen Header und Ende lesen
continue;
else // protocol violation ...
{
resetVars();
continue;
}
data.pos++;
}
}
/*==============================================================================*/
/* evaluate data
/*==============================================================================*/
void evaluateData(byte val)
{
if (data.type == tpm2DataFrame) // frame data
{
uint8_t* rgb = (uint8_t*)data.rgb;
rgb[data.pos-posData] = val;
}
else // command data
{
if (data.pos == posData)
{
data.command = val;
memset(args, 0, sizeof(args));
}
else
args[data.pos-posData-1] = val;
}
}
/*==============================================================================*/
/* reset variables
/*==============================================================================*/
void resetVars()
{
memset(&data, 0, sizeof(Data));
//data.rgb = (struct CRGB*)FastSPI_LED.getRGBData();
memset(data.rgb, 0, MAX_LEDS * sizeof(struct CRGB));
}
/*==============================================================================*/
/* parse packet
/*==============================================================================*/
void parsePacket()
{
switch (data.type)
{
case tpm2DataFrame:
{
showLeds();
break;
}
case tpm2Command:
{
setProgram();
break;
}
default:
break;
}
SERIAL.write(tpm2Ack);
resetVars();
data.pos = -1;
}
/*==============================================================================*/
/* set LED color
/*==============================================================================*/
void setLedColor(int led, uint8_t r, uint8_t g, uint8_t b)
{
data.rgb[led].r = r;
data.rgb[led].g = g;
data.rgb[led].b = b;
}
/*==============================================================================*/
/* one Color All
/*==============================================================================*/
void oneColorAll(uint8_t r, uint8_t g, uint8_t b)
{
memset(data.rgb, 0, MAX_LEDS * sizeof(struct CRGB));
for (int led = 0; led < MAX_LEDS; led++)
setLedColor(led, r, g, b);
showLeds();
effectDelay = 1000;
}
/*==============================================================================*/
/* Output Leds
/*==============================================================================*/
void showLeds()
{
FastLED.show();
}
void setProgram()
{
program = data.command;
}
void playProgram()
{
switch (program)
{
case 0: oneColorAll(args[0],args[1],args[2]); break;
case 1: rainbow_loop(20); break;
default: oneColorAll(0,0,0); break;
}
}
/* Set LED Color of given LED */
void oneColorAllNOSHOW(int r, int g, int b)
{
resetVars();
for (int led = 0; led < MAX_LEDS; led++)
{
setLedColor(led, r, g, b);
}
}
/*==============================================================================*/
/* Effect 0: Fixed color - Arguments RR GG BB
/*==============================================================================*/
/*==============================================================================*/
/* Effect 1: Loops rainbow colors around the stripe
/*==============================================================================*/
void rainbow_loop(int idelay) { //-LOOP HSV RAINBOW
static double idex = 0;
static double ihue = 0;
double steps = (double)255/MAX_LEDS;
for(int led = 0 ; led < MAX_LEDS; led++ ) {
ihue = led * steps + idex;
if (ihue >= 255)
ihue -= 255;
data.rgb[led] = CHSV((int)ihue, 255, 255);
if (led == 0)
idex += steps;
if (idex >= 255)
idex = 0;
}
showLeds();
effectDelay = idelay;
}

View File

@ -1,78 +1,78 @@
{
"type":"object",
"script" : "clock.py",
"title":"edt_eff_clock_header_title",
"required":true,
"properties":{
{
"type": "object",
"script": "clock.py",
"title": "edt_eff_clock_header_title",
"required": true,
"properties": {
"offset": {
"type": "number",
"title":"edt_eff_offset_title",
"title": "edt_eff_offset_title",
"default": 0,
"append" : "edt_append_leds",
"propertyOrder" : 1
"append": "edt_append_leds",
"propertyOrder": 1
},
"hour-color": {
"type": "array",
"title":"edt_eff_colorHour_title",
"format":"colorpicker",
"default": [255,0,0],
"items" : {
"title": "edt_eff_colorHour_title",
"format": "colorpicker",
"default": [255, 0, 0],
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 3,
"maxItems": 3,
"propertyOrder" : 2
"propertyOrder": 2
},
"minute-color": {
"type": "array",
"title":"edt_eff_colorMinute_title",
"format":"colorpicker",
"default": [0,255,0],
"items" : {
"title": "edt_eff_colorMinute_title",
"format": "colorpicker",
"default": [0, 255, 0],
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 3,
"maxItems": 3,
"propertyOrder" : 3
"propertyOrder": 3
},
"second-color": {
"type": "array",
"title":"edt_eff_colorSecond_title",
"format":"colorpicker",
"default": [0,0,255],
"items" : {
"title": "edt_eff_colorSecond_title",
"format": "colorpicker",
"default": [0, 0, 255],
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"minItems": 3,
"maxItems": 3,
"propertyOrder" : 4
"propertyOrder": 4
},
"hour-margin": {
"type": "number",
"title":"edt_eff_hourMargin_title",
"title": "edt_eff_hourMargin_title",
"default": 2,
"append" : "edt_append_leds",
"propertyOrder" : 5
"append": "edt_append_leds",
"propertyOrder": 5
},
"minute-margin": {
"type": "number",
"title":"edt_eff_minuteMargin_title",
"title": "edt_eff_minuteMargin_title",
"default": 1,
"append" : "edt_append_leds",
"propertyOrder" : 6
"append": "edt_append_leds",
"propertyOrder": 6
},
"second-margin": {
"type": "number",
"title":"edt_eff_secondMargin_title",
"title": "edt_eff_secondMargin_title",
"default": 0,
"append" : "edt_append_leds",
"propertyOrder" : 7
"append": "edt_append_leds",
"propertyOrder": 7
}
},
"additionalProperties": false

View File

@ -1,4 +1,4 @@
{
{
"type":"object",
"script" : "pacman.py",
"title":"edt_eff_pacman_header_title",

View File

@ -40,11 +40,23 @@
"maxItems": 3,
"propertyOrder" : 3
},
"initial-blink": {
"type": "boolean",
"title":"edt_eff_initial_blink_title",
"default": true,
"propertyOrder" : 4
},
"set-post-color": {
"type": "boolean",
"title":"edt_eff_set_post_color_title",
"default": true,
"propertyOrder" : 5
},
"shutdown-enabled": {
"type": "boolean",
"title":"edt_eff_enableshutdown_title",
"default": false,
"propertyOrder" : 4
"propertyOrder" : 6
}
},
"additionalProperties": false

View File

@ -6,6 +6,8 @@
"speed" : 1.2,
"alarm-color" : [255,0,0],
"post-color" : [255,174,11],
"shutdown-enabled" : false
"shutdown-enabled" : false,
"initial-blink" : true,
"set-post-color" : true
}
}

View File

@ -13,13 +13,17 @@ sleepTime = float(hyperion.args.get('speed', 1.0))*0.5
alarmColor = hyperion.args.get('alarm-color', (255,0,0))
postColor = hyperion.args.get('post-color', (255,174,11))
off = bool(hyperion.args.get('shutdown-enabled', False))
initialBlink = bool(hyperion.args.get('initial-blink', True))
setPostColor = bool(hyperion.args.get('set-post-color', True))
width = 12
height = 10
imageData = bytearray(height * width * (0,0,0))
# Start the write data loop
for i in range(6):
if initialBlink:
for i in range(6):
if hyperion.abort():
off = False
break
@ -39,11 +43,12 @@ for y in range(height,0,-1):
time.sleep(sleepTime)
time.sleep(1)
for y in range(height):
if setPostColor:
for y in range(height):
for x in range(width):
setPixel(x, y, postColor)
hyperion.setImage(width, height, imageData)
time.sleep(2)
hyperion.setImage(width, height, imageData)
time.sleep(2)
if off and not hyperion.abort():
subprocess.call("halt")

View File

@ -6,6 +6,7 @@ LedDeviceAdalight::LedDeviceAdalight(const QJsonObject &deviceConfig)
, _ligthBerryAPA102Mode(false)
{
_deviceReady = init(deviceConfig);
connect(this,SIGNAL(receivedData(QByteArray)),this,SLOT(receivedData(QByteArray)));
}
LedDevice* LedDeviceAdalight::construct(const QJsonObject &deviceConfig)
@ -74,3 +75,7 @@ int LedDeviceAdalight::write(const std::vector<ColorRgb> & ledValues)
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}
void LedDeviceAdalight::receivedData(QByteArray data)
{
Debug(_log, ">>received %d bytes data", data.size());
}

View File

@ -22,6 +22,9 @@ public:
virtual bool init(const QJsonObject &deviceConfig);
public slots:
void receivedData(QByteArray data);
private:
///
/// Writes the led color values to the led-device

View File

@ -9,8 +9,14 @@ LedDeviceDMX::LedDeviceDMX(const QJsonObject &deviceConfig)
, _dmxSlotsPerLed(3)
, _dmxLedCount(0)
, _dmxChannelCount(0)
{
_deviceReady = init(deviceConfig);
}
bool LedDeviceDMX::init(const QJsonObject &deviceConfig)
{
ProviderRs232::init(deviceConfig);
std::string _dmxString = deviceConfig["dmxdevice"].toString("invalid").toStdString();
if (_dmxString == "raw")
{
@ -40,6 +46,8 @@ LedDeviceDMX::LedDeviceDMX(const QJsonObject &deviceConfig)
_ledBuffer.resize(_dmxChannelCount, 0);
_ledBuffer[0] = 0x00; // NULL START code
return true;
}
LedDevice* LedDeviceDMX::construct(const QJsonObject &deviceConfig)

View File

@ -19,6 +19,8 @@ public:
/// constructs leddevice
static LedDevice* construct(const QJsonObject &deviceConfig);
virtual bool init(const QJsonObject &deviceConfig);
private:
///
/// Writes the led color values to the led-device

View File

@ -51,9 +51,9 @@ bool LedDeviceFadeCandy::init(const QJsonObject &deviceConfig)
const QJsonArray whitePointConfig = deviceConfig["whitePoint"].toArray();
if ( !whitePointConfig.isEmpty() && whitePointConfig.size() == 3 )
{
_whitePoint_r = whitePointConfig[0].toDouble();
_whitePoint_g = whitePointConfig[1].toDouble();
_whitePoint_b = whitePointConfig[2].toDouble();
_whitePoint_r = whitePointConfig[0].toDouble() / 255.0;
_whitePoint_g = whitePointConfig[1].toDouble() / 255.0;
_whitePoint_b = whitePointConfig[2].toDouble() / 255.0;
}
_opc_data.resize( _ledRGBCount + OPC_HEADER_SIZE );

View File

@ -5,6 +5,9 @@
// Qt includes
#include <QTimer>
#include <QDateTime>
#include <QFile>
#include <QSerialPortInfo>
// Local Hyperion includes
#include "ProviderRs232.h"
@ -17,6 +20,9 @@ ProviderRs232::ProviderRs232()
, _bytesWritten(0)
, _frameDropCounter(0)
, _lastError(QSerialPort::NoError)
, _preOpenDelayTimeOut(0)
, _preOpenDelay(2000)
, _enableAutoDeviceName(false)
{
connect(&_rs232Port, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(error(QSerialPort::SerialPortError)));
connect(&_rs232Port, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64)));
@ -29,13 +35,30 @@ bool ProviderRs232::init(const QJsonObject &deviceConfig)
LedDevice::init(deviceConfig);
_deviceName = deviceConfig["output"].toString().toStdString();
_deviceName = deviceConfig["output"].toString("auto");
_enableAutoDeviceName = _deviceName == "auto";
_baudRate_Hz = deviceConfig["rate"].toInt();
_delayAfterConnect_ms = deviceConfig["delayAfterConnect"].toInt(250);
_delayAfterConnect_ms = deviceConfig["delayAfterConnect"].toInt(1500);
_preOpenDelay = deviceConfig["delayBeforeConnect"].toInt(1500);
return true;
}
QString ProviderRs232::findSerialDevice()
{
// take first available usb serial port - currently no probing!
for( auto port : QSerialPortInfo::availablePorts())
{
if (port.hasProductIdentifier() && port.hasVendorIdentifier() && !port.isBusy())
{
Info(_log, "found serial device: %s", port.systemLocation().toLocal8Bit().constData());
return port.systemLocation();
break;
}
}
return "";
}
void ProviderRs232::bytesWritten(qint64 bytes)
{
_bytesWritten += bytes;
@ -49,8 +72,8 @@ void ProviderRs232::bytesWritten(qint64 bytes)
void ProviderRs232::readyRead()
{
QByteArray data = _rs232Port.readAll();
Debug(_log, "received %d bytes data", data.size());
emit receivedData(_rs232Port.readAll());
Debug(_log, "received data");
}
@ -66,7 +89,9 @@ void ProviderRs232::error(QSerialPort::SerialPortError error)
case QSerialPort::DeviceNotFoundError:
Error(_log, "An error occurred while attempting to open an non-existing device."); break;
case QSerialPort::PermissionError:
Error(_log, "An error occurred while attempting to open an already opened device by another process or a user not having enough permission and credentials to open."); break;
Error(_log, "An error occurred while attempting to open an already opened device by another process or a user not having enough permission and credentials to open. Device disabled.");
_deviceReady = false;
break;
case QSerialPort::OpenError:
Error(_log, "An error occurred while attempting to open an already opened device in this object."); break;
case QSerialPort::NotOpenError:
@ -84,10 +109,14 @@ void ProviderRs232::error(QSerialPort::SerialPortError error)
case QSerialPort::ResourceError:
Error(_log, "An I/O error occurred when a resource becomes unavailable, e.g. when the device is unexpectedly removed from the system."); break;
case QSerialPort::UnsupportedOperationError:
Error(_log, "The requested device operation is not supported or prohibited by the running operating system."); break;
Error(_log, "The requested device operation is not supported or prohibited by the running operating system. Device disabled.");
_deviceReady = false;
break;
case QSerialPort::TimeoutError:
Error(_log, "A timeout error occurred."); break;
default: Error(_log,"An unidentified error occurred. (%d)", error);
default:
Error(_log,"An unidentified error occurred. Device disabled. (%d)", error);
_deviceReady = false;
}
_rs232Port.clearError();
closeDevice();
@ -106,28 +135,46 @@ void ProviderRs232::closeDevice()
if (_rs232Port.isOpen())
{
_rs232Port.close();
Debug(_log,"Close UART: %s", _deviceName.c_str());
Debug(_log,"Close UART: %s", _deviceName.toLocal8Bit().constData());
}
}
int ProviderRs232::open()
{
Info(_log, "Opening UART: %s", _deviceName.c_str());
_rs232Port.setPortName(_deviceName.c_str());
return tryOpen(_delayAfterConnect_ms) ? 0 : -1;
}
bool ProviderRs232::tryOpen(const int delayAfterConnect_ms)
{
if (_deviceName.isEmpty() || _rs232Port.portName().isEmpty())
{
if ( _enableAutoDeviceName )
{
_deviceName = findSerialDevice();
if ( _deviceName.isEmpty() )
{
return false;
}
}
Info(_log, "Opening UART: %s", _deviceName.toLocal8Bit().constData());
_rs232Port.setPortName(_deviceName);
}
if ( ! _rs232Port.isOpen() )
{
_frameDropCounter = 0;
if (QFile::exists(_deviceName))
{
if ( _preOpenDelayTimeOut > QDateTime::currentMSecsSinceEpoch() )
{
return false;
}
if ( ! _rs232Port.open(QIODevice::ReadWrite) )
{
if ( _stateChanged )
{
Error(_log, "Unable to open RS232 device (%s)", _deviceName.c_str());
Error(_log, "Unable to open RS232 device (%s)", _deviceName.toLocal8Bit().constData());
_stateChanged = false;
}
return false;
@ -135,6 +182,13 @@ bool ProviderRs232::tryOpen(const int delayAfterConnect_ms)
Debug(_log, "Setting baud rate to %d", _baudRate_Hz);
_rs232Port.setBaudRate(_baudRate_Hz);
_stateChanged = true;
_preOpenDelayTimeOut = 0;
}
else
{
_preOpenDelayTimeOut = QDateTime::currentMSecsSinceEpoch() + _preOpenDelay;
return false;
}
}
if (delayAfterConnect_ms > 0)

View File

@ -3,6 +3,7 @@
#include <QObject>
#include <QSerialPort>
#include <QTimer>
#include <QString>
// Leddevice includes
#include <leddevice/LedDevice.h>
@ -39,6 +40,19 @@ public:
///
int open();
private slots:
/// Write the last data to the leds again
int rewriteLeds();
/// Unblock the device after a connection delay
void unblockAfterDelay();
void error(QSerialPort::SerialPortError error);
void bytesWritten(qint64 bytes);
void readyRead();
signals:
void receivedData(QByteArray data);
protected:
/**
* Writes the given bytes to the RS232-device and
@ -52,22 +66,14 @@ protected:
void closeDevice();
private slots:
/// Write the last data to the leds again
int rewriteLeds();
QString findSerialDevice();
/// Unblock the device after a connection delay
void unblockAfterDelay();
void error(QSerialPort::SerialPortError error);
void bytesWritten(qint64 bytes);
void readyRead();
protected:
// tries to open device if not opened
bool tryOpen(const int delayAfterConnect_ms);
/// The name of the output device
std::string _deviceName;
QString _deviceName;
/// The used baudrate of the output device
qint32 _baudRate_Hz;
@ -86,4 +92,7 @@ protected:
qint64 _bytesWritten;
qint64 _frameDropCounter;
QSerialPort::SerialPortError _lastError;
qint64 _preOpenDelayTimeOut;
int _preOpenDelay;
bool _enableAutoDeviceName;
};

View File

@ -5,7 +5,7 @@
"output": {
"type": "string",
"title":"edt_dev_spec_outputPath_title",
"default":"/dev/ttyUSB0",
"default":"/dev/ttyACM0",
"propertyOrder" : 1
},
"rate": {

View File

@ -5,6 +5,7 @@
"output": {
"type": "string",
"title":"edt_dev_spec_outputPath_title",
"default":"/dev/ttyUSB0",
"propertyOrder" : 1
},
"rate": {

View File

@ -5,6 +5,7 @@
"output": {
"type": "string",
"title":"edt_dev_spec_outputPath_title",
"default":"/dev/ttyUSB0",
"propertyOrder" : 1
},
"rate": {
@ -18,13 +19,6 @@
"title":"edt_dev_spec_delayAfterConnect_title",
"default": 250,
"propertyOrder" : 3
},
"rewriteTime": {
"type": "integer",
"title":"edt_dev_spec_rewriteTime_title",
"default": 5000,
"append" : "ms",
"propertyOrder" : 4
}
},
"additionalProperties": true

View File

@ -55,15 +55,15 @@
"type" : "array",
"title" : "edt_dev_spec_whitepoint_title",
"propertyOrder" : 9,
"default" : [1.0,1.0,1.0],
"default" : [255,255,255],
"maxItems" : 3,
"minItems" : 3,
"format" : "table",
"format" : "colorpicker",
"items" : {
"type" : "number",
"minimum" : 0.0,
"maximum": 1.0,
"default" : 1.0
"type" : "integer",
"minimum" : 0,
"maximum": 255,
"default" : 255
}
}
},

View File

@ -5,6 +5,7 @@
"output": {
"type": "string",
"title":"edt_dev_spec_outputPath_title",
"default":"/dev/ttyACM0",
"propertyOrder" : 1
},
"rate": {

View File

@ -5,6 +5,7 @@
"output": {
"type": "string",
"title":"edt_dev_spec_outputPath_title",
"default":"/dev/ttyACM0",
"propertyOrder" : 1
},
"rate": {

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python
import simplejson, sys, glob
import json, sys, glob
from os import path
from jsonschema import Draft3Validator
@ -12,14 +12,14 @@ retval = 0
total = 0
errors = 0
with open("libsrc/effectengine/EffectDefinition.schema.json") as baseSchemaFile:
baseSchema = simplejson.load(baseSchemaFile)
baseSchema = json.loads(baseSchemaFile.read().decode('utf-8-sig'))
baseValidator = Draft3Validator(baseSchema)
for filename in glob.glob(jsonFiles+'/*.json'):
with open(filename) as f:
total += 1
msg = " check effect %s ... " % filename
try:
effect = simplejson.load(f)
effect = json.loads(f.read().decode('utf-8-sig'))
script = path.basename(effect['script'])
if not path.exists(jsonFiles+'/'+script):
raise ValueError('script file: '+script+' not found.')
@ -31,7 +31,7 @@ with open("libsrc/effectengine/EffectDefinition.schema.json") as baseSchemaFile:
# validate against schema
with open(schema) as s:
effectSchema = simplejson.load(s)
effectSchema = json.loads(s.read().decode('utf-8-sig'))
Draft3Validator.check_schema(effectSchema)
validator = Draft3Validator(effectSchema)
baseValidator.validate(effect)

View File

@ -11,10 +11,10 @@ for filename in sys.argv[1:]:
total += 1
msg = " check json %s ... " % filename
try:
json.load(f)
json.loads(f.read().decode('utf-8-sig'))
#print(msg + "ok")
except ValueError as e:
print(msg + 'invalid')
print(msg + 'invalid ('+str(e)+')')
retval = 1
errors += 1

View File

@ -11,8 +11,8 @@ schemaFileName = sys.argv[2]
try:
with open(schemaFileName) as schemaFile:
with open(jsonFileName) as jsonFile:
j = json.load(jsonFile)
validator = Draft3Validator(json.load(schemaFile))
j = json.loads(jsonFile.read().decode('utf-8-sig'))
validator = Draft3Validator(json.loads(schemaFile.read().decode('utf-8-sig')))
validator.validate(j)
except Exception as e:
print('validation error: '+jsonFileName + ' '+schemaFileName+' ('+str(e)+')')