Cayenne Low Power Payload
Cayenne Low Power Payload
Overview
myDevices created the Cayenne Low Power Payload (LPP) which provides a convenient and easy way to send data over LPWAN Networks such as LoRaWAN. The Cayenne LPP is compliant with the payload size restriction, which can be lowered down to 11 bytes, and allows the device to send multiple sensor data at one time.
Additionally, the Cayenne LPP allows the device to send different sensor data in different frames. In order to do that, each sensor data must be prefixed with two bytes:
- Data Channel: Uniquely identifies each sensor in the device across frames, eg. “indoor sensor”
- Data Type: Identifies the data type in the frame, eg. “temperature”.
Libraries
Payload structure
1 Byte | 1 Byte | N Bytes | 1 Byte | 1 Byte | M Bytes | ... |
---|---|---|---|---|---|---|
Data1 Ch. | Data1 Type | Data1 | Data2 Ch. | Data2 Type | Data 2 | ... |
Data Types
Data Types conform to the IPSO Alliance Smart Objects Guidelines, which identifies each data type with an “Object ID”. However, as shown below, a conversion is made to fit the Object ID into a single byte.
LPP_DATA_TYPE = IPSO_OBJECT_ID - 3200
Each data type can use 1 or more bytes to send the data according to the following table.
Type | IPSO | LPP | Hex | Data Size | Data Resolution per bit |
---|---|---|---|---|---|
Digital Input | 3200 | 0 | 0 | 1 | 1 |
Digital Output | 3201 | 1 | 1 | 1 | 1 |
Analog Input | 3202 | 2 | 2 | 2 | 0.01 |
Analog Output | 3203 | 3 | 3 | 2 | 0.01 |
Illuminance Sensor | 3301 | 101 | 65 | 2 | 1 |
Presence Sensor | 3302 | 102 | 66 | 1 | 1 |
Temperature Sensor | 3303 | 103 | 67 | 2 | 0.1°C Signed MSB |
Humidity Sensor | 3304 | 104 | 68 | 1 | 0.5 % Unsigned |
Accelerometer | 3313 | 113 | 71 | 6 | 0.001 G Signed MSB per axis |
Barometer | 3315 | 115 | 73 | 2 | 0.1 hPa Unsigned MSB |
Gyrometer | 3334 | 134 | 86 | 6 | 0.01 °/s Signed MSB per axis |
GPS Location | 3336 | 136 | 88 | 9 | Latitude : 0.0001 ° Signed MSB |
GPS Location | 3336 | 136 | 88 | 9 | Longitude : 0.0001 ° Signed MSB |
GPS Location | 3336 | 136 | 88 | 9 | Altitude : 0.01 meter Signed MSB |
Examples
Device with 2 temperature sensors
Payload (Hex) | 03 67 01 10 05 67 00 FF | |
---|---|---|
Data Channel | Type | Value |
03 ⇒ 3 | 67 ⇒ Temperature | 0110 = 272 ⇒ 27.2°C |
05 ⇒ 5 | 67 ⇒ Temperature | 00FF = 255 ⇒ 25.5°C |
Device with temperature and acceleration sensors
Frame N
Payload (Hex) | 01 67 FF D7 | |
---|---|---|
Data Channel | Type | Value |
01 ⇒ 1 | 67 ⇒ Temperature | FFD7 = -41 ⇒ -4.1°C |
Frame N+1
Payload (Hex) | 06 71 04 D2 FB 2E 00 00 | |
---|---|---|
Data Channel | Type | Value |
06 ⇒ 6 | 71 ⇒ Accelerometer | X: 04D2 = +1234 => + 1.234G |
06 ⇒ 6 | 71 ⇒ Accelerometer | Y: FB2E = -1234 => - 1.234G |
06 ⇒ 6 | 71 ⇒ Accelerometer | Z: 0000 = 0 => 0G |
Device with GPS
Payload (Hex) | 01 88 06 76 5f f2 96 0a 00 03 e8 | |
---|---|---|
Data Channel | Type | Value |
01 ⇒ 1 | 88 ⇒ GPS | Latitude: 06765f ⇒ 42.3519 |
01 ⇒ 1 | 88 ⇒ GPS | Longitude: F2960a ⇒ -87.9094 |
01 ⇒ 1 | 88 ⇒ GPS | Altitude: 0003E8 ⇒ 10 meters |
IPSO Smart Objects Reference
For full information about IPSO Smart Objects, see http://www.ipso-alliance.org/.
IPSO Smart Objects are based on the object model specified in OMA LightWeight M2M [1] Chapter 6,
Identifiers and Resources.
An IPSO Smart Object is a specified collection of reusable resources (See Table 2, Reusable Resources)
that has a well-known object ID (See Table 1, Smart Objects) and which represents a particular type of
physical sensor, actuator, connected object or other data source. The reusable resources,which make up
the Smart Object, represent static and dynamic properties of the connected physical object and the
embedded software contained therein.
This document defines a set of IPSO Smart Objects, which conform to the OMA LWM2MObject Model, and
which can be used as data objects, or web objects, to represent common sensors, actuators, and data
sources.
Although OMA LWM2M is based on the IETF CoAP [2] protocol, these objects may be used with other
transport protocols (e.g. HTTP [3] with REST [4]) by supporting the Content-Types and access methods
defined in [1].
IPSO Smart Objects Guideline - Starter Pack - Version 1.0 ©2014 IPSO Alliance
The following section provides information extracted from the IPSO Smart Objects specifications. It includes all of the Data Types (object ID) that can be used with Cayenne LPP. Therefore, the current implementation is limited to the data types listed in the Data Types section.
Starter Pack Data Types
For full specification, see http://www.ipso-alliance.org/so-starter-pack/.
Expansion Pack Data Types
For full specification, see http://www.ipso-alliance.org/so-expansion-pack/.
Reference Implementation
Cayenne LPP C/C++ constants definitions
#define LPP_DIGITAL_INPUT 0 // 1 byte
#define LPP_DIGITAL_OUTPUT 1 // 1 byte
#define LPP_ANALOG_INPUT 2 // 2 bytes, 0.01 signed
#define LPP_ANALOG_OUTPUT 3 // 2 bytes, 0.01 signed
#define LPP_LUMINOSITY 101 // 2 bytes, 1 lux unsigned
#define LPP_PRESENCE 102 // 1 byte, 1
#define LPP_TEMPERATURE 103 // 2 bytes, 0.1°C signed
#define LPP_RELATIVE_HUMIDITY 104 // 1 byte, 0.5% unsigned
#define LPP_ACCELEROMETER 113 // 2 bytes per axis, 0.001G
#define LPP_BAROMETRIC_PRESSURE 115 // 2 bytes 0.1 hPa Unsigned
#define LPP_GYROMETER 134 // 2 bytes per axis, 0.01 °/s
#define LPP_GPS 136 // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01m
// Data ID + Data Type + Data Size
#define LPP_DIGITAL_INPUT_SIZE 3
#define LPP_DIGITAL_OUTPUT_SIZE 3
#define LPP_ANALOG_INPUT_SIZE 4
#define LPP_ANALOG_OUTPUT_SIZE 4
#define LPP_LUMINOSITY_SIZE 4
#define LPP_PRESENCE_SIZE 3
#define LPP_TEMPERATURE_SIZE 4
#define LPP_RELATIVE_HUMIDITY_SIZE 3
#define LPP_ACCELEROMETER_SIZE 8
#define LPP_BAROMETRIC_PRESSURE_SIZE 4
#define LPP_GYROMETER_SIZE 8
#define LPP_GPS_SIZE 11
Cayenne LPP C++ payload builder
This chapter describes the C++ class definition of the reference myDevices implementation, followed with by the implementation details.
class CayenneLPP {
public:
CayenneLPP(uint8_t size);
~CayenneLPP();
void reset(void);
uint8_t getSize(void);
uint8_t* getBuffer(void);
uint8_t copy(uint8_t* buffer);
uint8_t addDigitalInput(uint8_t channel, uint8_t value);
uint8_t addDigitalOutput(uint8_t channel, uint8_t value);
uint8_t addAnalogInput(uint8_t channel, float value);
uint8_t addAnalogOutput(uint8_t channel, float value);
uint8_t addLuminosity(uint8_t channel, uint16_t lux);
uint8_t addPresence(uint8_t channel, uint8_t value);
uint8_t addTemperature(uint8_t channel, float celsius);
uint8_t addRelativeHumidity(uint8_t channel, float rh);
uint8_t addAccelerometer(uint8_t channel, float x, float y, float z);
uint8_t addBarometricPressure(uint8_t channel, float hpa);
uint8_t addGyrometer(uint8_t channel, float x, float y, float z);
uint8_t addGPS(uint8_t channel, float latitude, float longitude, float meters);
private:
uint8_t *buffer;
uint8_t maxsize;
uint8_t cursor;
};
CayenneLPP::CayenneLPP(uint8_t size) : maxsize(size)
Initialize the payload buffer with the given maximum size.
{
buffer = (uint8_t*) malloc(size);
cursor = 0;
}
CayenneLPP::~CayenneLPP(void)
{
free(buffer);
}
void CayenneLPP::reset(void)
Reset the payload, to call before building a frame payload
{
cursor = 0;
}
uint8_t CayenneLPP::getSize(void)
Returns the current size of the payload
{
return cursor;
}
uint8_t* CayenneLPP::getBuffer(void)
Return the payload buffer
{
return buffer;
}
uint8_t CayenneLPP::copy(uint8_t* dst)
{
memcpy(dst, buffer, cursor);
return cursor;
}
uint8_t CayenneLPP::addDigitalInput(uint8_t channel, uint8_t value)
{
if ((cursor + LPP_DIGITAL_INPUT_SIZE) > maxsize) {
return 0;
}
buffer[cursor++] = channel;
buffer[cursor++] = LPP_DIGITAL_INPUT;
buffer[cursor++] = value;
return cursor;
}
uint8_t CayenneLPP::addDigitalOutput(uint8_t channel, uint8_t value)
{
if ((cursor + LPP_DIGITAL_OUTPUT_SIZE) > maxsize) {
return 0;
}
buffer[cursor++] = channel;
buffer[cursor++] = LPP_DIGITAL_OUTPUT;
buffer[cursor++] = value;
return cursor;
}
uint8_t CayenneLPP::addAnalogInput(uint8_t channel, float value)
{
if ((cursor + LPP_ANALOG_INPUT_SIZE) > maxsize) {
return 0;
}
int16_t val = value * 100;
buffer[cursor++] = channel;
buffer[cursor++] = LPP_ANALOG_INPUT;
buffer[cursor++] = val >> 8;
buffer[cursor++] = val;
return cursor;
}
uint8_t CayenneLPP::addAnalogOutput(uint8_t channel, float value)
{
if ((cursor + LPP_ANALOG_OUTPUT_SIZE) > maxsize) {
return 0;
}
int16_t val = value * 100;
buffer[cursor++] = channel;
buffer[cursor++] = LPP_ANALOG_OUTPUT;
buffer[cursor++] = val >> 8;
buffer[cursor++] = val;
return cursor;
}
uint8_t CayenneLPP::addLuminosity(uint8_t channel, uint16_t lux)
{
if ((cursor + LPP_LUMINOSITY_SIZE) > maxsize) {
return 0;
}
buffer[cursor++] = channel;
buffer[cursor++] = LPP_LUMINOSITY;
buffer[cursor++] = lux >> 8;
buffer[cursor++] = lux;
return cursor;
}
uint8_t CayenneLPP::addPresence(uint8_t channel, uint8_t value)
{
if ((cursor + LPP_PRESENCE_SIZE) > maxsize) {
return 0;
}
buffer[cursor++] = channel;
buffer[cursor++] = LPP_PRESENCE;
buffer[cursor++] = value;
return cursor;
}
uint8_t CayenneLPP::addTemperature(uint8_t channel, float celsius)
{
if ((cursor + LPP_TEMPERATURE_SIZE) > maxsize) {
return 0;
}
int16_t val = celsius * 10;
buffer[cursor++] = channel;
buffer[cursor++] = LPP_TEMPERATURE;
buffer[cursor++] = val >> 8;
buffer[cursor++] = val;
return cursor;
}
uint8_t CayenneLPP::addRelativeHumidity(uint8_t channel, float rh)
{
if ((cursor + LPP_RELATIVE_HUMIDITY_SIZE) > maxsize) {
return 0;
}
buffer[cursor++] = channel;
buffer[cursor++] = LPP_RELATIVE_HUMIDITY;
buffer[cursor++] = rh * 2;
return cursor;
}
uint8_t CayenneLPP::addAccelerometer(uint8_t channel, float x, float y, float z)
{
if ((cursor + LPP_ACCELEROMETER_SIZE) > maxsize) {
return 0;
}
int16_t vx = x * 1000;
int16_t vy = y * 1000;
int16_t vz = z * 1000;
buffer[cursor++] = channel;
buffer[cursor++] = LPP_ACCELEROMETER;
buffer[cursor++] = vx >> 8;
buffer[cursor++] = vx;
buffer[cursor++] = vy >> 8;
buffer[cursor++] = vy;
buffer[cursor++] = vz >> 8;
buffer[cursor++] = vz;
return cursor;
}
uint8_t CayenneLPP::addBarometricPressure(uint8_t channel, float hpa)
{
if ((cursor + LPP_BAROMETRIC_PRESSURE_SIZE) > maxsize) {
return 0;
}
int16_t val = hpa * 10;
buffer[cursor++] = channel;
buffer[cursor++] = LPP_BAROMETRIC_PRESSURE;
buffer[cursor++] = val >> 8;
buffer[cursor++] = val;
return cursor;
}
uint8_t CayenneLPP::addGyrometer(uint8_t channel, float x, float y, float z)
{
if ((cursor + LPP_GYROMETER_SIZE) > maxsize) {
return 0;
}
int16_t vx = x * 100;
int16_t vy = y * 100;
int16_t vz = z * 100;
buffer[cursor++] = channel;
buffer[cursor++] = LPP_GYROMETER;
buffer[cursor++] = vx >> 8;
buffer[cursor++] = vx;
buffer[cursor++] = vy >> 8;
buffer[cursor++] = vy;
buffer[cursor++] = vz >> 8;
buffer[cursor++] = vz;
return cursor;
}
uint8_t CayenneLPP::addGPS(uint8_t channel, float latitude, float longitude, float meters)
{
if ((cursor + LPP_GPS_SIZE) > maxsize) {
return 0;
}
int32_t lat = latitude * 10000;
int32_t lon = longitude * 10000;
int32_t alt = meters * 100;
buffer[cursor++] = channel;
buffer[cursor++] = LPP_GPS;
buffer[cursor++] = lat >> 16;
buffer[cursor++] = lat >> 8;
buffer[cursor++] = lat;
buffer[cursor++] = lon >> 16;
buffer[cursor++] = lon >> 8;
buffer[cursor++] = lon;
buffer[cursor++] = alt >> 16;
buffer[cursor++] = alt >> 8;
buffer[cursor++] = alt;
return cursor;
}