├── CayenneLPP.py ├── LICENSE └── README.md /CayenneLPP.py: -------------------------------------------------------------------------------- 1 | # 2 | # This module is basically a rewrite for python for CayenneLPP from TTN Arduino library. 3 | # I did not include the size checks, as I dont think it is needed? 4 | # 5 | # * https://github.com/TheThingsNetwork/arduino-device-lib 6 | # * https://github.com/TheThingsNetwork/arduino-device-lib/blob/master/src/CayenneLPP.cpp 7 | # 8 | import struct 9 | import math 10 | 11 | LPP_DIGITAL_INPUT = 0 # 1 byte 12 | LPP_DIGITAL_OUTPUT = 1 # 1 byte 13 | LPP_ANALOG_INPUT = 2 # 2 bytes, 0.01 signed 14 | LPP_ANALOG_OUTPUT = 3 # 2 bytes, 0.01 signed 15 | LPP_LUMINOSITY = 101 # 2 bytes, 1 lux unsigned 16 | LPP_PRESENCE = 102 # 1 byte, 1 17 | LPP_TEMPERATURE = 103 # 2 bytes, 0.1°C signed 18 | LPP_RELATIVE_HUMIDITY = 104 # 1 byte, 0.5% unsigned 19 | LPP_ACCELEROMETER = 113 # 2 bytes per axis, 0.001G 20 | LPP_BAROMETRIC_PRESSURE = 115 # 2 bytes 0.1 hPa Unsigned 21 | LPP_GYROMETER = 134 # 2 bytes per axis, 0.01 °/s 22 | LPP_GPS = 136 # 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter 23 | 24 | # Data ID + Data Type + Data Size 25 | LPP_DIGITAL_INPUT_SIZE = 3 # 1 byte 26 | LPP_DIGITAL_OUTPUT_SIZE = 3 # 1 byte 27 | LPP_ANALOG_INPUT_SIZE = 4 # 2 bytes, 0.01 signed 28 | LPP_ANALOG_OUTPUT_SIZE = 4 # 2 bytes, 0.01 signed 29 | LPP_LUMINOSITY_SIZE = 4 # 2 bytes, 1 lux unsigned 30 | LPP_PRESENCE_SIZE = 3 # 1 byte, 1 31 | LPP_TEMPERATURE_SIZE = 4 # 2 bytes, 0.1°C signed 32 | LPP_RELATIVE_HUMIDITY_SIZE = 3 # 1 byte, 0.5% unsigned 33 | LPP_ACCELEROMETER_SIZE = 8 # 2 bytes per axis, 0.001G 34 | LPP_BAROMETRIC_PRESSURE_SIZE = 4 # 2 bytes 0.1 hPa Unsigned 35 | LPP_GYROMETER_SIZE = 8 # 2 bytes per axis, 0.01 °/s 36 | LPP_GPS_SIZE = 11 # 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter 37 | 38 | class CayenneLPP: 39 | def __init__(self): 40 | self.buffer = bytearray() 41 | 42 | def get_buffer(self): 43 | return self.buffer 44 | 45 | def reset(self): 46 | self.buffer = bytearray() 47 | 48 | def get_size(self): 49 | return len(self.buffer) 50 | 51 | def add_temperature(self, channel, value): 52 | value = math.floor(value * 10); 53 | 54 | self._add_to_buffer(channel) 55 | self._add_to_buffer(LPP_TEMPERATURE) 56 | self._add_to_buffer(value >> 8) 57 | self._add_to_buffer(value) 58 | 59 | # todo should return 65.4 not 65 in cayenne ui 60 | def add_relative_humidity(self, channel, value): 61 | value = math.floor(value * 2) 62 | 63 | self._add_to_buffer(channel) 64 | self._add_to_buffer(LPP_RELATIVE_HUMIDITY) 65 | self._add_to_buffer(value) 66 | 67 | def add_digital_input(self, channel, value): 68 | self._add_to_buffer(channel) 69 | self._add_to_buffer(LPP_DIGITAL_INPUT) 70 | self._add_to_buffer(value) 71 | 72 | def add_digital_output(self, channel, value): 73 | self._add_to_buffer(channel) 74 | self._add_to_buffer(LPP_DIGITAL_OUTPUT) 75 | self._add_to_buffer(value) 76 | 77 | def add_analog_input(self, channel, value): 78 | value = math.floor(value * 100) 79 | 80 | self._add_to_buffer(channel) 81 | self._add_to_buffer(LPP_ANALOG_INPUT) 82 | self._add_to_buffer(value >> 8) 83 | self._add_to_buffer(value) 84 | 85 | def add_analog_output(self, channel, value): 86 | value = math.floor(value * 100) 87 | 88 | self._add_to_buffer(channel) 89 | self._add_to_buffer(LPP_ANALOG_OUTPUT) 90 | self._add_to_buffer(value >> 8) 91 | self._add_to_buffer(value) 92 | 93 | def add_luminosity(self, channel, value): 94 | self._add_to_buffer(channel) 95 | self._add_to_buffer(LPP_LUMINOSITY) 96 | self._add_to_buffer(value >> 8) 97 | self._add_to_buffer(value) 98 | 99 | def add_presence(self, channel, value): 100 | self._add_to_buffer(channel) 101 | self._add_to_buffer(LPP_PRESENCE) 102 | self._add_to_buffer(value) 103 | 104 | def add_accelerometer(self, channel, x, y, z): 105 | vx = math.floor(x * 1000) 106 | vy = math.floor(y * 1000) 107 | vz = math.floor(z * 1000) 108 | 109 | self._add_to_buffer(channel) 110 | self._add_to_buffer(LPP_ACCELEROMETER) 111 | self._add_to_buffer(vx >> 8) 112 | self._add_to_buffer(vx) 113 | self._add_to_buffer(vy >> 8) 114 | self._add_to_buffer(vy) 115 | self._add_to_buffer(vz >> 8) 116 | self._add_to_buffer(vz) 117 | 118 | def add_barometric_pressure(self, channel, value): 119 | value = math.floor(value * 10) 120 | 121 | self._add_to_buffer(channel) 122 | self._add_to_buffer(LPP_BAROMETRIC_PRESSURE) 123 | self._add_to_buffer(value >> 8) 124 | self._add_to_buffer(value) 125 | 126 | def add_gryrometer(self, channel, x, y, z): 127 | vx = math.floor(x * 100) 128 | vy = math.floor(y * 100) 129 | vz = math.floor(z * 100) 130 | 131 | self._add_to_buffer(channel) 132 | self._add_to_buffer(LPP_GYROMETER) 133 | self._add_to_buffer(vx >> 8) 134 | self._add_to_buffer(vx) 135 | self._add_to_buffer(vy >> 8) 136 | self._add_to_buffer(vy) 137 | self._add_to_buffer(vz >> 8) 138 | self._add_to_buffer(vz) 139 | 140 | def add_gps(self, channel, latitude, longitude, meters): 141 | lat = math.floor(latitude * 10000) 142 | lon = math.floor(longitude * 10000) 143 | alt = math.floor(meters * 100) 144 | 145 | self._add_to_buffer(channel) 146 | self._add_to_buffer(LPP_GPS) 147 | self._add_to_buffer(lat >> 16) 148 | self._add_to_buffer(lat >> 8) 149 | self._add_to_buffer(lat) 150 | self._add_to_buffer(lon >> 16) 151 | self._add_to_buffer(lon >> 8) 152 | self._add_to_buffer(lon) 153 | self._add_to_buffer(alt >> 16) 154 | self._add_to_buffer(alt >> 8) 155 | self._add_to_buffer(alt) 156 | 157 | def _add_to_buffer(self, b): 158 | self.buffer.extend(struct.pack('b', b)) 159 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 simonqbs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cayenne LPP 2 | 3 | Tested on a Lopy running micropython. 4 | 5 | ## Instructions (for LoPy) 6 | 7 | Copy `CayenneLPP.py` to the `lib/` directory and in your `main.py` use 8 | this example code: 9 | 10 | ```python 11 | from CayenneLPP import CayenneLPP 12 | 13 | var cayenne = new CayenneLPP(); 14 | cayenne.add_temperature(1, 23.54); 15 | 16 | # ... lora setup ... 17 | s.send(cayenne.get_buffer()) 18 | ``` 19 | 20 | ## Credits 21 | 22 | Based on: https://github.com/TheThingsNetwork/arduino-device-lib/blob/master/src/CayenneLPP.cpp --------------------------------------------------------------------------------