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 LPWANetworks 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 Byte1 ByteN Bytes1 Byte1 ByteM Bytes...
Data1 Ch.Data1 TypeData1Data2 Ch.Data2 TypeData 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.

TypeIPSOLPPHexData SizeData Resolution per bit
Digital Input32000011
Digital Output32011111
Analog Input32022220.01
Analog Output32033320.01
Illuminance Sensor33011016521
Presence Sensor33021026611
Temperature Sensor33031036720.1°C Signed MSB
Humidity Sensor33041046810.5 % Unsigned
Accelerometer33131137160.001 G Signed MSB per axis
Barometer33151157320.1 hPa Unsigned MSB
Gyrometer33341348660.01 °/s Signed MSB per axis
GPS Location3336136889Latitude : 0.0001 ° Signed MSB
GPS Location3336136889Longitude : 0.0001 ° Signed MSB
GPS Location3336136889Altitude : 0.01 meter Signed MSB

Examples#

Device with 2 temperature sensors#

Payload (Hex)03 67 01 10 05 67 00 FF
Data ChannelTypeValue
03 ⇒ 367 ⇒ Temperature0110 = 272 ⇒ 27.2°C
05 ⇒ 567 ⇒ Temperature00FF = 255 ⇒ 25.5°C

Device with temperature and acceleration sensors#

Frame N

Payload (Hex)01 67 FF D7
Data ChannelTypeValue
01 ⇒ 167 ⇒ TemperatureFFD7 = -41 ⇒ -4.1°C

Frame N+1

Payload (Hex)06 71 04 D2 FB 2E 00 00
Data ChannelTypeValue
06 ⇒ 671 ⇒ AccelerometerX: 04D2 = +1234 => + 1.234G
06 ⇒ 671 ⇒ AccelerometerY: FB2E = -1234 => - 1.234G
06 ⇒ 671 ⇒ AccelerometerZ: 0000 = 0 => 0G

Device with GPS#

Payload (Hex)01 88 06 76 5f f2 96 0a 00 03 e8
Data ChannelTypeValue
01 ⇒ 188 ⇒ GPSLatitude: 06765f ⇒ 42.3519
01 ⇒ 188 ⇒ GPSLongitude: F2960a ⇒ -87.9094
01 ⇒ 188 ⇒ GPSAltitude: 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;
}