From 8f989f512702c6bdfd40e4b13c1ef5b2d22ff9b0 Mon Sep 17 00:00:00 2001 From: Stefan Krupop Date: Thu, 25 May 2017 20:24:12 +0200 Subject: [PATCH 1/5] - Added description of sensors - Added callback to retrieve sensor values --- examples/RDMSerialRecv/RDMSerialRecv.ino | 3 +- keywords.txt | 1 + src/DMXSerial2.cpp | 83 +++++++++++++++++++++++- src/DMXSerial2.h | 23 ++++++- 4 files changed, 106 insertions(+), 4 deletions(-) diff --git a/examples/RDMSerialRecv/RDMSerialRecv.ino b/examples/RDMSerialRecv/RDMSerialRecv.ino index 20857ba..37737e5 100644 --- a/examples/RDMSerialRecv/RDMSerialRecv.ino +++ b/examples/RDMSerialRecv/RDMSerialRecv.ino @@ -62,7 +62,8 @@ struct RDMINIT rdmInit = { 1, // Device Model ID "Arduino RDM Device", // Device Model Label 3, // footprint - (sizeof(my_pids)/sizeof(uint16_t)), my_pids + (sizeof(my_pids)/sizeof(uint16_t)), my_pids, + 0, NULL }; diff --git a/keywords.txt b/keywords.txt index 6282b18..bbd0c1c 100644 --- a/keywords.txt +++ b/keywords.txt @@ -30,6 +30,7 @@ isIdentifyMode KEYWORD2 getStartAddress KEYWORD2 getFootprint KEYWORD2 attachRDMCallback KEYWORD2 +attachSensorCallback KEYWORD2 tick KEYWORD2 term KEYWORD2 deviceLabel KEYWORD2 diff --git a/src/DMXSerial2.cpp b/src/DMXSerial2.cpp index 5e25afb..3fca45e 100644 --- a/src/DMXSerial2.cpp +++ b/src/DMXSerial2.cpp @@ -44,6 +44,7 @@ // 12.04.2015 change of using datatype boolean to bool8. // 15.06.2015 On DMX lines sometimes a BREAK condition occures inbetween RDM packets from the controller // and the device response. Ignore that when no data has arrived. +// 25.05.2017 Stefan Krupop: Add support for sensors // - - - - - #include "Arduino.h" @@ -344,7 +345,7 @@ int random255(); // Initialize or reinitialize the DMX RDM mode. // The other values are stored for later use with the specific commands. -void DMXSerialClass2::init(struct RDMINIT *initData, RDMCallbackFunction func, uint8_t modePin, uint8_t modeIn, uint8_t modeOut) +void DMXSerialClass2::init(struct RDMINIT *initData, RDMCallbackFunction func, RDMGetSensorValue sensorFunc, uint8_t modePin, uint8_t modeIn, uint8_t modeOut) { // This structure is defined for mapping the values in the EEPROM struct EEPROMVALUES eeprom; @@ -352,6 +353,7 @@ void DMXSerialClass2::init(struct RDMINIT *initData, RDMCallbackFunction func, u // save the given initData for later use. _initData = initData; _rdmFunc = func; + _sensorFunc = sensorFunc; _dmxModePin = modePin; _dmxModeIn = modeIn; @@ -452,6 +454,11 @@ void DMXSerialClass2::attachRDMCallback(RDMCallbackFunction newFunction) _rdmFunc = newFunction; } // attachRDMCallback +// Register a self implemented function to get sensor values +void DMXSerialClass2::attachSensorCallback(RDMGetSensorValue newFunction) +{ + _sensorFunc = newFunction; +} // attachSensorCallback // some functions to hide the internal variables from beeing changed @@ -663,7 +670,7 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool devInfo->personalityCount = 1; devInfo->startAddress = SWAPINT(_startAddress); devInfo->subDeviceCount = 0; - devInfo->sensorCount = 0; + devInfo->sensorCount = _initData->sensorsLength; _rdm.packet.DataLength = sizeof(DEVICEINFO); handled = true; @@ -805,6 +812,78 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool // ADD: PARAMETER_DESCRIPTION + } else if (Parameter == SWAPINT(E120_SENSOR_DEFINITION)) { // 0x0200 + if (CmdClass == E120_GET_COMMAND) { + if (_rdm.packet.DataLength != 1) { + // Unexpected data + nackReason = E120_NR_FORMAT_ERROR; + } else if (_rdm.packet.SubDev != 0) { + // No sub-devices supported + nackReason = E120_NR_SUB_DEVICE_OUT_OF_RANGE; + } else { + uint8_t sensorNr = _rdm.packet.Data[0]; + if (sensorNr >= _initData->sensorsLength) { + // Out of range sensor + nackReason = E120_NR_DATA_OUT_OF_RANGE; + } else { + _rdm.packet.DataLength = 13 + strlen(_initData->sensors[sensorNr].description); + _rdm.packet.Data[0] = sensorNr; + _rdm.packet.Data[1] = _initData->sensors[sensorNr].type; + _rdm.packet.Data[2] = _initData->sensors[sensorNr].unit; + _rdm.packet.Data[3] = _initData->sensors[sensorNr].prefix; + WRITEINT(_rdm.packet.Data + 4, _initData->sensors[sensorNr].rangeMin); + WRITEINT(_rdm.packet.Data + 6, _initData->sensors[sensorNr].rangeMax); + WRITEINT(_rdm.packet.Data + 8, _initData->sensors[sensorNr].normalMin); + WRITEINT(_rdm.packet.Data + 10, _initData->sensors[sensorNr].normalMax); + _rdm.packet.Data[12] = (_initData->sensors[sensorNr].lowHighSupported ? 2 : 0) | (_initData->sensors[sensorNr].recordedSupported ? 1 : 0); + memcpy(_rdm.packet.Data + 13, _initData->sensors[sensorNr].description, _rdm.packet.DataLength - 13); + handled = true; + } + } + } else if (CmdClass == E120_SET_COMMAND) { + // Unexpected set + nackReason = E120_NR_UNSUPPORTED_COMMAND_CLASS; + } + } else if (Parameter == SWAPINT(E120_SENSOR_VALUE)) { // 0x0201 + if (CmdClass == E120_GET_COMMAND) { + if (_rdm.packet.DataLength != 1) { + // Unexpected data + nackReason = E120_NR_FORMAT_ERROR; + } else if (_rdm.packet.SubDev != 0) { + // No sub-devices supported + nackReason = E120_NR_SUB_DEVICE_OUT_OF_RANGE; + } else { + uint8_t sensorNr = _rdm.packet.Data[0]; + if (sensorNr >= _initData->sensorsLength) { + // Out of range sensor + nackReason = E120_NR_DATA_OUT_OF_RANGE; + } else { + int16_t sensorValue = 0; + int16_t lowestValue = 0; + int16_t highestValue = 0; + int16_t recordedValue = 0; + bool8 res = false; + if (_sensorFunc) { + res = _sensorFunc(sensorNr, &sensorValue, &lowestValue, &highestValue, &recordedValue); + } + if (res) { + _rdm.packet.DataLength = 9; + _rdm.packet.Data[0] = sensorNr; + WRITEINT(_rdm.packet.Data + 1, sensorValue); + WRITEINT(_rdm.packet.Data + 3, lowestValue); + WRITEINT(_rdm.packet.Data + 5, highestValue); + WRITEINT(_rdm.packet.Data + 7, recordedValue); + handled = true; + } else { + nackReason = E120_NR_HARDWARE_FAULT; + } + } + } + } else if (CmdClass == E120_SET_COMMAND) { + // Unexpected set + nackReason = E120_NR_UNSUPPORTED_COMMAND_CLASS; + } + } else { handled = false; diff --git a/src/DMXSerial2.h b/src/DMXSerial2.h index b67244b..2d52b65 100644 --- a/src/DMXSerial2.h +++ b/src/DMXSerial2.h @@ -86,6 +86,7 @@ struct RDMDATA { extern "C" { typedef bool8 (*RDMCallbackFunction)(struct RDMDATA *buffer, uint16_t *nackReason); + typedef bool8 (*RDMGetSensorValue)(uint8_t sensorNr, int16_t *value, int16_t *lowestValue, int16_t *highestValue, int16_t *recordedValue); } // ----- Library Class ----- @@ -98,6 +99,18 @@ struct RDMPERSONALITY { // maybe more here... when supporting more personalitites. }; // struct RDMPERSONALITY +struct RDMSENSOR { + uint8_t type; + uint8_t unit; + uint8_t prefix; + int16_t rangeMin; + int16_t rangeMax; + int16_t normalMin; + int16_t normalMax; + bool8 lowHighSupported; + bool8 recordedSupported; + char *description; +}; // struct RDMSENSOR struct RDMINIT { char *manufacturerLabel; // @@ -108,6 +121,8 @@ struct RDMINIT { // RDMPERSONALITY *personalities; const uint16_t additionalCommandsLength; const uint16_t *additionalCommands; + const uint8_t sensorsLength; + const RDMSENSOR *sensors; }; // struct RDMINIT @@ -116,7 +131,7 @@ class DMXSerialClass2 { public: // Initialize for RDM mode. - void init (struct RDMINIT *initData, RDMCallbackFunction func, uint8_t modePin = 2, uint8_t modeIn = 0, uint8_t modeOut = 1); + void init (struct RDMINIT *initData, RDMCallbackFunction func, RDMGetSensorValue sensorFunc = NULL, uint8_t modePin = 2, uint8_t modeIn = 0, uint8_t modeOut = 1); // Read the last known value of a channel. uint8_t read (int channel); @@ -147,6 +162,9 @@ class DMXSerialClass2 // Register a device-specific implemented function for RDM callbacks void attachRDMCallback (RDMCallbackFunction newFunction); + // Register a device-specific implemented function for getting sensor values + void attachSensorCallback (RDMGetSensorValue newFunction); + // check for unprocessed RDM Command. void tick(void); @@ -171,6 +189,9 @@ class DMXSerialClass2 // callback function to device specific code RDMCallbackFunction _rdmFunc; + // callback function to get sensor value + RDMGetSensorValue _sensorFunc; + // remember the given manufacturer label and device model strings during init struct RDMINIT *_initData; From a4880bb0f4e7e95c482e8b904ec57c7e94539781 Mon Sep 17 00:00:00 2001 From: Stefan Krupop Date: Thu, 25 May 2017 20:46:22 +0200 Subject: [PATCH 2/5] Added E120_SENSOR_DEFINITION and E120_SENSOR_VALUE to E120_SUPPORTED_PARAMETERS reply --- src/DMXSerial2.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/DMXSerial2.cpp b/src/DMXSerial2.cpp index 3fca45e..60a7463 100644 --- a/src/DMXSerial2.cpp +++ b/src/DMXSerial2.cpp @@ -796,12 +796,14 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool // E120_DEVICE_INFO // E120_DMX_START_ADDRESS // E120_SOFTWARE_VERSION_LABEL - _rdm.packet.DataLength = 2 * (3 + _initData->additionalCommandsLength); + _rdm.packet.DataLength = 2 * (5 + _initData->additionalCommandsLength); WRITEINT(_rdm.packet.Data , E120_MANUFACTURER_LABEL); WRITEINT(_rdm.packet.Data+ 2, E120_DEVICE_MODEL_DESCRIPTION); WRITEINT(_rdm.packet.Data+ 4, E120_DEVICE_LABEL); + WRITEINT(_rdm.packet.Data+ 6, E120_SENSOR_DEFINITION); + WRITEINT(_rdm.packet.Data+ 8, E120_SENSOR_VALUE); for (int n = 0; n < _initData->additionalCommandsLength; n++) { - WRITEINT(_rdm.packet.Data+6+n+n, _initData->additionalCommands[n]); + WRITEINT(_rdm.packet.Data+10+n+n, _initData->additionalCommands[n]); } handled = true; } From 175db2a3da8bf95ca67410ab536d89db7fe6e2a6 Mon Sep 17 00:00:00 2001 From: Stefan Krupop Date: Sun, 28 May 2017 03:33:08 +0200 Subject: [PATCH 3/5] - Added init method with old signature - Only report E120_SENSOR_DEFINITION and E120_SENSOR_VALUE when sensors are defined - Fixed comment for settings sensor values --- src/DMXSerial2.cpp | 16 +++++++++++----- src/DMXSerial2.h | 5 ++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/DMXSerial2.cpp b/src/DMXSerial2.cpp index 60a7463..3164ddc 100644 --- a/src/DMXSerial2.cpp +++ b/src/DMXSerial2.cpp @@ -796,14 +796,19 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool // E120_DEVICE_INFO // E120_DMX_START_ADDRESS // E120_SOFTWARE_VERSION_LABEL - _rdm.packet.DataLength = 2 * (5 + _initData->additionalCommandsLength); + _rdm.packet.DataLength = 2 * (3 + _initData->additionalCommandsLength); WRITEINT(_rdm.packet.Data , E120_MANUFACTURER_LABEL); WRITEINT(_rdm.packet.Data+ 2, E120_DEVICE_MODEL_DESCRIPTION); WRITEINT(_rdm.packet.Data+ 4, E120_DEVICE_LABEL); - WRITEINT(_rdm.packet.Data+ 6, E120_SENSOR_DEFINITION); - WRITEINT(_rdm.packet.Data+ 8, E120_SENSOR_VALUE); + uint8_t offset = 6; + if (_initData->sensorsLength > 0) { + _rdm.packet.DataLength += 2 * 2; + offset += 2 * 2; + WRITEINT(_rdm.packet.Data+ 6, E120_SENSOR_DEFINITION); + WRITEINT(_rdm.packet.Data+ 8, E120_SENSOR_VALUE); + } for (int n = 0; n < _initData->additionalCommandsLength; n++) { - WRITEINT(_rdm.packet.Data+10+n+n, _initData->additionalCommands[n]); + WRITEINT(_rdm.packet.Data+offset+n+n, _initData->additionalCommands[n]); } handled = true; } @@ -882,7 +887,8 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool } } } else if (CmdClass == E120_SET_COMMAND) { - // Unexpected set + // Unhandled set. Set on a sensor is used to reset stats. + // User should process it in own handler when sensor supports high/low or recorded value. nackReason = E120_NR_UNSUPPORTED_COMMAND_CLASS; } diff --git a/src/DMXSerial2.h b/src/DMXSerial2.h index 2d52b65..b0d525f 100644 --- a/src/DMXSerial2.h +++ b/src/DMXSerial2.h @@ -131,7 +131,10 @@ class DMXSerialClass2 { public: // Initialize for RDM mode. - void init (struct RDMINIT *initData, RDMCallbackFunction func, RDMGetSensorValue sensorFunc = NULL, uint8_t modePin = 2, uint8_t modeIn = 0, uint8_t modeOut = 1); + void init (struct RDMINIT *initData, RDMCallbackFunction func, uint8_t modePin = 2, uint8_t modeIn = 0, uint8_t modeOut = 1) { + init(initData, func, NULL, modePin, modeIn, modeOut); + } + void init (struct RDMINIT *initData, RDMCallbackFunction func, RDMGetSensorValue sensorFunc, uint8_t modePin = 2, uint8_t modeIn = 0, uint8_t modeOut = 1); // Read the last known value of a channel. uint8_t read (int channel); From 6a15c43d29180a71bc8320f9203ee34448dca404 Mon Sep 17 00:00:00 2001 From: Stefan Krupop Date: Sun, 28 May 2017 09:42:36 +0200 Subject: [PATCH 4/5] Fixed tab indentation --- src/DMXSerial2.cpp | 10 +++++----- src/DMXSerial2.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/DMXSerial2.cpp b/src/DMXSerial2.cpp index 3164ddc..44a8362 100644 --- a/src/DMXSerial2.cpp +++ b/src/DMXSerial2.cpp @@ -800,13 +800,13 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool WRITEINT(_rdm.packet.Data , E120_MANUFACTURER_LABEL); WRITEINT(_rdm.packet.Data+ 2, E120_DEVICE_MODEL_DESCRIPTION); WRITEINT(_rdm.packet.Data+ 4, E120_DEVICE_LABEL); - uint8_t offset = 6; - if (_initData->sensorsLength > 0) { + uint8_t offset = 6; + if (_initData->sensorsLength > 0) { _rdm.packet.DataLength += 2 * 2; - offset += 2 * 2; + offset += 2 * 2; WRITEINT(_rdm.packet.Data+ 6, E120_SENSOR_DEFINITION); WRITEINT(_rdm.packet.Data+ 8, E120_SENSOR_VALUE); - } + } for (int n = 0; n < _initData->additionalCommandsLength; n++) { WRITEINT(_rdm.packet.Data+offset+n+n, _initData->additionalCommands[n]); } @@ -888,7 +888,7 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool } } else if (CmdClass == E120_SET_COMMAND) { // Unhandled set. Set on a sensor is used to reset stats. - // User should process it in own handler when sensor supports high/low or recorded value. + // User should process it in own handler when sensor supports high/low or recorded value. nackReason = E120_NR_UNSUPPORTED_COMMAND_CLASS; } diff --git a/src/DMXSerial2.h b/src/DMXSerial2.h index b0d525f..988e38d 100644 --- a/src/DMXSerial2.h +++ b/src/DMXSerial2.h @@ -132,8 +132,8 @@ class DMXSerialClass2 public: // Initialize for RDM mode. void init (struct RDMINIT *initData, RDMCallbackFunction func, uint8_t modePin = 2, uint8_t modeIn = 0, uint8_t modeOut = 1) { - init(initData, func, NULL, modePin, modeIn, modeOut); - } + init(initData, func, NULL, modePin, modeIn, modeOut); + } void init (struct RDMINIT *initData, RDMCallbackFunction func, RDMGetSensorValue sensorFunc, uint8_t modePin = 2, uint8_t modeIn = 0, uint8_t modeOut = 1); // Read the last known value of a channel. From 9d639527295ae77db607dd6ca8e0c58be68d4881 Mon Sep 17 00:00:00 2001 From: Stefan Krupop Date: Sun, 28 May 2017 14:18:46 +0200 Subject: [PATCH 5/5] Do not handle E120_SENSOR_DEFINITION and E120_SENSOR_VALUE when there are no sensors defined --- src/DMXSerial2.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DMXSerial2.cpp b/src/DMXSerial2.cpp index 44a8362..693aeb5 100644 --- a/src/DMXSerial2.cpp +++ b/src/DMXSerial2.cpp @@ -819,7 +819,7 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool // ADD: PARAMETER_DESCRIPTION - } else if (Parameter == SWAPINT(E120_SENSOR_DEFINITION)) { // 0x0200 + } else if (Parameter == SWAPINT(E120_SENSOR_DEFINITION) && _initData->sensorsLength > 0) { // 0x0200 if (CmdClass == E120_GET_COMMAND) { if (_rdm.packet.DataLength != 1) { // Unexpected data @@ -851,7 +851,7 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool // Unexpected set nackReason = E120_NR_UNSUPPORTED_COMMAND_CLASS; } - } else if (Parameter == SWAPINT(E120_SENSOR_VALUE)) { // 0x0201 + } else if (Parameter == SWAPINT(E120_SENSOR_VALUE) && _initData->sensorsLength > 0) { // 0x0201 if (CmdClass == E120_GET_COMMAND) { if (_rdm.packet.DataLength != 1) { // Unexpected data