diff --git a/ESP8266_Transmitter/_transmission.h b/ESP8266_Transmitter/_transmission.h index fdfd00828685edb6f694986a6e160033e7354552..6495c3dd5b8f9ba69fa730c22ff12ecfd1d450e5 100644 --- a/ESP8266_Transmitter/_transmission.h +++ b/ESP8266_Transmitter/_transmission.h @@ -155,7 +155,7 @@ boolean listenForTxPayloadAccept(int listenDuration, int messageID) { // Verify Sensor UID Match & Message ID if (authIsForUid != getSensorUID() || authIsForMessageID != messageID) { - // TODO Else UID Mis-Match so we wait for next message + // UID Mis-Match so we wait for next message if (DEBUG) { Serial.println("[-] Transmit - Payload - Ack Message ID or Sensor ID Mis-Match"); } return false; } diff --git a/RaspberryPi_Receiver/Pi_Receiver.py b/RaspberryPi_Receiver/Pi_Receiver.py index f1c37e10e4de7ed03510815eeda526d6b78e5502..bcbd745fbd14a17daf995a84da4f04e583d331d9 100644 --- a/RaspberryPi_Receiver/Pi_Receiver.py +++ b/RaspberryPi_Receiver/Pi_Receiver.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -''' +""" ========================================================================================= CS408 Environmental Monitoring Independent of Existing Infrastructure @@ -28,141 +28,199 @@ https://github.com/raspberrypi-tw/lora-sx1276 ========================================================================================= -''' -# Usage: python Pi_Receiver.py -f 433 -b BW125 -s 7 - -from argparse import ArgumentError -import sys -sys.path.insert(0, '../') + Usage: python Pi_Receiver.py -f 433 -b BW125 -s 7 + ========================================================================================= +""" +import sys from time import sleep, time import json -import requests # pip3 install requests -import random - +import requests # pip3 install requests from SX127x.LoRa import * from SX127x.LoRaArgumentParser import LoRaArgumentParser from SX127x.board_config import BOARD import SX127x.packer as packer - import secrets +sys.path.insert(0, '../') + +# SYSTEM CONFIG DEBUG = 2 API_URL = "https://emiei.4oh4.co/api" -MAX_TX_RESERVATION_TIME = 2 # Seconds +MAX_TX_RESERVATION_TIME = 2 # Seconds +# Configure Pi for LoRa BOARD.setup() +parser = LoRaArgumentParser("Continuous LoRa receiver.") -parser = LoRaArgumentParser("Continous LoRa receiver.") -# Recived from Sensors when they have data to send class RxHello(object): - def __init__(self, uid, reservationTime, messageID): + """JSON Object received from sensor when they transmit a "TX Hello" request to send + NOTE: Variable names must match JSON object from sensor! + """ + + def __init__(self, gatewayUID, uid, reservationTime, messageID): + """Initialise object, we will pass JSON object from which these values are retrieved + :param gatewayUID: UID Of this gateway + :param uid: Sensor UID, of the form EMIEI-... where ... is a number + :param reservationTime: Requested period of time to transmit message, may be overridden by ourselves + :param messageID: ID of message coming from sensor + """ + self.gatewayUid = gatewayUID self.uid = uid self.reservationTime = min(reservationTime, MAX_TX_RESERVATION_TIME) self.messageID = messageID - self.gatewayUid = getserial() self.okTransmit = False - def setOK(self, okToTransmit): - self.okTransmit = okToTransmit + def set_ok(self, okTransmit): + """Is sensor node OK to transmit? + :param okTransmit: Boolean Value, True/False + """ + self.okTransmit = okTransmit # Python Object to JSON Object - def ToJson(self): + def to_json(self): return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True) + class SensorResponse(object): + """JSON Object received from sensor when they transmit a "TX Payload" + NOTE: Variable names must match JSON object from sensor! + """ + def __init__(self, sensorMetadata, data): self.gatewayMetadata = GatewayMetadata() self.sensorMetadata = SensorMetadata(**sensorMetadata) self.sensorReading = SensorReading(**data) # Python Object to JSON Object - def ToJson(self): + def to_json(self): return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True) - def sendToApi(self): + def send_to_api(self): headers = {'Content-Type': 'Application/json'} - response = requests.post(API_URL + '/sensor/reading', data=self.ToJson(), headers=headers)#, verify=False) # Using self signed cert for now, sort later! + response = requests.post(API_URL + '/sensor/reading', data=self.to_json(), headers=headers) + + # If using self-signed cert turn off signature validation + # response = requests.post(API_URL + '/sensor/reading', data=self.ToJson(), headers=headers, verify=False) # + if DEBUG > 1: - print(self.ToJson()) - print(response) # Ensure 200 Response! + print(self.to_json()) + print(response) # TODO Ensure 200 Response! return + class GatewayMetadata(object): + """JSON Object received from sensor when they transmit a "TX Payload". Component within SensorResponse(object) + NOTE: Variable names must match JSON object from sensor! + """ def __init__(self): - self.gatewayUID = getserial() + self.gatewayUID = get_gateway_serial() self.apiKey = secrets.GATEWAY_API_KEY + class SensorMetadata(object): + """JSON Object received from sensor when they transmit a "TX Payload". Component within SensorResponse(object) + NOTE: Variable names must match JSON object from sensor! + """ def __init__(self, uid, messageID, samplePeriod): self.uid = uid self.messageID = messageID self.samplePeriod = samplePeriod self.sampleTime = round(time()) + class SensorReading(object): + """JSON Object received from sensor when they transmit a "TX Payload". Component within SensorResponse(object) + NOTE: Variable names must match JSON object from sensor! + """ def __init__(self, ppm, sht, co2): self.ppm = PPM(**ppm) self.sht = SHT(**sht) self.co2 = Co2(**co2) + class PPM(object): + """JSON Object received from sensor when they transmit a "TX Payload". + Component within SensorResponse(object).SensorReading + NOTE: Variable names must match JSON object from sensor! + """ def __init__(self, p10, p25, p100): self.p10 = p10 self.p25 = p25 self.p100 = p100 + class SHT(object): + """JSON Object received from sensor when they transmit a "TX Payload". + Component within SensorResponse(object).SensorReading + NOTE: Variable names must match JSON object from sensor! + """ def __init__(self, temperature, humidity): self.temperature = temperature self.humidity = humidity + class Co2(object): + """JSON Object received from sensor when they transmit a "TX Payload". + Component within SensorResponse(object).SensorReading + NOTE: Variable names must match JSON object from sensor! + """ def __init__(self, co2): self.co2 = co2 -class API(): - def getSensorConfig(self, sensorUID): - headers = {'Content-Type': 'Application/json'} - response = requests.post(API_URL + '/' + secrets.API_KEY + '/sensor/getConfig/' + sensorUID, headers=headers) - if (response.status_code != 200): - return None +def get_sensor_config_from_api(sensor_uid): + headers = {'Content-Type': 'Application/json'} + response = requests.post(API_URL + '/' + secrets.API_KEY + '/sensor/getConfig/' + sensor_uid, headers=headers) - return json.loads(response.content) + if response.status_code != 200: + return None -class Reply(): + return json.loads(response.content) + + +class Reply: + """JSON Object transmitted back to sensor when they transmit a "TX Payload". + Include ack status, and any config updates, along with sensor ID, gateway ID & message ID + NOTE: Variable names must match JSON object from sensor! + """ ackStatus = False def __init__(self, remoteSensorID, replyMsgID): - self.gatewayUid = getserial() + self.gatewayUid = get_gateway_serial() self.uid = remoteSensorID self.replyMsgID = replyMsgID self.txAfterNReadings = None self.pollingFrequency = None - def setAckStatus(self, ackStatus): - self.ackStatus = ackStatus + def set_ack_status(self, ack_status): + """Set Acknowledgement Status + :param ack_status: True if gateway correctly processed value, False otherwise + """ + self.ackStatus = ack_status - def updateConfigTxAfterNReadings(self, _nReadings): - self.txAfterNReadings = _nReadings + def update_config_tx_after_n_readings(self, _n_readings): + self.txAfterNReadings = _n_readings # Provide value in ms - def updateConfigPollingFrequency(self, _pollingFrequency): - self.pollingFrequency = _pollingFrequency + def update_config_polling_frequency(self, _polling_frequency): + self.pollingFrequency = _polling_frequency - def ToJson(self): + def to_json(self): return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True) + class LoRaReceiver(LoRa): + """ See SX127x Library + """ def __init__(self, verbose=False): super(LoRaReceiver, self).__init__(verbose) - self._id = "Base-01" + self._id = get_gateway_serial() self.set_mode(MODE.SLEEP) self.set_dio_mapping([0] * 6) - self.rxHelloQueue = RxHelloQueue() # Track transmission requests + self.rx_hello_queue = RxHelloQueue() # Track transmission requests def on_rx_done(self): self.clear_irq_flags(RxDone=1) @@ -170,7 +228,7 @@ class LoRaReceiver(LoRa): # Parse our data here! data = ''.join([chr(c) for c in payload]) - handleData(self.rxHelloQueue, data) + handle_incoming_lora_packet(self.rx_hello_queue, data) self.reset_ptr_rx() self.set_mode(MODE.RXCONT) @@ -178,7 +236,7 @@ class LoRaReceiver(LoRa): def on_tx_done(self): print("\nTxDone") # set RX - self.set_dio_mapping([0,0,0,0,0,0]) # RX + self.set_dio_mapping([0, 0, 0, 0, 0, 0]) # RX sleep(1) self.reset_ptr_rx() self.set_mode(MODE.RXCONT) @@ -208,11 +266,10 @@ class LoRaReceiver(LoRa): self.reset_ptr_rx() self.set_mode(MODE.RXCONT) while True: - if (self.get_mode() == MODE.TX): + if self.get_mode() == MODE.TX: rssi_value = self.get_rssi_value() status = self.get_modem_status() - # if DEBUG > 1: sys.stdout.flush() sys.stdout.write("\r%d %d %d" % (rssi_value, status['rx_ongoing'], status['modem_clear'])) @@ -223,119 +280,123 @@ class LoRaReceiver(LoRa): self.set_mode(MODE.TX) -class RxHelloQueue(): +def transmit_object_with_lora(obj): + if DEBUG > 1: + print(obj.to_json()) + + _length, _payload = packer.Pack_Str(obj.to_json()) + + payload = [int(hex(c), 0) for c in _payload] + + sleep(0.2) # Slight delay before transmitting, else too quick for sensor to start listening + loraReceiver.transmit(payload) + sleep(0.2) + + +class RxHelloQueue: + """Keep track of what messages we have incoming and when their transmission window expires + Used to accept / reject "TX Hello" requests + """ def __init__(self): self.reserved = False self.sensorID = None self.reservedUntil = None - - def hasReservationExpired(self): + def has_reservation_expired(self): + """Returns true when there is no longer any scheduled transmission due + Returns false otherwise + """ print("Time Now: ", time(), ", Reserved Until: ", self.reservedUntil) + if self.reserved and self.reservedUntil < time(): self.reserved = False return True - - return False - - - def canAccept(self): - self.hasReservationExpired() - - if not self.reserved: - return True - # TODO Additional Logic Here! return False + def can_accept(self): + self.has_reservation_expired() + return not self.reserved - def acceptNew(self, sensorID, reservationDuration): - if not self.canAccept: + def accept_new(self, sensor_id, reservation_duration): + if not self.can_accept: return False - + self.reserved = True - self.sensorID = sensorID - self.reservedUntil = time() + reservationDuration + 1 - -# Source: https://www.raspberrypi-spy.co.uk/2012/09/getting-your-raspberry-pi-serial-number-using-python/ -def getserial(): - # Extract serial from cpuinfo file - cpuserial = "0000000000000000" - try: - f = open('/proc/cpuinfo','r') - for line in f: - if line[0:6]=='Serial': - cpuserial = line[10:26] - f.close() - except: - cpuserial = "ERROR000000000" - - return cpuserial + self.sensorID = sensor_id + self.reservedUntil = time() + reservation_duration + 1 -# Upon succesfully receiving a message, send back an ack -def ackMsg(sensorResponse): - data = Reply(sensorResponse.sensorMetadata.uid, sensorResponse.sensorMetadata.messageID) - data.setAckStatus(True) - # Retrieve up-to-date sensor config from API & Transmit back to sensor - updated_sensor_config = API().getSensorConfig(sensorResponse.sensorMetadata.uid) - if updated_sensor_config is not None: - data.updateConfigPollingFrequency(updated_sensor_config['pollingFrequency']) - data.updateConfigTxAfterNReadings(updated_sensor_config['txAfterNReadings']) +def get_gateway_serial(): + """Extract serial number from cpu info file + Source: https://www.raspberrypi-spy.co.uk/2012/09/getting-your-raspberry-pi-serial-number-using-python/ + """ + cpu_serial = "0000000000000000" + try: + f = open('/proc/cpuinfo', 'r') + for line in f: + if line[0:6] == 'Serial': + cpu_serial = line[10:26] + f.close() - if DEBUG > 1: - print(data.ToJson()) + except Exception: + cpu_serial = "ERROR000000000" - _length, _payload = packer.Pack_Str( data.ToJson() ) + return cpu_serial - payload = [int(hex(c), 0) for c in _payload] - sleep(0.2) # Slight delay before transmitting, else too quick for sensor to start listening - loraReceiver.transmit(payload) +# Upon successfully receiving a message, send back an ack +def ack_message(sensor_response): + data = Reply(sensor_response.sensorMetadata.uid, sensor_response.sensorMetadata.messageID) + data.set_ack_status(True) + + # Retrieve up-to-date sensor config from API & Transmit back to sensor + updated_sensor_config = get_sensor_config_from_api(sensor_response.sensorMetadata.uid) + if updated_sensor_config is not None: + data.update_config_polling_frequency(updated_sensor_config['pollingFrequency']) + data.update_config_tx_after_n_readings(updated_sensor_config['txAfterNReadings']) + + transmit_object_with_lora(data) return + # When a sensor has data to transmit, it will send a TxHello. # Pick up that message here and decide if we can accept the message at this time. -def handleRxHello(rxHelloQueue, data): +def handle_rx_hello(rx_hello_queue, data): print("\n[?] Check for RX Hello") if DEBUG > 1: print("\nRaw data: %s" % data) try: - rxHelloJson = json.loads(data) - rxHello = RxHello(**rxHelloJson) - print(" RX Hello Confirmed!") + rx_hello_json = json.loads(data) + rx_hello = RxHello(get_gateway_serial(), **rx_hello_json) - except Exception as e: + except Exception: print(" Not RX Hello") return False - # TODO Check i'm not expecting any methods within rxHello.reservationTime seconds - if not rxHelloQueue.canAccept(): - rxHello.setOK(False) - print("[-] RX Hello \"No\" Reply Sent - Reserved by %s for %i seconds" % (rxHelloQueue.sensorID, rxHelloQueue.reservedUntil - time())) - + print(" RX Hello Confirmed!") + + # Check I'm not expecting any methods within rx_hello.reservationTime seconds + if not rx_hello_queue.can_accept(): + rx_hello.set_ok(False) + print("[-] RX Hello \"No\" Reply Sent - Reserved by %s for %i seconds" + % (rx_hello_queue.sensorID, rx_hello_queue.reservedUntil - time())) + else: - rxHelloQueue.acceptNew(rxHello.uid, rxHello.reservationTime) + rx_hello_queue.accept_new(rx_hello.uid, rx_hello.reservationTime) # Send "OK" + UID - rxHello.setOK(True) # We are happy for this sensor to send it's data - print("[+] RX Hello \"OK\" Reply Sent - %s is permitted to send for %d seconds" % (rxHello.uid, rxHello.reservationTime)) - - if DEBUG > 1: - print(rxHello.ToJson()) + rx_hello.set_ok(True) # We are happy for this sensor to send its data + print("[+] RX Hello \"OK\" Reply Sent - %s is permitted to send for %d seconds" + % (rx_hello.uid, rx_hello.reservationTime)) - _length, _payload = packer.Pack_Str( rxHello.ToJson() ) # Send OK Back - payload = [int(hex(c), 0) for c in _payload] - - sleep(0.2) # Slight delay before transmitting, else too quick for sensor to start listening - loraReceiver.transmit(payload) - sleep(0.2) + transmit_object_with_lora(rx_hello) return True # handle SensorResponse data packet -def handleSensorResponsePacket(data): +def handle_sensor_response_packet(data): print("\n[?] Check for Sensor Response Data") if DEBUG > 1: print("\nRaw data: %s" % data) @@ -345,62 +406,55 @@ def handleSensorResponsePacket(data): p = SensorResponse(**parsed) except Exception as e: - print("[-] Unable to Parse response, ignoring") # TODO Error handling, log increased error rates etc + print("[-] Unable to Parse response, ignoring") if DEBUG > 1: print("\tE: %e" % e) return False print(" Sensor Response Data Confirmed!") - - if DEBUG > 1: - print(" Received Payload: %s" % p.ToJson()) - - # TODO Validate response is valid and non-corrupt - # TODO Ack / process here + if DEBUG > 1: + print(" Received Payload: %s" % p.to_json()) # TODO Transmit ACK print(" Sending Sensor Response Ack") - ackMsg(p) + ack_message(p) print("[+] Sensor Response Ack Sent") - # TODO Process response? + # TODO Process response? Rabbit MQ? - # TODO Send To API + # Send Data To API print(" Sending Data to API") - p.sendToApi() + p.send_to_api() print("[+] Data Sent to API") sleep(0.2) return True - # Parse LoRa response, validate, save / transmit -def handleData(rxHelloQueue, data): +def handle_incoming_lora_packet(rx_hello_queue, data): print("\n===================\n[i] Packet Incoming") - # Handle TxHello Transmission - # Rx - "I've got data to send!" + UID (From Sensor) - # If not expecting any messages, continue - # Tx - "OK" + UID (Back to Sensor) + TX_Auth_ID - # - # <Do not Tx for 2s> - if (handleRxHello(rxHelloQueue, data)): # Handled all OK + """ Handle TX Hello Transmission + Rx - "I've got data to send!" + UID (From Sensor) + + If not expecting any messages, permit payload send, else send tx auth denied + Tx - "OK" + UID (Back to Sensor) + TX_Auth_ID + [or] TX - "NO" + UID (Back to Sensor) + """ + if handle_rx_hello(rx_hello_queue, data): # Handled all OK return - # Handle Payload Transmission - # Rx - Packet + UID + Tx_Auth_ID (From Sensor) - # Handle message, ack / nak appropriately - # Tx - Ack + UID + Next_Send_Interval - # Tx - Nak + UID - # - # <Do not Tx for 3s> - if (handleSensorResponsePacket(data)): + """ Handle Payload Transmission + Rx - Packet + UID + Tx_Auth_ID (From Sensor) + + Handle message, ack / nak appropriately + Tx - Ack + UID + Next_Send_Interval + [or] Tx - Nak + UID + """ + if handle_sensor_response_packet(data): return - - - # T+3 Tx reserved window expires # Setup Receiver @@ -409,23 +463,13 @@ args = parser.parse_args(loraReceiver) loraReceiver.set_mode(MODE.STDBY) loraReceiver.set_pa_config(pa_select=PA_SELECT.PA_BOOST) -#loraReceiver.set_rx_crc(True) -#loraReceiver.set_coding_rate(CODING_RATE.CR4_6) -loraReceiver.set_pa_config(max_power=300, output_power=500) # Default max_power = 10.8dB, output_power = 4.2dB -#loraReceiver.set_lna_gain(GAIN.G1) -#loraReceiver.set_implicit_header_mode(False) -#loraReceiver.set_low_data_rate_optim(True) -#loraReceiver.set_pa_ramp(PA_RAMP.RAMP_50_us) -#loraReceiver.set_agc_auto_on(True) - +loraReceiver.set_pa_config(max_power=300, output_power=500) # Default max_power = 10.8dB, output_power = 4.2dB -print("Power:") -print(loraReceiver.get_pa_config(convert_dBm=True)) - -# Go Go Go! print("[+] Receiver & API Gateway") +print("[i] Power Levels:", loraReceiver.get_pa_config(convert_dBm=True)) + loraReceiver.set_agc_auto_on(1) -assert(loraReceiver.get_agc_auto_on() == 1) +assert (loraReceiver.get_agc_auto_on() == 1) try: loraReceiver.receive() diff --git a/test/Pi_Receiver.py b/test/Pi_Receiver.py deleted file mode 100644 index 6495104dde00af5606fe5adf6f36d90b03a791bc..0000000000000000000000000000000000000000 --- a/test/Pi_Receiver.py +++ /dev/null @@ -1,363 +0,0 @@ -#!/usr/bin/env python3 - -''' - ========================================================================================= - - CS408 Environmental Monitoring Independent of Existing Infrastructure - Copyright (C) 2021 Callum Inglis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - Contact: Callum.Inglis.2018(at)uni.strath.ac.uk - - ========================================================================================= - - Libraries Used - https://github.com/raspberrypi-tw/lora-sx1276 - - ========================================================================================= -''' - -# Usage: python Pi_Receiver.py -f 433 -b BW125 -s 7 - -from argparse import ArgumentError -import sys -sys.path.insert(0, '../') - -from time import sleep, time -import json -import requests # pip3 install requests -import random - -from SX127x.LoRa import * -from SX127x.LoRaArgumentParser import LoRaArgumentParser -from SX127x.board_config import BOARD -import SX127x.packer as packer - -DEBUG = 1 -API_URL = "http://environmental-monitoring.int.4oh4.co/api" - -BOARD.setup() - -parser = LoRaArgumentParser("Continous LoRa receiver.") - -# Recived from Sensors when they have data to send -class RxHello(object): - def __init__(self, uid, reservationTime): - self.uid = uid - self.reservationTime = reservationTime - self.okToTransmit = False - self.txAuthID = None - - def setOK(self, okToTransmit): - self.okToTransmit = okToTransmit - - def setTxAuthID(self, txAuthID): - self.txAuthID = txAuthID - - # Python Object to JSON Object - def ToJson(self): - return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) - -class SensorResponse(object): - def __init__(self, sensorMetadata, data): - self.sensorMetadata = SensorMetadata(**sensorMetadata) - self.sensorReading = SensorReading(**data) - - # Python Object to JSON Object - def ToJson(self): - return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) - - def sendToApi(self): - headers = {'Content-Type': 'Application/json'} - response = requests.post(API_URL + '/sensor/reading', data=self.ToJson(), headers=headers, verify=False) # Using self signed cert for now, sort later! - if DEBUG > 0: - print(response) - return - -class SensorMetadata(object): - def __init__(self, uid, messageID, samplePeriod): - self.uid = uid - self.messageID = messageID - self.samplePeriod = samplePeriod - self.sampleTime = round(time()) - -class SensorReading(object): - def __init__(self, ppm, sht, co2): - self.ppm = PPM(**ppm) - self.sht = SHT(**sht) - self.co2 = Co2(**co2) - -class PPM(object): - def __init__(self, p10, p25, p100): - self.p10 = p10 - self.p25 = p25 - self.p100 = p100 - -class SHT(object): - def __init__(self, temperature, humidity): - self.temperature = temperature - self.humidity = humidity - -class Co2(object): - def __init__(self, tmp): - self.tmp = "Coming Soon!" - -class Reply(): - ackStatus = False - - def __init__(self, remoteSensorID, replyMsgID): - self.messageID = random.randrange(10000, 99999) - self.gatewayUid = getserial() - self.remoteSensorID = remoteSensorID - self.replyMsgID = replyMsgID - - def setAckStatus(self, ackStatus): - self.ackStatus = ackStatus - - def ToJson(self): - return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=4) - -class LoRaReceiver(LoRa): - def __init__(self, verbose=False): - super(LoRaReceiver, self).__init__(verbose) - self._id = "Base-01" - self.set_mode(MODE.SLEEP) - self.set_dio_mapping([0] * 6) - - def on_rx_done(self): - if DEBUG > 1: - print("\n[+] Rx Done") - - self.clear_irq_flags(RxDone=1) - payload = self.read_payload(nocheck=True) - - # Parse our data here! - data = ''.join([chr(c) for c in payload]) - handleData(data) - - self.reset_ptr_rx() - self.set_mode(MODE.RXCONT) - - def on_tx_done(self): - print("\nTxDone") - # set RX - self.set_dio_mapping([0,0,0,0,0,0]) # RX - sleep(1) - self.reset_ptr_rx() - self.set_mode(MODE.RXCONT) - self.clear_irq_flags(RxDone=1) - - def on_cad_done(self): - print("\non_CadDone") - print(self.get_irq_flags()) - - def on_rx_timeout(self): - print("\non_RxTimeout") - print(self.get_irq_flags()) - - def on_valid_header(self): - print("\non_ValidHeader") - print(self.get_irq_flags()) - - def on_payload_crc_error(self): - print("\non_PayloadCrcError") - print(self.get_irq_flags()) - - def on_fhss_change_channel(self): - print("\non_FhssChangeChannel") - print(self.get_irq_flags()) - - def receive(self): - self.reset_ptr_rx() - self.set_mode(MODE.RXCONT) - while True: - if (self.get_mode() == MODE.TX): - rssi_value = self.get_rssi_value() - status = self.get_modem_status() - - if DEBUG > 1: - sys.stdout.flush() - sys.stdout.write("\r%d %d %d" % (rssi_value, status['rx_ongoing'], status['modem_clear'])) - - def transmit(self, _payload): - global args - self.tx_counter = 0 - self.write_payload(_payload) - self.set_mode(MODE.TX) - -# Source: https://www.raspberrypi-spy.co.uk/2012/09/getting-your-raspberry-pi-serial-number-using-python/ -def getserial(): - # Extract serial from cpuinfo file - cpuserial = "0000000000000000" - try: - f = open('/proc/cpuinfo','r') - for line in f: - if line[0:6]=='Serial': - cpuserial = line[10:26] - f.close() - except: - cpuserial = "ERROR000000000" - - return cpuserial - -# Upon succesfully receiving a message, send back an ack -def ackMsg(sensorResponse): - data = Reply(sensorResponse.sensorMetadata.uid, sensorResponse.sensorMetadata.messageID) - data.setAckStatus(True) - - print(data.ToJson()) - - _length, _payload = packer.Pack_Str( data.ToJson() ) - - payload = [int(hex(c), 0) for c in _payload] - - loraReceiver.transmit(payload) - return - -# When a sensor has data to transmit, it will send a TxHello. -# Pick up that message here and decide if we can accept the message at this time. -def handleRxHello(data): - timeBegin = time() - print("\n[?] Try RX Hello!") - if DEBUG > 1: - print("\nRaw data: %s" % data) - - try: - rxHelloJson = json.loads(data) - rxHello = RxHello(**rxHelloJson) - - except Exception as e: - print("[-] Not RX Hello\n") - return False - - print("[+] RX Hello Confirmed!\n") - # TODO Check i'm not expecting any methods within rxHello.reservationTime seconds - - # Send "OK" + UID + TX_AUTH_ID - rxHello.setOK(True) # We are happy for this sensor to send it's data - rxHello.setTxAuthID(123) - print(rxHello.ToJson()) - - _length, _payload = packer.Pack_Str( rxHello.ToJson() ) # Send OK Back Back - payload = [int(hex(c), 0) for c in _payload] - - loraReceiver.transmit(payload) - print("\n[+] RX Hello \"OK\" Reply Sent\n") - - # Sleep until out transmit block expires (2 Seconds) - #sleep(2 - (time() - timeBegin)) - sleep(0.2) - return True - - -# handle SensorResponse data packet -def handleSensorResponsePacket(data): - timeBegin = time() - print("\n[?] Try Sensor Response Data!\nRaw data: %s" % data) - - try: - parsed = json.loads(data) - p = SensorResponse(**parsed) - - except Exception as e: - print("\n[-] Unable to Parse response, ignoring") # TODO Error handling, log increased error rates etc - if DEBUG > 1: - print("\tE: %e" % e) - return False - - print("[+] Sensor Response Data Confirmed!\n") - print(p.ToJson()) - - # TODO Validate response is valid and non-corrupt - - # TODO Ack / process here - - # TODO Transmit ACK - print("\n[+] Sending Sensor Response Ack") - ackMsg(p) - print("[+] Sensor Response Ack Sent\n") - - # Process response - print("Sensor ID: %s \tPPM 10: %s\n" % (p.sensorMetadata.uid, p.sensorReading.ppm.p10)) - - # TODO Send To API - print("\n[+] Sending Data to API") - p.sendToApi() - print("[+] Data Sent to API\n") - - # Sleep until out transmit block expires (2 Seconds) - #sleep(2 - (time() - timeBegin)) - sleep(0.2) - - - -# Parse LoRa response, validate, save / transmit -def handleData(data): - # Handle TxHello Transmission - # Rx - "I've got data to send!" + UID (From Sensor) - # If not expecting any messages, continue - # Tx - "OK" + UID (Back to Sensor) + TX_Auth_ID - # - # <Do not Tx for 2s> - if (handleRxHello(data)): # Handled all OK - return - - # Handle Payload Transmission - # Rx - Packet + UID + Tx_Auth_ID (From Sensor) - # Handle message, ack / nak appropriately - # Tx - Ack + UID + Next_Send_Interval - # Tx - Nak + UID - # - # <Do not Tx for 3s> - if (handleSensorResponsePacket(data)): - return - - - # T+3 Tx reserved window expires - - -# Setup Receiver -loraReceiver = LoRaReceiver(verbose=False) -args = parser.parse_args(loraReceiver) - -loraReceiver.set_mode(MODE.STDBY) -loraReceiver.set_pa_config(pa_select=1) -#loraReceiver.set_rx_crc(True) -#loraReceiver.set_coding_rate(CODING_RATE.CR4_6) -#loraReceiver.set_pa_config(max_power=0, output_power=0) -#loraReceiver.set_lna_gain(GAIN.G1) -#loraReceiver.set_implicit_header_mode(False) -#loraReceiver.set_low_data_rate_optim(True) -#loraReceiver.set_pa_ramp(PA_RAMP.RAMP_50_us) -#loraReceiver.set_agc_auto_on(True) - -# Go Go Go! -print("[+] Receiver & API Gateway") -assert(loraReceiver.get_agc_auto_on() == 1) - -try: - loraReceiver.receive() - -except KeyboardInterrupt: - sys.stdout.flush() - print("") - sys.stderr.write("KeyboardInterrupt\n") - -finally: - sys.stdout.flush() - print("") - loraReceiver.set_mode(MODE.SLEEP) - BOARD.teardown()