Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for sensors #7

Merged
merged 5 commits into from
Jun 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/RDMSerialRecv/RDMSerialRecv.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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
};


Expand Down
1 change: 1 addition & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ isIdentifyMode KEYWORD2
getStartAddress KEYWORD2
getFootprint KEYWORD2
attachRDMCallback KEYWORD2
attachSensorCallback KEYWORD2
tick KEYWORD2
term KEYWORD2
deviceLabel KEYWORD2
Expand Down
93 changes: 90 additions & 3 deletions src/DMXSerial2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -344,14 +345,15 @@ 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break people's existing code, you should probably either stick it at the end, or do a wrapper version of the old prototype which calls the new one with a NULL.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, added init with "old" signature in header

{
// This structure is defined for mapping the values in the EEPROM
struct EEPROMVALUES eeprom;

// save the given initData for later use.
_initData = initData;
_rdmFunc = func;
_sensorFunc = sensorFunc;

_dmxModePin = modePin;
_dmxModeIn = modeIn;
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -793,8 +800,15 @@ 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) {
_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+6+n+n, _initData->additionalCommands[n]);
WRITEINT(_rdm.packet.Data+offset+n+n, _initData->additionalCommands[n]);
}
handled = true;
}
Expand All @@ -805,6 +819,79 @@ void DMXSerialClass2::_processRDMMessage(byte CmdClass, uint16_t Parameter, bool

// ADD: PARAMETER_DESCRIPTION

} else if (Parameter == SWAPINT(E120_SENSOR_DEFINITION) && _initData->sensorsLength > 0) { // 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also check that sensorNr < 0xff (all sensors) either here, or at init time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... sensorsLength is defined as uint8_t, so the highest possible value would be 0xff anyway. As the "if" here checks for larger or equal, it would return E120_NR_DATA_OUT_OF_RANGE when the user would have defined 255 sensors and sensorNr is 0xff.
Maybe it's too late here (3:38 am), but I think the logic works.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's sort of my point, you can only have 254 sensors, but you could technically pass 255 into the system. It probably really wants to be at init time, but you ought to ignore a sensor 255, as that's reserved for "all sensors".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but you ought to ignore a sensor 255

Isn't this exactly what is happening? If the user should have passed 255 sensors, sensorsLength would also be 255. If then sensor 255 is requested, the library replies with E120_NR_DATA_OUT_OF_RANGE instead of the sensor data, effectively ignoring it.
Also, for any smaller number of defined sensors 255 will result in E120_NR_DATA_OUT_OF_RANGE.

Sorry, but I still do not see the case where this is problematic...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep fair point. I think it needs trapping/overwriting earlier though as otherwise devInfo->sensorCount will be populated by it, which can't be valid.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, double-checking the standard, it says "Valid sensor numbers are in the range from 0x00 – 0xFE. The sensor number 0xFF is used to address all sensors." and "When a device or sub-device is fitted with a single sensor, it would return a value of 0x01 for the Sensor Count. This sensor would then be addressed as Sensor Number 0x00 when using the other sensor-related parameter messages.".

So you can have 255 sensors, which are addressed as 0 to 254.

// 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) && _initData->sensorsLength > 0) { // 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again 0xff.

// 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) {
// 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;
}

} else {
handled = false;

Expand Down
26 changes: 25 additions & 1 deletion src/DMXSerial2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 -----
Expand All @@ -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; //
Expand All @@ -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


Expand All @@ -116,7 +131,10 @@ 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, 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);
Expand Down Expand Up @@ -147,6 +165,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);

Expand All @@ -171,6 +192,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;

Expand Down