Skip to content

Commit 67258be

Browse files
Merge pull request #190 from TheThingsNetwork/feature/cayennelpp
CayenneLPP support
2 parents d957c89 + 5407b7c commit 67258be

File tree

4 files changed

+442
-0
lines changed

4 files changed

+442
-0
lines changed

docs/CayenneLPP.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# API Reference
2+
3+
The `CayenneLPP` class enables Arduino devices to encode data with the Cayenne Lower Power Protocol (LPP). [Read more about Cayenne LPP](https://mydevices.com/cayenne/docs/#lora-cayenne-low-power-payload)
4+
5+
## Class: `CayenneLPP`
6+
7+
Include and instantiate the CayenneLPP class. The constructor takes the size of the allocated buffer. Depending on the LoRa frequency plan and data rate used, the maximum payload varies. It's safe to send up to 51 bytes of payload.
8+
9+
```c
10+
#include <CayenneLPP.h>
11+
12+
CayenneLPP lpp(uint8_t size);
13+
```
14+
15+
- `uint8_t size`: The maximum payload size to send, e.g. `51`.
16+
17+
## Example
18+
19+
```c
20+
TheThingsNetwork ttn(loraSerial, debugSerial, freqPlan);
21+
CayenneLPP lpp(51);
22+
23+
lpp.reset();
24+
lpp.addTemperature(1, 22.5);
25+
lpp.addBarometricPressure(2, 1073.21);
26+
lpp.addGPS(3, 52.37365, 4.88650, 2);
27+
28+
ttn.sendBytes(lpp.getBuffer(), lpp.getSize());
29+
```
30+
31+
See the [CayenneLPP](https://github.com/TheThingsNetwork/arduino-device-lib/blob/master/examples/CayenneLPP/CayenneLPP.ino) example.
32+
33+
## Method: `reset`
34+
35+
Resets the buffer.
36+
37+
```c
38+
void reset(void);
39+
```
40+
41+
## Method: `getSize`
42+
43+
Returns the size of the buffer.
44+
45+
```c
46+
uint8_t getSize(void);
47+
```
48+
49+
## Method: `getBuffer`
50+
51+
Returns a pointer to the buffer.
52+
53+
```c
54+
uint8_t *getBuffer(void);
55+
```
56+
57+
## Method: `copy`
58+
59+
Copies the internal buffer to a specified buffer and returns the copied size.
60+
61+
```c
62+
uint8_t copy(uint8_t *buffer);
63+
```
64+
65+
## Methods: `add...`
66+
67+
Add data to the buffer. The `channel` parameter acts as a key for the data field. The data fields you send are dynamic; you can selectively send data as long as the channel matches.
68+
69+
```c
70+
uint8_t addDigitalInput(uint8_t channel, uint8_t value);
71+
uint8_t addDigitalOutput(uint8_t channel, uint8_t value);
72+
73+
uint8_t addAnalogInput(uint8_t channel, float value);
74+
uint8_t addAnalogOutput(uint8_t channel, float value);
75+
76+
uint8_t addLuminosity(uint8_t channel, uint16_t lux);
77+
uint8_t addPresence(uint8_t channel, uint8_t value);
78+
uint8_t addTemperature(uint8_t channel, float celsius);
79+
uint8_t addRelativeHumidity(uint8_t channel, float rh);
80+
uint8_t addAccelerometer(uint8_t channel, float x, float y, float z);
81+
uint8_t addBarometricPressure(uint8_t channel, float hpa);
82+
uint8_t addGyrometer(uint8_t channel, float x, float y, float z);
83+
uint8_t addGPS(uint8_t channel, float latitude, float longitude, float meters);
84+
```

examples/CayenneLPP/CayenneLPP.ino

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <TheThingsNetwork.h>
2+
#include <CayenneLPP.h>
3+
4+
// Set your AppEUI and AppKey
5+
const char *appEui = "0000000000000000";
6+
const char *appKey = "00000000000000000000000000000000";
7+
8+
#define loraSerial Serial1
9+
#define debugSerial Serial
10+
11+
// Replace REPLACE_ME with TTN_FP_EU868 or TTN_FP_US915
12+
#define freqPlan REPLACE_ME
13+
14+
TheThingsNetwork ttn(loraSerial, debugSerial, freqPlan);
15+
CayenneLPP lpp(51);
16+
17+
void setup()
18+
{
19+
loraSerial.begin(57600);
20+
debugSerial.begin(9600);
21+
22+
// Wait a maximum of 10s for Serial Monitor
23+
while (!debugSerial && millis() < 10000)
24+
;
25+
26+
debugSerial.println("-- STATUS");
27+
ttn.showStatus();
28+
29+
debugSerial.println("-- JOIN");
30+
ttn.join(appEui, appKey);
31+
}
32+
33+
void loop()
34+
{
35+
debugSerial.println("-- LOOP");
36+
37+
lpp.reset();
38+
lpp.addTemperature(1, 22.5);
39+
lpp.addBarometricPressure(2, 1073.21);
40+
lpp.addGPS(3, 52.37365, 4.88650, 2);
41+
42+
// Send it off
43+
ttn.sendBytes(lpp.getBuffer(), lpp.getSize());
44+
45+
delay(10000);
46+
}

src/CayenneLPP.cpp

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
// Adapted from https://developer.mbed.org/teams/myDevicesIoT/code/Cayenne-LPP/
2+
3+
// Copyright © 2017 The Things Network
4+
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.
5+
6+
#include "CayenneLPP.h"
7+
8+
CayenneLPP::CayenneLPP(uint8_t size) : maxsize(size)
9+
{
10+
buffer = (uint8_t *)malloc(size);
11+
cursor = 0;
12+
}
13+
14+
CayenneLPP::~CayenneLPP(void)
15+
{
16+
free(buffer);
17+
}
18+
19+
void CayenneLPP::reset(void)
20+
{
21+
cursor = 0;
22+
}
23+
24+
uint8_t CayenneLPP::getSize(void)
25+
{
26+
return cursor;
27+
}
28+
29+
uint8_t *CayenneLPP::getBuffer(void)
30+
{
31+
// uint8_t[cursor] result;
32+
// memcpy(result, buffer, cursor);
33+
// return result;
34+
return buffer;
35+
}
36+
37+
uint8_t CayenneLPP::copy(uint8_t *dst)
38+
{
39+
memcpy(dst, buffer, cursor);
40+
return cursor;
41+
}
42+
43+
uint8_t CayenneLPP::addDigitalInput(uint8_t channel, uint8_t value)
44+
{
45+
if ((cursor + LPP_DIGITAL_INPUT_SIZE) > maxsize)
46+
{
47+
return 0;
48+
}
49+
buffer[cursor++] = channel;
50+
buffer[cursor++] = LPP_DIGITAL_INPUT;
51+
buffer[cursor++] = value;
52+
53+
return cursor;
54+
}
55+
56+
uint8_t CayenneLPP::addDigitalOutput(uint8_t channel, uint8_t value)
57+
{
58+
if ((cursor + LPP_DIGITAL_OUTPUT_SIZE) > maxsize)
59+
{
60+
return 0;
61+
}
62+
buffer[cursor++] = channel;
63+
buffer[cursor++] = LPP_DIGITAL_OUTPUT;
64+
buffer[cursor++] = value;
65+
66+
return cursor;
67+
}
68+
69+
uint8_t CayenneLPP::addAnalogInput(uint8_t channel, float value)
70+
{
71+
if ((cursor + LPP_ANALOG_INPUT_SIZE) > maxsize)
72+
{
73+
return 0;
74+
}
75+
76+
int16_t val = value * 100;
77+
buffer[cursor++] = channel;
78+
buffer[cursor++] = LPP_ANALOG_INPUT;
79+
buffer[cursor++] = val >> 8;
80+
buffer[cursor++] = val;
81+
82+
return cursor;
83+
}
84+
85+
uint8_t CayenneLPP::addAnalogOutput(uint8_t channel, float value)
86+
{
87+
if ((cursor + LPP_ANALOG_OUTPUT_SIZE) > maxsize)
88+
{
89+
return 0;
90+
}
91+
int16_t val = value * 100;
92+
buffer[cursor++] = channel;
93+
buffer[cursor++] = LPP_ANALOG_OUTPUT;
94+
buffer[cursor++] = val >> 8;
95+
buffer[cursor++] = val;
96+
97+
return cursor;
98+
}
99+
100+
uint8_t CayenneLPP::addLuminosity(uint8_t channel, uint16_t lux)
101+
{
102+
if ((cursor + LPP_LUMINOSITY_SIZE) > maxsize)
103+
{
104+
return 0;
105+
}
106+
buffer[cursor++] = channel;
107+
buffer[cursor++] = LPP_LUMINOSITY;
108+
buffer[cursor++] = lux >> 8;
109+
buffer[cursor++] = lux;
110+
111+
return cursor;
112+
}
113+
114+
uint8_t CayenneLPP::addPresence(uint8_t channel, uint8_t value)
115+
{
116+
if ((cursor + LPP_PRESENCE_SIZE) > maxsize)
117+
{
118+
return 0;
119+
}
120+
buffer[cursor++] = channel;
121+
buffer[cursor++] = LPP_PRESENCE;
122+
buffer[cursor++] = value;
123+
124+
return cursor;
125+
}
126+
127+
uint8_t CayenneLPP::addTemperature(uint8_t channel, float celsius)
128+
{
129+
if ((cursor + LPP_TEMPERATURE_SIZE) > maxsize)
130+
{
131+
return 0;
132+
}
133+
int16_t val = celsius * 10;
134+
buffer[cursor++] = channel;
135+
buffer[cursor++] = LPP_TEMPERATURE;
136+
buffer[cursor++] = val >> 8;
137+
buffer[cursor++] = val;
138+
139+
return cursor;
140+
}
141+
142+
uint8_t CayenneLPP::addRelativeHumidity(uint8_t channel, float rh)
143+
{
144+
if ((cursor + LPP_RELATIVE_HUMIDITY_SIZE) > maxsize)
145+
{
146+
return 0;
147+
}
148+
buffer[cursor++] = channel;
149+
buffer[cursor++] = LPP_RELATIVE_HUMIDITY;
150+
buffer[cursor++] = rh * 2;
151+
152+
return cursor;
153+
}
154+
155+
uint8_t CayenneLPP::addAccelerometer(uint8_t channel, float x, float y, float z)
156+
{
157+
if ((cursor + LPP_ACCELEROMETER_SIZE) > maxsize)
158+
{
159+
return 0;
160+
}
161+
int16_t vx = x * 1000;
162+
int16_t vy = y * 1000;
163+
int16_t vz = z * 1000;
164+
165+
buffer[cursor++] = channel;
166+
buffer[cursor++] = LPP_ACCELEROMETER;
167+
buffer[cursor++] = vx >> 8;
168+
buffer[cursor++] = vx;
169+
buffer[cursor++] = vy >> 8;
170+
buffer[cursor++] = vy;
171+
buffer[cursor++] = vz >> 8;
172+
buffer[cursor++] = vz;
173+
174+
return cursor;
175+
}
176+
177+
uint8_t CayenneLPP::addBarometricPressure(uint8_t channel, float hpa)
178+
{
179+
if ((cursor + LPP_BAROMETRIC_PRESSURE_SIZE) > maxsize)
180+
{
181+
return 0;
182+
}
183+
int16_t val = hpa * 10;
184+
185+
buffer[cursor++] = channel;
186+
buffer[cursor++] = LPP_BAROMETRIC_PRESSURE;
187+
buffer[cursor++] = val >> 8;
188+
buffer[cursor++] = val;
189+
190+
return cursor;
191+
}
192+
193+
uint8_t CayenneLPP::addGyrometer(uint8_t channel, float x, float y, float z)
194+
{
195+
if ((cursor + LPP_GYROMETER_SIZE) > maxsize)
196+
{
197+
return 0;
198+
}
199+
int16_t vx = x * 100;
200+
int16_t vy = y * 100;
201+
int16_t vz = z * 100;
202+
203+
buffer[cursor++] = channel;
204+
buffer[cursor++] = LPP_GYROMETER;
205+
buffer[cursor++] = vx >> 8;
206+
buffer[cursor++] = vx;
207+
buffer[cursor++] = vy >> 8;
208+
buffer[cursor++] = vy;
209+
buffer[cursor++] = vz >> 8;
210+
buffer[cursor++] = vz;
211+
212+
return cursor;
213+
}
214+
215+
uint8_t CayenneLPP::addGPS(uint8_t channel, float latitude, float longitude, float meters)
216+
{
217+
if ((cursor + LPP_GPS_SIZE) > maxsize)
218+
{
219+
return 0;
220+
}
221+
int32_t lat = latitude * 10000;
222+
int32_t lon = longitude * 10000;
223+
int32_t alt = meters * 100;
224+
225+
buffer[cursor++] = channel;
226+
buffer[cursor++] = LPP_GPS;
227+
228+
buffer[cursor++] = lat >> 16;
229+
buffer[cursor++] = lat >> 8;
230+
buffer[cursor++] = lat;
231+
buffer[cursor++] = lon >> 16;
232+
buffer[cursor++] = lon >> 8;
233+
buffer[cursor++] = lon;
234+
buffer[cursor++] = alt >> 16;
235+
buffer[cursor++] = alt >> 8;
236+
buffer[cursor++] = alt;
237+
238+
return cursor;
239+
}

0 commit comments

Comments
 (0)