def post(self, deviceUid=None): if (deviceUid): log.info("Posted data for device #%s" % deviceUid) frame = None if (request.content_type.startswith('application/json')): log.info("Received a json LPP: %s" % request.json) args = parser.parse_args(strict=True) log.info("JSON payload: %s" % json.dumps(args)) lppBase64Payload = args['lpp'] frame = LppFrame().from_base64(lppBase64Payload) elif (request.content_type.startswith('application/octet-stream')): lppBinaryPayload = request.data log.info("Received a binary LPP: %s" % lppBinaryPayload) frame = LppFrame().from_bytes(lppBinaryPayload) #lppPayload = ''.join(["%02x" % char for char in lppData]) #log.info("Hex LPP in string format: %s" % lppPayload) if (deviceUid and frame): log.info("Frame LPP: %s" % frame) try: processLpp(deviceUid, frame) except Exception as e: log.error('Error trying to process LPP !', exc_info=e) abort(400, "Unable to process LPP !") return {"message": "ok"}
def uplink_callback(msg, client): print("Uplink message") print(" FROM: ", msg.dev_id) print(" TIME: ", msg.metadata.time) print(" RAW: ", msg.payload_raw) with open(f_logging, 'a') as fl: print(str(msg), file=fl) frame = LppFrame.from_base64(msg.payload_raw) for d in frame.data: print(" DATA:", d) lat = frame.data[0].value[0] lon = frame.data[0].value[1] location = { "name": msg.dev_id, "description": "Continuously updated GPS location of tracker device", "encodingType": "application/vnd.geo+json", "location": { "type": "Feature", "geometry": { "type": "Point", "coordinates": [lon, lat] } } } print(" POST values: ", str(location)) if msg.dev_id in dev_urls: print(" POST URL: ", dev_urls[msg.dev_id]['url']) r = requests.post(dev_urls[msg.dev_id]['url'], json=location) print(" POST status: %d" % r.status_code) else: print(" invalid device: ", msg.dev_id)
def sen frame = LppFrame() frame.add_temperature(0, raw_temp) frame.add_humitidy(1, humidity) frame.add_pressure(2, pressure) frame.add_ buffer = frame.bytes() #print(buffer) lora = Rak811() lora.hard_reset() lora.mode = Mode.LoRaWan lora.band = 'EU868' lora.set_config(app_eui='70B3D57ED001C921', app_key='55B77F583763DCFE02AC75DB77CDD4B1') lora.join_otaa() lora.dr = 5 lora.send(bytes(buffer)) lora.close()
def test_uplink_system_temperature(network_lora, caplog): """ Pretend to invoke the datalogger on a LoPy4 with basic system sensors. Effectively, only the "system.temperature" sensor will be transmitted over LoRa telemetry. By intercepting the lora socket communication, proof that the submitted payload is correct by checking the raw payload value and decoding it through Cayenne. """ # Define artificial LoRa conversation. network_lora.register_conversation() # Invoke datalogger with LoRaWAN telemetry settings for a single duty cycle. from test.settings import telemetry_lorawan invoke_datalogger_pycom(caplog, settings=telemetry_lorawan) # Capture log output. captured = caplog.text # Proof it works by verifying log output. assert "Starting Terkin datalogger" in captured, captured assert "platform: LoPy4" in captured, captured assert "[LoRa] Starting LoRa Manager" in captured, captured assert "Telemetry transport: CayenneLPP over LoRaWAN/TTN" in captured, captured assert "Telemetry status: SUCCESS (1/1)" in captured, captured # Check the raw LoRa payload. from mocket import Mocket assert Mocket.last_request() == bytearray(b'\x00g\x01\xbf\x00\x01\x00') # Check the value after decoding from CayenneLPP. from cayennelpp import LppFrame data = LppFrame.from_bytes(Mocket.last_request()).data # System temperature assert data[0].channel == 0 assert data[0].type == 103 assert data[0].value == (44.7, ) # EOF? assert data[1].channel == 0 assert data[1].type == 1 assert data[1].value == (0, ) assert "[LoRa] No downlink message processed" in captured, captured
def to_cayenne_lpp_ratrack(self): """ Serialize plain data dictionary to binary CayenneLPP format. """ data = self.data from cayennelpp import LppFrame # create empty frame frame = LppFrame() channel = 1 # add some sensor data if 'temperature' in data: try: frame.add_temperature(channel, data.get('temperature')) except: log.exception('[Cayenne] Serialization failed') #if 'humidity' in data: # try: # frame.add_humitidy(channel, data.get('add_humidity')) # except: # log.exception('[Cayenne] Serialization failed') if 'pressure' in data: try: frame.add_barometer(channel, data.get('pressure')) except: log.exception('[Cayenne] Serialization failed') if 'speed' in data and 'roll' in data and 'pitch' in data: try: frame.add_gyrometer(channel, data.get('speed'), data.get('roll'), data.get('pitch')) except: log.exception('[Cayenne] Serialization failed') if 'battery_voltage' in data: try: frame.add_analog_input(channel, data.get('battery_voltage')) except: log.exception('[Cayenne] Serialization failed') if 'latitude' in data and 'longitude' in data and 'altitude' in data: try: log.info('GPS to Cayenne') frame.add_gps(channel, data.get('latitude'), data.get('longitude'), data.get('altitude')) except: log.exception('[Cayenne] Serialization failed') #channel = 2 #if 'cog' in data: # try: # frame.add_analog_input(channel, data.get('cog')) # except: # log.exception('[Cayenne] Serialization failed') channel = 3 if 'memfree' in data: try: frame.add_analog_input(channel, data.get('memfree')) except: log.exception('[Cayenne] Serialization failed') channel = 4 if 'waterlevel_volt' in data: try: frame.add_analog_input(channel, data.get('waterlevel_volt')) except: log.exception('[Cayenne] Serialization failed') channel = 5 if 'moisture_volt' in data: try: frame.add_analog_input(channel, data.get('moisture_volt')) except: log.exception('[Cayenne] Serialization failed') return frame.bytes()
# Most of the setup should happen only once... print('Setup') lora.hard_reset() lora.mode = Mode.LoRaWan lora.band = 'EU868' lora.set_config(dev_addr="", apps_key="", nwks_key="") print('Joining') lora.join_abp() # Note that DR is different from SF and depends on the region # See: https://docs.exploratory.engineering/lora/dr_sf/ # Set Data Rate to 5 which is SF7/125kHz for EU868 lora.dr = 5 while True: frame = LppFrame() print("addgps") frame.add_gps(1,gps.get_latitude(),gps.get_longitude(),gps.get_altitude()) lora.send(bytes(frame.bytes())) sleep(2) print("loop") lora.close()
def to_cayenne_lpp(dataframe: DataFrame): """ Serialize dataframe to binary CayenneLPP format. :param dataframe: """ from cayennelpp import LppFrame frame = LppFrame() channel = {} channel['temp'] = 0 channel['ana_out'] = 0 channel['hum'] = 0 channel['press'] = 0 channel['scale'] = 0 # TODO: Iterate ``dataframe.readings`` to get more metadata from sensor configuration. # It is a list of ``SensorReading`` instances, each having a ``sensor`` and ``data`` attribute. for key, value in dataframe.data_out.items(): #log.debug('-' * 42) # TODO: Maybe implement different naming conventions. name = key.split("_")[0] # try: # channel = int(key.split(":")[1]) # except IndexError: # channel = 0 if "temperature" in name: frame.add_temperature(channel['temp'], value) channel['temp'] += 1 elif "voltage" in name: frame.add_analog_output(channel['ana_out'], value) channel['ana_out'] += 1 elif "humidity" in name: frame.add_humidity(channel['hum'], value) channel['hum'] += 1 elif "pressure" in name: frame.add_barometer(channel['press'], value) channel['press'] += 1 elif "weight" in name: frame.add_analog_input(channel['scale'], value) channel['scale'] += 1 elif "analog-output" in name: frame.add_analog_output(channel, value) elif "analog-input" in name: frame.add_analog_input(channel, value) elif "digital-input" in name: frame.add_digital_input(channel, value) elif "digital_output" in name: frame.add_digital_output(channel, value) elif "illuminance" in name: frame.add_luminosity(channel, value) elif "barometer" in name: frame.add_barometer(channel, value) elif "presence" in name: frame.add_presence(channel, value) elif "accelerometer" in name: frame.add_accelerometer(channel, value) elif "gyrometer" in name: frame.add_gyrometer(channel, value) elif "gps" in name: frame.add_gps(channel, value) # TODO: Fork cayenneLPP and implement load cell telemetry. # TODO: Add load encoder as ID 122 (3322) # http://openmobilealliance.org/wp/OMNA/LwM2M/LwM2MRegistry.html#extlabel # http://www.openmobilealliance.org/tech/profiles/lwm2m/3322.xml # elif False and "load" in name: # frame.add_load(channel, value) # TODO: Map memfree and other baseline sensors appropriately. else: # TODO: raise Exception here? log.info( '[CayenneLPP] Sensor type "{}" not found in CayenneLPP'.format( name)) return frame.bytes()
def test_uplink_environmental_sensors(mocker, network_lora, caplog): """ Pretend to invoke the datalogger on a LoPy4 with environmental sensors. By intercepting the lora socket communication, proof that the submitted payload is correct by checking the raw payload value and decoding it through Cayenne. """ # Define artificial LoRa conversation. network_lora.register_conversation() # Mix together different settings. from test.settings import telemetry_lorawan from test.settings import sensors as sensor_settings mocker.patch('test.settings.telemetry_lorawan.sensors', sensor_settings.sensors) # Invoke datalogger with LoRaWAN telemetry settings for a single duty cycle. invoke_datalogger_pycom(caplog, settings=telemetry_lorawan) # Capture log output. captured = caplog.text # Proof it works by verifying log output. assert "Starting Terkin datalogger" in captured, captured assert "platform: LoPy4" in captured, captured assert "[LoRa] Starting LoRa Manager" in captured, captured assert "Telemetry transport: CayenneLPP over LoRaWAN/TTN" in captured, captured assert "Telemetry status: SUCCESS (1/1)" in captured, captured # Check the raw LoRa payload. from mocket import Mocket assert Mocket.last_request() == bytearray( b'\x00g\x01\xbf\x00\x03\x01\xa4\x00\x02\x01\x80\x01g\x01\xe1\x02g\x01\xe1' b'\x03g\x00\x97\x00s)7\x00h\x9b\x00\x01\x00') # Check the value after decoding from CayenneLPP. from cayennelpp import LppFrame data = LppFrame.from_bytes(Mocket.last_request()).data # System temperature assert data[0].channel == 0 assert data[0].type == 103 assert data[0].value == (44.7, ) # Voltage assert data[1].channel == 0 assert data[1].type == 3 assert data[1].value == (4.2, ) # Weight (kg) assert data[2].channel == 0 assert data[2].type == 2 assert data[2].value == (3.84, ) # DS18B20 temperature assert data[3].channel == 1 assert data[3].type == 103 assert data[3].value == (48.1, ) assert data[4].channel == 2 assert data[4].type == 103 assert data[4].value == (48.1, ) # BME280 temperature assert data[5].channel == 3 assert data[5].type == 103 assert data[5].value == (15.1, ) # BME280 pressure assert data[6].channel == 0 assert data[6].type == 115 assert data[6].value == (1055.1, ) # BME280 humidity assert data[7].channel == 0 assert data[7].type == 104 assert data[7].value == (77.5, ) # EOF? assert data[8].channel == 0 assert data[8].type == 1 assert data[8].value == (0, ) assert "[LoRa] No downlink message processed" in captured, captured
print("APP_KEY = ", str(APP_KEY)) lora.set_config(ch_mask='0,FF00') lora.set_config(ch_mask='1,0000') lora.set_config(ch_mask='2,0000') lora.set_config(ch_mask='3,0000') lora.set_config(ch_mask='4,0000') print('Joining') lora.join_otaa() # Note that DR is different from SF and depends on the region # See: https://docs.exploratory.engineering/lora/dr_sf/ # Set Data Rate to 5 which is SF7/125kHz for EU868 lora.dr = 3 # create empty frame frame = LppFrame() # add some sensor data frame.add_temperature(0, -11.2) frame.add_humidity(0, 30.5) #frame.add_generic(0, 1) # get byte buffer in CayenneLPP format buffer = frame.bytes() print(frame) #print(bytes.fromhex('0102{:04x}'.format(randint(0, 0x7FFF)))) #print(frame.bytes()) print("hexlify") print(bytes.fromhex(str(binascii.hexlify(frame.bytes()).decode()))) lora.send(bytes.fromhex(str(binascii.hexlify(frame.bytes()).decode()))) #print(str(frame.bytes())) #lora.send(bytes.fromhex(binascii.hexlify(frame.bytes()) ), port=11, confirm=True) print('DR', lora.dr)
lora.set_config(ch_mask = '0,FF00') lora.set_config(ch_mask = '1,0000') lora.set_config(ch_mask = '2,0000') lora.set_config(ch_mask = '3,0000') lora.set_config(ch_mask = '4,0000') print('Joining') lora.join_otaa() # Note that DR is different from SF and depends on the region # See: https://docs.exploratory.engineering/lora/dr_sf/ # Set Data Rate to 5 which is SF7/125kHz for EU868 lora.dr = 0 # create empty frame frame = LppFrame() # add some sensor data frame.add_temperature(0, -11.2) frame.add_humidity(0, 30.5) #frame.add_generic(0, 1) # get byte buffer in CayenneLPP format buffer = frame.bytes() print(frame) #print(bytes.fromhex('0102{:04x}'.format(randint(0, 0x7FFF)))) #print(frame.bytes()) print("hexlify") print(bytes.fromhex(str(binascii.hexlify(frame.bytes()).decode()))) lora.send(bytes.fromhex(str(binascii.hexlify(frame.bytes()).decode() ))) #print(str(frame.bytes())) #lora.send(bytes.fromhex(binascii.hexlify(frame.bytes()) ), port=11, confirm=True) print('DR', lora.dr)
def to_cayenne_lpp(data): """ Serialize plain data dictionary to binary CayenneLPP format. """ from cayennelpp import LppFrame frame = LppFrame() for key, value in data.items(): # TODO: Maybe implement different naming conventions. name = key.split("_")[0] try: channel = int(key.split("_")[1]) except IndexError: channel = 0 if "temperature" in name: frame.add_temperature(channel, value) elif "digital-input" in name: frame.add_digital_input(channel, value) elif "digital_output" in name: frame.add_digital_output(channel, value) elif "analog-input" in name: frame.add_analog_input(channel, value) elif "analog-output" in name: frame.add_analog_output(channel, value) elif "illuminance" in name: frame.add_illuminance(channel, value) elif "presence" in name: frame.add_presence(channel, value) elif "humidity" in name: frame.add_humidity(channel, value) elif "accelerometer" in name: frame.add_accelerometer(channel, value) elif "barometer" in name: frame.add_barometer(channel, value) elif "gyrometer" in name: frame.add_gyrometer(channel, value) elif "gps" in name: frame.add_gps(channel, value) # TODO: Fork cayenneLPP and implement load cell telemetry. # TODO: Add load encoder as ID 122 (3322) # http://openmobilealliance.org/wp/OMNA/LwM2M/LwM2MRegistry.html#extlabel # http://www.openmobilealliance.org/tech/profiles/lwm2m/3322.xml elif False and "load" in name: frame.add_load(channel, value) # TODO: Map memfree and other baseline sensors appropriately. else: # TODO: raise Exception here? log.info( '[CayenneLPP] Sensor type "{}" not found in CayenneLPP'.format( name)) return frame.bytes()
def to_cayenne_lpp_hiveeyes(dataframe: DataFrame): """ Serialize dataframe to binary CayenneLPP format. :param dataframe: """ from cayennelpp import LppFrame frame = LppFrame() channel = {} channel['temp'] = 10 channel['volt'] = 2 channel['pres'] = 1 channel['scal'] = 6 # TODO: Iterate ``dataframe.readings`` to get more metadata from sensor configuration. # It is a list of ``SensorReading`` instances, each having a ``sensor`` and ``data`` attribute. # log.info('dataframe.data_out.items : %s', dataframe.data_out.items()) for key, value in dataframe.data_out.items(): #log.debug('-' * 42) # TODO: Maybe implement different naming conventions. name = key.split("_")[0] # channel = 0 if "system" in name: if "voltage" in name: # treat voltage as system sensors # put battery voltage to channel 0 # put solar voltage to channel 1 # put other voltages to channel 2++ if "battery" in name: chan = 0 elif "solar" in name: chan = 1 else: chan = channel['volt'] channel['volt'] += 1 frame.add_voltage(chan, value) elif "temperature" in name: frame.add_temperature(0, value) elif "i2c" in name: # assume BME280 on I2C bus as outside sensor and assign the variables to channel 5 if "temperature" in name: frame.add_temperature(5, value) elif "humidity" in name: frame.add_humidity(5, value) elif "pressure" in name: frame.add_barometer(5, value) elif "weight" in name: # treat weight as outside sensor and assign to channel 5-9 # Channel 5 for single reading (weight.0)) # Channel 6-9 for multiple readings (weight.[1-4]) if name == "weight.0": chan = 5 else: chan = channel['scal'] channel['scal'] += 1 frame.add_load(chan, value) elif "onewire" in name: # assume DS18B20 as inside sensors and assign to channel 10++ if "temperature" in name: frame.add_temperature(channel['temp'], value) channel['temp'] += 1 elif "analog-output" in name: frame.add_analog_output(channel, value) elif "analog-input" in name: frame.add_analog_input(channel, value) elif "digital-input" in name: frame.add_digital_input(channel, value) elif "digital_output" in name: frame.add_digital_output(channel, value) elif "illuminance" in name: frame.add_luminosity(channel, value) elif "barometer" in name: frame.add_barometer(channel, value) elif "presence" in name: frame.add_presence(channel, value) elif "accelerometer" in name: frame.add_accelerometer(channel, value) elif "gyrometer" in name: frame.add_gyrometer(channel, value) #elif "gps" in name: # frame.add_gps(channel, value) # TODO: Fork cayenneLPP and implement load cell telemetry. # TODO: Add load encoder as ID 122 (3322) # http://openmobilealliance.org/wp/OMNA/LwM2M/LwM2MRegistry.html#extlabel # http://www.openmobilealliance.org/tech/profiles/lwm2m/3322.xml # elif False and "load" in name: # frame.add_load(channel, value) # TODO: Map memfree and other baseline sensors appropriately. else: # TODO: raise Exception here? log.info('[CayenneLPP] Sensor type "{}" not found in CayenneLPP'.format(name)) return frame.bytes()
def to_cayenne_lpp_ratrack(dataframe: DataFrame): """ Serialize dataframe to binary CayenneLPP format. :param dataframe: """ from cayennelpp import LppFrame frame = LppFrame() channel = {} channel['temp'] = 10 if ('alt' or 'altitude') and ('lat' or 'longitude') and ('lon' or 'longitude') in dataframe.data_out.keys(): log.info('GOT GPS!') frame.add_gps(0, dataframe.data_out.get('lat'), dataframe.data_out.get('lon'), dataframe.data_out.get('alt')) if 'batterie_volt' in dataframe.data_out.keys(): frame.add_analog_input(14, dataframe.data_out.get('batterie_volt')) for key, value in dataframe.data_out.items(): name = key if "system" in name: pass elif "i2c" in name: # assume BME280 on I2C bus as outside sensor and assign the variables to channel 5 if "temperature" in name: frame.add_temperature(5, value) elif "humidity" in name: frame.add_humidity(5, value) elif "pressure" in name: frame.add_barometer(5, value) elif "onewire" in name: # assume DS18B20 as inside sensors and assign to channel 10++ if "temperature" in name: frame.add_temperature(channel['temp'], value) channel['temp'] += 1 else: # TODO: raise Exception here? #log.info('[CayenneLPP] Sensor type "{}" not found in CayenneLPP'.format(name)) hu1 = 2 return frame.bytes()
from cayennelpp import LppFrame # byte buffer in CayenneLPP format with 1 data item # i.e. on channel 1, with a temperature of 25.5C buffer = bytearray( [0x01, 0x02, 0x01, 0xa8, 0x03, 0x68, 0x07, 0x05, 0x67, 0xfe, 0x5b]) # create frame from bytes frame = LppFrame().from_bytes(buffer) # print the frame and its data print(frame)