diff --git a/sensord.example b/sensord.example new file mode 100755 index 0000000..1240f21 --- /dev/null +++ b/sensord.example @@ -0,0 +1,34 @@ +#!/usr/bin/python3 -u + +import sys, signal, os +# sys.path.append('/usr/local/lib/python3.5/dist-packages/sensord') + +from sensord.RFM69receiver import RFM69Receiver, LogConsole +from sensord.InfluxDBWriter import InfluxDBWriter +from sensord.MQTT import MQTT + +RFM_NODE = 1 +RFM_NETW = 101 +RFM_ENCR = '0123456789ABCDEF' + +MQTT_HOST = "mqtt.broker" +MQTT_PORT = 8883 +MQTT_BASE = "sensor/" +MQTT_USER = "sensor" +MQTT_PASS = "secretsauce" +MQTT_CERT = "/etc/mosquitto/ca_certificates/ca.crt" + +INFLUX_HOST = "influxdb.host" +INFLUX_PORT = 8086 +INFLUX_DB = "sensors" + +rfm = RFM69Receiver(RFM_NODE, RFM_NETW, RFM_ENCR) + +influx = InfluxDBWriter(INFLUX_HOST, INFLUX_PORT, INFLUX_DB) +mqtt = MQTT(MQTT_HOST, MQTT_PORT, MQTT_BASE, MQTT_USER, MQTT_PASS, MQTT_CERT) + +rfm.attach_observer(influx) +rfm.attach_observer(mqtt) +rfm.attach_observer(LogConsole()) + +rfm.run() diff --git a/sensord/InfluxDBWriter.py b/sensord/InfluxDBWriter.py new file mode 100644 index 0000000..d134969 --- /dev/null +++ b/sensord/InfluxDBWriter.py @@ -0,0 +1,43 @@ +""" Module to Store Sensor Data into InfluxDB """ + +from influxdb import InfluxDBClient +from copy import copy + +class InfluxDBWriter(object): + """ Class to Represent the RRD Data of a Single Multi-Value Sensor """ + + def __init__(self, host, port=8086, dbname="sensors", user=None, passwd=None): + """ Initialize Class + + Arguments: + host : InfluxDB Hostname (mandatory) + port: InfluxDB HTTP Port + dbname: Name of the InfluxDB database + user: Username + pass: Passwort (mandatory if user is set) + """ + + self.__host = host + self.__port = port + self.__dbname = dbname + if user != None and passwd != None: + self.__client = InfluxDBClient(host, port, user, passwd, dbname) + else: + self.__client = InfluxDBClient(host, port, database=dbname) + + + def notify(self, msg): + """ Notify Method Called from the Receiver Class, Inserts Values into InfluxDB + + Arguments: + msg: Object Representation of the Sensor Data + """ + tmp = copy(msg) + json_body = [{"measurement": str(tmp['sid']), "fields": {}}] + tmp.pop('sid', None) + json_body[0]["fields"] = tmp + + try: + self.__client.write_points(json_body) + except: + print("Writing to InfluxDB Failed") diff --git a/sensord/MQTT.py b/sensord/MQTT.py new file mode 100644 index 0000000..85f2bbe --- /dev/null +++ b/sensord/MQTT.py @@ -0,0 +1,56 @@ +""" Module to Publish Sensor Data to MQTT """ + +import paho.mqtt.client as paho +from copy import copy + +class MQTT(object): + """ Class to Represent a Client to a single MQTT Broker """ + + def __init__(self, host, port=1883, base="sensor/", user=None, passwd=None, certfile=None): + """ Initialize Class + + Arguments: + host : MQTT Broker Hostname (mandatory) + port: MQTT Broker Port + base: Base MQTT Topic + user: Username + pass: Passwort (mandatory if user is set) + certfile: Path to CA-Cert File of the broker. TLS if used if set + """ + + self.__base = base + self.__client = paho.Client() + self.__client.on_connect = self.__on_connect + if user != None and passwd != None: + self.__client.username_pw_set(user, passwd) + if certfile != None: + self.__client.tls_set(certfile) + + try: + self.__client.connect(host, port, 60) + self.__client.loop_start() + except: + print("Error Setting up MQTT Client") + raise RuntimeError + + def __del__(self): + self.__client.loop_stop() + self.__client.disconnect() + + def __on_connect(self, client, userdata, ret, dummy): + """ Do Something when MQTT Connection is established """ + print("Connected with result code "+str(ret)) + + def notify(self, msg): + """ Publish Sensor data to MQTT + + Arguments: + msg: Object Representation of the Sensor Data + """ + tmp = copy(msg) + + sid = tmp['sid'] + tmp.pop('sid', None) + + for stype, svalue in tmp.items(): + self.__client.publish(self.__base + str(sid) + "/" + stype, str(svalue), 2, True) diff --git a/sensord/RFM69.py b/sensord/RFM69.py new file mode 100644 index 0000000..418fa7f --- /dev/null +++ b/sensord/RFM69.py @@ -0,0 +1,378 @@ +#!/usr/bin/env python2 + +from RFM69registers import * +import spidev +import RPi.GPIO as GPIO +import time + +class RFM69(object): + def __init__(self, freqBand, nodeID, networkID, isRFM69HW = False, intPin = 18, rstPin = 28, spiBus = 0, spiDevice = 0): + + self.freqBand = freqBand + self.address = nodeID + self.networkID = networkID + self.isRFM69HW = isRFM69HW + self.intPin = intPin + self.rstPin = rstPin + self.spiBus = spiBus + self.spiDevice = spiDevice + self.intLock = False + self.mode = "" + self.promiscuousMode = False + self.DATASENT = False + self.DATALEN = 0 + self.SENDERID = 0 + self.TARGETID = 0 + self.PAYLOADLEN = 0 + self.ACK_REQUESTED = 0 + self.ACK_RECEIVED = 0 + self.RSSI = 0 + self.DATA = [] + + GPIO.setmode(GPIO.BOARD) + GPIO.setup(self.intPin, GPIO.IN) + GPIO.setup(self.rstPin, GPIO.OUT) + + frfMSB = {RF69_315MHZ: RF_FRFMSB_315, RF69_433MHZ: RF_FRFMSB_433, + RF69_868MHZ: RF_FRFMSB_868, RF69_915MHZ: RF_FRFMSB_915} + frfMID = {RF69_315MHZ: RF_FRFMID_315, RF69_433MHZ: RF_FRFMID_433, + RF69_868MHZ: RF_FRFMID_868, RF69_915MHZ: RF_FRFMID_915} + frfLSB = {RF69_315MHZ: RF_FRFLSB_315, RF69_433MHZ: RF_FRFLSB_433, + RF69_868MHZ: RF_FRFLSB_868, RF69_915MHZ: RF_FRFLSB_915} + + self.CONFIG = { + 0x01: [REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_LISTEN_OFF | RF_OPMODE_STANDBY], + #no shaping + 0x02: [REG_DATAMODUL, RF_DATAMODUL_DATAMODE_PACKET | RF_DATAMODUL_MODULATIONTYPE_FSK | RF_DATAMODUL_MODULATIONSHAPING_00], + #default:4.8 KBPS + 0x03: [REG_BITRATEMSB, RF_BITRATEMSB_55555], + 0x04: [REG_BITRATELSB, RF_BITRATELSB_55555], + #default:5khz, (FDEV + BitRate/2 <= 500Khz) + 0x05: [REG_FDEVMSB, RF_FDEVMSB_50000], + 0x06: [REG_FDEVLSB, RF_FDEVLSB_50000], + + 0x07: [REG_FRFMSB, frfMSB[freqBand]], + 0x08: [REG_FRFMID, frfMID[freqBand]], + 0x09: [REG_FRFLSB, frfLSB[freqBand]], + + # looks like PA1 and PA2 are not implemented on RFM69W, hence the max output power is 13dBm + # +17dBm and +20dBm are possible on RFM69HW + # +13dBm formula: Pout=-18+OutputPower (with PA0 or PA1**) + # +17dBm formula: Pout=-14+OutputPower (with PA1 and PA2)** + # +20dBm formula: Pout=-11+OutputPower (with PA1 and PA2)** and high power PA settings (section 3.3.7 in datasheet) + #0x11: [REG_PALEVEL, RF_PALEVEL_PA0_ON | RF_PALEVEL_PA1_OFF | RF_PALEVEL_PA2_OFF | RF_PALEVEL_OUTPUTPOWER_11111], + #over current protection (default is 95mA) + #0x13: [REG_OCP, RF_OCP_ON | RF_OCP_TRIM_95], + + # RXBW defaults are { REG_RXBW, RF_RXBW_DCCFREQ_010 | RF_RXBW_MANT_24 | RF_RXBW_EXP_5} (RxBw: 10.4khz) + #//(BitRate < 2 * RxBw) + 0x19: [REG_RXBW, RF_RXBW_DCCFREQ_010 | RF_RXBW_MANT_16 | RF_RXBW_EXP_2], + #for BR-19200: //* 0x19 */ { REG_RXBW, RF_RXBW_DCCFREQ_010 | RF_RXBW_MANT_24 | RF_RXBW_EXP_3 }, + #DIO0 is the only IRQ we're using + 0x25: [REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_01], + #must be set to dBm = (-Sensitivity / 2) - default is 0xE4=228 so -114dBm + 0x29: [REG_RSSITHRESH, 220], + #/* 0x2d */ { REG_PREAMBLELSB, RF_PREAMBLESIZE_LSB_VALUE } // default 3 preamble bytes 0xAAAAAA + 0x2e: [REG_SYNCCONFIG, RF_SYNC_ON | RF_SYNC_FIFOFILL_AUTO | RF_SYNC_SIZE_2 | RF_SYNC_TOL_0], + #attempt to make this compatible with sync1 byte of RFM12B lib + 0x2f: [REG_SYNCVALUE1, 0x2D], + #NETWORK ID + 0x30: [REG_SYNCVALUE2, networkID], + 0x37: [REG_PACKETCONFIG1, RF_PACKET1_FORMAT_VARIABLE | RF_PACKET1_DCFREE_OFF | + RF_PACKET1_CRC_ON | RF_PACKET1_CRCAUTOCLEAR_ON | RF_PACKET1_ADRSFILTERING_OFF], + #in variable length mode: the max frame size, not used in TX + 0x38: [REG_PAYLOADLENGTH, 66], + #* 0x39 */ { REG_NODEADRS, nodeID }, //turned off because we're not using address filtering + #TX on FIFO not empty + 0x3C: [REG_FIFOTHRESH, RF_FIFOTHRESH_TXSTART_FIFONOTEMPTY | RF_FIFOTHRESH_VALUE], + #RXRESTARTDELAY must match transmitter PA ramp-down time (bitrate dependent) + 0x3d: [REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_2BITS | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF], + #for BR-19200: //* 0x3d */ { REG_PACKETCONFIG2, RF_PACKET2_RXRESTARTDELAY_NONE | RF_PACKET2_AUTORXRESTART_ON | RF_PACKET2_AES_OFF }, //RXRESTARTDELAY must match transmitter PA ramp-down time (bitrate dependent) + #* 0x6F */ { REG_TESTDAGC, RF_DAGC_CONTINUOUS }, // run DAGC continuously in RX mode + # run DAGC continuously in RX mode, recommended default for AfcLowBetaOn=0 + 0x6F: [REG_TESTDAGC, RF_DAGC_IMPROVED_LOWBETA0], + 0x00: [255, 0] + } + + #initialize SPI + self.spi = spidev.SpiDev() + self.spi.open(self.spiBus, self.spiDevice) + self.spi.max_speed_hz = 4000000 + + # Hard reset the RFM module + GPIO.output(self.rstPin, GPIO.HIGH); + time.sleep(0.1) + GPIO.output(self.rstPin, GPIO.LOW); + time.sleep(0.1) + + #verify chip is syncing? + while self.readReg(REG_SYNCVALUE1) != 0xAA: + self.writeReg(REG_SYNCVALUE1, 0xAA) + + while self.readReg(REG_SYNCVALUE1) != 0x55: + self.writeReg(REG_SYNCVALUE1, 0x55) + + #write config + for value in self.CONFIG.values(): + self.writeReg(value[0], value[1]) + + self.encrypt(0) + self.setHighPower(self.isRFM69HW) + # Wait for ModeReady + while (self.readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00: + pass + + GPIO.remove_event_detect(self.intPin) + GPIO.add_event_detect(self.intPin, GPIO.RISING, callback=self.interruptHandler) + + def setFreqeuncy(self, FRF): + self.writeReg(REG_FRFMSB, FRF >> 16) + self.writeReg(REG_FRFMID, FRF >> 8) + self.writeReg(REG_FRFLSB, FRF) + + def setMode(self, newMode): + if newMode == self.mode: + return + + if newMode == RF69_MODE_TX: + self.writeReg(REG_OPMODE, (self.readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_TRANSMITTER) + if self.isRFM69HW: + self.setHighPowerRegs(True) + elif newMode == RF69_MODE_RX: + self.writeReg(REG_OPMODE, (self.readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_RECEIVER) + if self.isRFM69HW: + self.setHighPowerRegs(False) + elif newMode == RF69_MODE_SYNTH: + self.writeReg(REG_OPMODE, (self.readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_SYNTHESIZER) + elif newMode == RF69_MODE_STANDBY: + self.writeReg(REG_OPMODE, (self.readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_STANDBY) + elif newMode == RF69_MODE_SLEEP: + self.writeReg(REG_OPMODE, (self.readReg(REG_OPMODE) & 0xE3) | RF_OPMODE_SLEEP) + else: + return + + # we are using packet mode, so this check is not really needed + # but waiting for mode ready is necessary when going from sleep because the FIFO may not be immediately available from previous mode + while self.mode == RF69_MODE_SLEEP and self.readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY == 0x00: + pass + + self.mode = newMode; + + def sleep(self): + self.setMode(RF69_MODE_SLEEP) + + def setAddress(self, addr): + self.address = addr + self.writeReg(REG_NODEADRS, self.address) + + def setNetwork(self, networkID): + self.networkID = networkID + self.writeReg(REG_SYNCVALUE2, networkID) + + def setPowerLevel(self, powerLevel): + if powerLevel > 31: + powerLevel = 31 + self.powerLevel = powerLevel + self.writeReg(REG_PALEVEL, (self.readReg(REG_PALEVEL) & 0xE0) | self.powerLevel) + + def canSend(self): + if self.mode == RF69_MODE_STANDBY: + self.receiveBegin() + return True + #if signal stronger than -100dBm is detected assume channel activity + elif self.mode == RF69_MODE_RX and self.PAYLOADLEN == 0 and self.readRSSI() < CSMA_LIMIT: + self.setMode(RF69_MODE_STANDBY) + return True + return False + + def send(self, toAddress, buff = "", requestACK = False): + self.writeReg(REG_PACKETCONFIG2, (self.readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART) + now = time.time() + while (not self.canSend()) and time.time() - now < RF69_CSMA_LIMIT_S: + self.receiveDone() + self.sendFrame(toAddress, buff, requestACK, False) + +# to increase the chance of getting a packet across, call this function instead of send +# and it handles all the ACK requesting/retrying for you :) +# The only twist is that you have to manually listen to ACK requests on the other side and send back the ACKs +# The reason for the semi-automaton is that the lib is ingterrupt driven and +# requires user action to read the received data and decide what to do with it +# replies usually take only 5-8ms at 50kbps@915Mhz + + def sendWithRetry(self, toAddress, buff = "", retries = 3, retryWaitTime = 10): + for i in range(0, retries): + self.send(toAddress, buff, True) + sentTime = time.time() + while (time.time() - sentTime) * 1000 < retryWaitTime: + if self.ACKReceived(toAddress): + return True + return False + + def ACKReceived(self, fromNodeID): + if self.receiveDone(): + return (self.SENDERID == fromNodeID or fromNodeID == RF69_BROADCAST_ADDR) and self.ACK_RECEIVED + return False + + def ACKRequested(self): + return self.ACK_REQUESTED and self.TARGETID != RF69_BROADCAST_ADDR + + def sendACK(self, toAddress = 0, buff = ""): + toAddress = toAddress if toAddress > 0 else self.SENDERID + while not self.canSend(): + self.receiveDone() + self.sendFrame(toAddress, buff, False, True) + + def sendFrame(self, toAddress, buff, requestACK, sendACK): + #turn off receiver to prevent reception while filling fifo + self.setMode(RF69_MODE_STANDBY) + #wait for modeReady + while (self.readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00: + pass + # DIO0 is "Packet Sent" + self.writeReg(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_00) + + if (len(buff) > RF69_MAX_DATA_LEN): + buff = buff[0:RF69_MAX_DATA_LEN] + + ack = 0 + if sendACK: + ack = 0x80 + elif requestACK: + ack = 0x40 + if isinstance(buff, str): + self.spi.xfer2([REG_FIFO | 0x80, len(buff) + 3, toAddress, self.address, ack] + [int(ord(i)) for i in list(buff)]) + else: + self.spi.xfer2([REG_FIFO | 0x80, len(buff) + 3, toAddress, self.address, ack] + buff) + + startTime = time.time() + self.DATASENT = False + self.setMode(RF69_MODE_TX) + while not self.DATASENT: + if time.time() - startTime > 1.0: + break + self.setMode(RF69_MODE_RX) + + def interruptHandler(self, pin): + self.intLock = True + self.DATASENT = True + if self.mode == RF69_MODE_RX and self.readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY: + self.setMode(RF69_MODE_STANDBY) + self.PAYLOADLEN, self.TARGETID, self.SENDERID, CTLbyte = self.spi.xfer2([REG_FIFO & 0x7f,0,0,0,0])[1:] + if self.PAYLOADLEN > 66: + self.PAYLOADLEN = 66 + if not (self.promiscuousMode or self.TARGETID == self.address or self.TARGETID == RF69_BROADCAST_ADDR): + self.PAYLOADLEN = 0 + self.intLock = False + return + self.DATALEN = self.PAYLOADLEN - 3 + self.ACK_RECEIVED = CTLbyte & 0x80 + self.ACK_REQUESTED = CTLbyte & 0x40 + + self.DATA = self.spi.xfer2([REG_FIFO & 0x7f] + [0 for i in range(0, self.DATALEN)])[1:] + + self.RSSI = self.readRSSI() + self.intLock = False + + def receiveBegin(self): + + while self.intLock: + time.sleep(.1) + self.DATALEN = 0 + self.SENDERID = 0 + self.TARGETID = 0 + self.PAYLOADLEN = 0 + self.ACK_REQUESTED = 0 + self.ACK_RECEIVED = 0 + self.RSSI = 0 + if (self.readReg(REG_IRQFLAGS2) & RF_IRQFLAGS2_PAYLOADREADY): + # avoid RX deadlocks + self.writeReg(REG_PACKETCONFIG2, (self.readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART) + #set DIO0 to "PAYLOADREADY" in receive mode + self.writeReg(REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_01) + self.setMode(RF69_MODE_RX) + + def receiveDone(self): + if (self.mode == RF69_MODE_RX or self.mode == RF69_MODE_STANDBY) and self.PAYLOADLEN > 0: + self.setMode(RF69_MODE_STANDBY) + return True + if self.readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_TIMEOUT: + # https://github.com/russss/rfm69-python/blob/master/rfm69/rfm69.py#L112 + # Russss figured out that if you leave alone long enough it times out + # tell it to stop being silly and listen for more packets + self.writeReg(REG_PACKETCONFIG2, (self.readReg(REG_PACKETCONFIG2) & 0xFB) | RF_PACKET2_RXRESTART) + elif self.mode == RF69_MODE_RX: + # already in RX no payload yet + return False + self.receiveBegin() + return False + + def readRSSI(self, forceTrigger = False): + rssi = 0 + if forceTrigger: + self.writeReg(REG_RSSICONFIG, RF_RSSI_START) + while self.readReg(REG_RSSICONFIG) & RF_RSSI_DONE == 0x00: + pass + rssi = self.readReg(REG_RSSIVALUE) * -1 + rssi = rssi >> 1 + return rssi + + def encrypt(self, key): + self.setMode(RF69_MODE_STANDBY) + if key != 0 and len(key) == 16: + self.spi.xfer([REG_AESKEY1 | 0x80] + [int(ord(i)) for i in list(key)]) + self.writeReg(REG_PACKETCONFIG2,(self.readReg(REG_PACKETCONFIG2) & 0xFE) | RF_PACKET2_AES_ON) + else: + self.writeReg(REG_PACKETCONFIG2,(self.readReg(REG_PACKETCONFIG2) & 0xFE) | RF_PACKET2_AES_OFF) + + def readReg(self, addr): + return self.spi.xfer([addr & 0x7F, 0])[1] + + def writeReg(self, addr, value): + self.spi.xfer([addr | 0x80, value]) + + def promiscuous(self, onOff): + self.promiscuousMode = onOff + + def setHighPower(self, onOff): + if onOff: + self.writeReg(REG_OCP, RF_OCP_OFF) + #enable P1 & P2 amplifier stages + self.writeReg(REG_PALEVEL, (self.readReg(REG_PALEVEL) & 0x1F) | RF_PALEVEL_PA1_ON | RF_PALEVEL_PA2_ON) + else: + self.writeReg(REG_OCP, RF_OCP_ON) + #enable P0 only + self.writeReg(REG_PALEVEL, RF_PALEVEL_PA0_ON | RF_PALEVEL_PA1_OFF | RF_PALEVEL_PA2_OFF | powerLevel) + + def setHighPowerRegs(self, onOff): + if onOff: + self.writeReg(REG_TESTPA1, 0x5D) + self.writeReg(REG_TESTPA2, 0x7C) + else: + self.writeReg(REG_TESTPA1, 0x55) + self.writeReg(REG_TESTPA2, 0x70) + + def readAllRegs(self): + results = [] + for address in range(1, 0x50): + results.append([str(hex(address)), str(bin(self.readReg(address)))]) + return results + + def readTemperature(self, calFactor): + self.setMode(RF69_MODE_STANDBY) + self.writeReg(REG_TEMP1, RF_TEMP1_MEAS_START) + while self.readReg(REG_TEMP1) & RF_TEMP1_MEAS_RUNNING: + pass + # COURSE_TEMP_COEF puts reading in the ballpark, user can add additional correction + #'complement'corrects the slope, rising temp = rising val + return (int(~self.readReg(REG_TEMP2)) * -1) + COURSE_TEMP_COEF + calFactor + + + def rcCalibration(self): + self.writeReg(REG_OSC1, RF_OSC1_RCCAL_START) + while self.readReg(REG_OSC1) & RF_OSC1_RCCAL_DONE == 0x00: + pass + + def shutdown(self): + self.setHighPower(False) + self.sleep() + GPIO.cleanup() diff --git a/sensord/RFM69receiver.py b/sensord/RFM69receiver.py new file mode 100644 index 0000000..0fd29db --- /dev/null +++ b/sensord/RFM69receiver.py @@ -0,0 +1,149 @@ +""" +rfm69receiver - A RFM69 433MHz Receiver Using the Observer Pattern +""" + +import time +import struct +from pprint import pprint +import RFM69 +from RFM69registers import RF69_433MHZ + +class RFM69Receiver(object): + """ Receives Messages from a RFM69 Wireless SPI Transceiver and Can Notify + Multiple Observers to Process the Messages + """ + + def __init__(self, nodeID, netID, encKey=None): + """ Initialize the Transceiver Module + + Arguments: + nodeID: ID of the Node in the Network, typically 1 for the Receiver, between 1 and 255 + netID: Network ID, all Nodes must use the same ID to communicate, between 1 and 255 + encKey: 16 byte Encryption Key (AES-128). If None no encryption is used + """ + + self.__observers = [] + self.__running = False + self.__lastsid = None + self.__lastts = 0 + + try: + print("Initializing RFM69 Module as Node " + str(nodeID) + " on Network " + str(netID)) + self.__rfm = RFM69.RFM69(RF69_433MHZ, nodeID, netID, False, rstPin=22) + self.__rfm.rcCalibration() + if encKey != None: + self.__rfm.encrypt(encKey) + except: + print("ERROR: Could not Initialize RFM69 Module") + raise RuntimeError + + def __del__(self): + """ Free the Receiver Module Resources on Exit """ + + print("Shutting Down RFM69 Module") + self.__rfm.shutdown() + def _rawbytes(self, s): + """Convert a string to raw bytes without encoding""" + outlist = [] + for cp in s: + num = ord(cp) + if num < 255: + outlist.append(struct.pack('B', num)) + elif num < 65535: + outlist.append(struct.pack('>H', num)) + else: + b = (num & 0xFF0000) >> 16 + H = num & 0xFFFF + outlist.append(struct.pack('>bH', b, H)) + return b''.join(outlist) + + + def _process(self, sid, data, rssi): + """ Process received Data and Notify Registered Observers + + Arguments: + sid: NodeID of the Sending Sensor + data: Data String or byte array + rssi: Signal Strength Indicator + """ + + if self.__lastsid == sid and (time.time() - self.__lastts) < 10: + self.__lastsid = sid + self.__lastts = time.time() + return + + self.__lastsid = sid + self.__lastts = time.time() + msg = {} + msg['sid'] = sid + msg['rssi'] = rssi + if (data.find(';') != -1 and data.find('=') != -1): + values = data.split(";") + for value in values: + vsplit = value.split("=") + if vsplit[0] == "t" or vsplit[0] == "h" or vsplit[0] == "p": + msg[vsplit[0]] = int(vsplit[1].rstrip())/100 + elif vsplit[0] == "v": + msg[vsplit[0]] = int(vsplit[1].rstrip())/1000 + else: + msg[vsplit[0]] = int(vsplit[1].rstrip()) + else: + bdata = self._rawbytes(data) + msg['t'] = int.from_bytes(bdata[0:3], byteorder='little', signed=True)/100 + msg['p'] = int.from_bytes(bdata[4:7], byteorder='little', signed=True)/100 + msg['h'] = int.from_bytes(bdata[8:11], byteorder='little', signed=True)/100 + msg['v'] = int.from_bytes(bdata[12:15], byteorder='little', signed=True)/1000 + + for observer in self.__observers: + observer.notify(msg) + + def attach_observer(self, obs): + """ Add an Object to the List of Known Observers """ + + self.__observers.append(obs) + + def clear_observers(self): + """ Delete all Known Observers """ + + self.__observers.clear() + + def run(self): + """ Start the Receiving Loop """ + + self.__running = True + while self.__running: + self.__rfm.receiveBegin() + while not self.__rfm.receiveDone(): + time.sleep(.1) + + sid = self.__rfm.SENDERID + rssi = self.__rfm.RSSI + + data = "" + for char in self.__rfm.DATA: + data += chr(char) + + if self.__rfm.ACKRequested(): + self.__rfm.sendACK() + + if rssi != 0: + self._process(sid, data, rssi) + + def stop(self): + """ Stop the Receiving Loop """ + + self.__running = False + +class LogConsole(object): + """ This is an Example Observer Class That Logs received Sensor Data to STDOUT """ + + def __init__(self): + """ NOOP, does nothing """ + pass + + @staticmethod + def notify(msg): + """ Pretty-Print the received Data """ + + print('[' + time.strftime('%H:%M:%S %d %b %Y') + '] Message Received: ', end='') + pprint(msg) diff --git a/sensord/RFM69registers.py b/sensord/RFM69registers.py new file mode 100644 index 0000000..425ed08 --- /dev/null +++ b/sensord/RFM69registers.py @@ -0,0 +1,1093 @@ +#!/usr/bin/env python + +# ********************************************************************************** +# Registers used in driver definition for HopeRF RFM69W/RFM69HW, Semtech SX1231/1231H +# ********************************************************************************** +# Copyright Felix Rusu (2014), felix@lowpowerlab.com +# http://lowpowerlab.com/ +# ********************************************************************************** +# License +# ********************************************************************************** +# 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., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Licence can be viewed at +# http://www.fsf.org/licenses/gpl.txt +# +# Please maintain this license information along with authorship +# and copyright notices in any redistribution of this code +# ********************************************************************************** +# RFM69/SX1231 Internal registers addresses +#************************************************** + +REG_FIFO = 0x00 +REG_OPMODE = 0x01 +REG_DATAMODUL = 0x02 +REG_BITRATEMSB = 0x03 +REG_BITRATELSB = 0x04 +REG_FDEVMSB = 0x05 +REG_FDEVLSB = 0x06 +REG_FRFMSB = 0x07 +REG_FRFMID = 0x08 +REG_FRFLSB = 0x09 +REG_OSC1 = 0x0A +REG_AFCCTRL = 0x0B +REG_LOWBAT = 0x0C +REG_LISTEN1 = 0x0D +REG_LISTEN2 = 0x0E +REG_LISTEN3 = 0x0F +REG_VERSION = 0x10 +REG_PALEVEL = 0x11 +REG_PARAMP = 0x12 +REG_OCP = 0x13 +REG_AGCREF = 0x14 +REG_AGCTHRESH1 = 0x15 +REG_AGCTHRESH2 = 0x16 +REG_AGCTHRESH3 = 0x17 +REG_LNA = 0x18 +REG_RXBW = 0x19 +REG_AFCBW = 0x1A +REG_OOKPEAK = 0x1B +REG_OOKAVG = 0x1C +REG_OOKFIX = 0x1D +REG_AFCFEI = 0x1E +REG_AFCMSB = 0x1F +REG_AFCLSB = 0x20 +REG_FEIMSB = 0x21 +REG_FEILSB = 0x22 +REG_RSSICONFIG = 0x23 +REG_RSSIVALUE = 0x24 +REG_DIOMAPPING1 = 0x25 +REG_DIOMAPPING2 = 0x26 +REG_IRQFLAGS1 = 0x27 +REG_IRQFLAGS2 = 0x28 +REG_RSSITHRESH = 0x29 +REG_RXTIMEOUT1 = 0x2A +REG_RXTIMEOUT2 = 0x2B +REG_PREAMBLEMSB = 0x2C +REG_PREAMBLELSB = 0x2D +REG_SYNCCONFIG = 0x2E +REG_SYNCVALUE1 = 0x2F +REG_SYNCVALUE2 = 0x30 +REG_SYNCVALUE3 = 0x31 +REG_SYNCVALUE4 = 0x32 +REG_SYNCVALUE5 = 0x33 +REG_SYNCVALUE6 = 0x34 +REG_SYNCVALUE7 = 0x35 +REG_SYNCVALUE8 = 0x36 +REG_PACKETCONFIG1 = 0x37 +REG_PAYLOADLENGTH = 0x38 +REG_NODEADRS = 0x39 +REG_BROADCASTADRS = 0x3A +REG_AUTOMODES = 0x3B +REG_FIFOTHRESH = 0x3C +REG_PACKETCONFIG2 = 0x3D +REG_AESKEY1 = 0x3E +REG_AESKEY2 = 0x3F +REG_AESKEY3 = 0x40 +REG_AESKEY4 = 0x41 +REG_AESKEY5 = 0x42 +REG_AESKEY6 = 0x43 +REG_AESKEY7 = 0x44 +REG_AESKEY8 = 0x45 +REG_AESKEY9 = 0x46 +REG_AESKEY10 = 0x47 +REG_AESKEY11 = 0x48 +REG_AESKEY12 = 0x49 +REG_AESKEY13 = 0x4A +REG_AESKEY14 = 0x4B +REG_AESKEY15 = 0x4C +REG_AESKEY16 = 0x4D +REG_TEMP1 = 0x4E +REG_TEMP2 = 0x4F +REG_TESTPA1 = 0x5A #only present on RFM69HW/SX1231H +REG_TESTPA2 = 0x5C #only present on RFM69HW/SX1231H +REG_TESTDAGC = 0x6F + +#****************************************************** +# RF69/SX1231 bit control definition +#****************************************************** +# RegOpMode +RF_OPMODE_SEQUENCER_OFF = 0x80 +RF_OPMODE_SEQUENCER_ON = 0x00 # Default + +RF_OPMODE_LISTEN_ON = 0x40 +RF_OPMODE_LISTEN_OFF = 0x00 # Default + +RF_OPMODE_LISTENABORT = 0x20 + +RF_OPMODE_SLEEP = 0x00 +RF_OPMODE_STANDBY = 0x04 # Default +RF_OPMODE_SYNTHESIZER = 0x08 +RF_OPMODE_TRANSMITTER = 0x0C +RF_OPMODE_RECEIVER = 0x10 + +# RegDataModul +RF_DATAMODUL_DATAMODE_PACKET = 0x00 # Default +RF_DATAMODUL_DATAMODE_CONTINUOUS = 0x40 +RF_DATAMODUL_DATAMODE_CONTINUOUSNOBSYNC = 0x60 + +RF_DATAMODUL_MODULATIONTYPE_FSK = 0x00 # Default +RF_DATAMODUL_MODULATIONTYPE_OOK = 0x08 + +RF_DATAMODUL_MODULATIONSHAPING_00 = 0x00 # Default +RF_DATAMODUL_MODULATIONSHAPING_01 = 0x01 +RF_DATAMODUL_MODULATIONSHAPING_10 = 0x02 +RF_DATAMODUL_MODULATIONSHAPING_11 = 0x03 + +# RegBitRate (bits/sec) example bit rates +RF_BITRATEMSB_1200 = 0x68 +RF_BITRATELSB_1200 = 0x2B +RF_BITRATEMSB_2400 = 0x34 +RF_BITRATELSB_2400 = 0x15 +RF_BITRATEMSB_4800 = 0x1A # Default +RF_BITRATELSB_4800 = 0x0B # Default +RF_BITRATEMSB_9600 = 0x0D +RF_BITRATELSB_9600 = 0x05 +RF_BITRATEMSB_19200 = 0x06 +RF_BITRATELSB_19200 = 0x83 +RF_BITRATEMSB_38400 = 0x03 +RF_BITRATELSB_38400 = 0x41 + +RF_BITRATEMSB_38323 = 0x03 +RF_BITRATELSB_38323 = 0x43 + +RF_BITRATEMSB_34482 = 0x03 +RF_BITRATELSB_34482 = 0xA0 + +RF_BITRATEMSB_76800 = 0x01 +RF_BITRATELSB_76800 = 0xA1 +RF_BITRATEMSB_153600 = 0x00 +RF_BITRATELSB_153600 = 0xD0 +RF_BITRATEMSB_57600 = 0x02 +RF_BITRATELSB_57600 = 0x2C +RF_BITRATEMSB_115200 = 0x01 +RF_BITRATELSB_115200 = 0x16 +RF_BITRATEMSB_12500 = 0x0A +RF_BITRATELSB_12500 = 0x00 +RF_BITRATEMSB_25000 = 0x05 +RF_BITRATELSB_25000 = 0x00 +RF_BITRATEMSB_50000 = 0x02 +RF_BITRATELSB_50000 = 0x80 +RF_BITRATEMSB_100000 = 0x01 +RF_BITRATELSB_100000 = 0x40 +RF_BITRATEMSB_150000 = 0x00 +RF_BITRATELSB_150000 = 0xD5 +RF_BITRATEMSB_200000 = 0x00 +RF_BITRATELSB_200000 = 0xA0 +RF_BITRATEMSB_250000 = 0x00 +RF_BITRATELSB_250000 = 0x80 +RF_BITRATEMSB_300000 = 0x00 +RF_BITRATELSB_300000 = 0x6B +RF_BITRATEMSB_32768 = 0x03 +RF_BITRATELSB_32768 = 0xD1 +#custom bit rates +RF_BITRATEMSB_55555 = 0x02 +RF_BITRATELSB_55555 = 0x40 +RF_BITRATEMSB_200KBPS = 0x00 +RF_BITRATELSB_200KBPS = 0xa0 + +# RegFdev - frequency deviation (Hz) +RF_FDEVMSB_2000 = 0x00 +RF_FDEVLSB_2000 = 0x21 +RF_FDEVMSB_5000 = 0x00 # Default +RF_FDEVLSB_5000 = 0x52 # Default +RF_FDEVMSB_7500 = 0x00 +RF_FDEVLSB_7500 = 0x7B +RF_FDEVMSB_10000 = 0x00 +RF_FDEVLSB_10000 = 0xA4 +RF_FDEVMSB_15000 = 0x00 +RF_FDEVLSB_15000 = 0xF6 +RF_FDEVMSB_20000 = 0x01 +RF_FDEVLSB_20000 = 0x48 +RF_FDEVMSB_25000 = 0x01 +RF_FDEVLSB_25000 = 0x9A +RF_FDEVMSB_30000 = 0x01 +RF_FDEVLSB_30000 = 0xEC +RF_FDEVMSB_35000 = 0x02 +RF_FDEVLSB_35000 = 0x3D +RF_FDEVMSB_40000 = 0x02 +RF_FDEVLSB_40000 = 0x8F +RF_FDEVMSB_45000 = 0x02 +RF_FDEVLSB_45000 = 0xE1 +RF_FDEVMSB_50000 = 0x03 +RF_FDEVLSB_50000 = 0x33 +RF_FDEVMSB_55000 = 0x03 +RF_FDEVLSB_55000 = 0x85 +RF_FDEVMSB_60000 = 0x03 +RF_FDEVLSB_60000 = 0xD7 +RF_FDEVMSB_65000 = 0x04 +RF_FDEVLSB_65000 = 0x29 +RF_FDEVMSB_70000 = 0x04 +RF_FDEVLSB_70000 = 0x7B +RF_FDEVMSB_75000 = 0x04 +RF_FDEVLSB_75000 = 0xCD +RF_FDEVMSB_80000 = 0x05 +RF_FDEVLSB_80000 = 0x1F +RF_FDEVMSB_85000 = 0x05 +RF_FDEVLSB_85000 = 0x71 +RF_FDEVMSB_90000 = 0x05 +RF_FDEVLSB_90000 = 0xC3 +RF_FDEVMSB_95000 = 0x06 +RF_FDEVLSB_95000 = 0x14 +RF_FDEVMSB_100000 = 0x06 +RF_FDEVLSB_100000 = 0x66 +RF_FDEVMSB_110000 = 0x07 +RF_FDEVLSB_110000 = 0x0A +RF_FDEVMSB_120000 = 0x07 +RF_FDEVLSB_120000 = 0xAE +RF_FDEVMSB_130000 = 0x08 +RF_FDEVLSB_130000 = 0x52 +RF_FDEVMSB_140000 = 0x08 +RF_FDEVLSB_140000 = 0xF6 +RF_FDEVMSB_150000 = 0x09 +RF_FDEVLSB_150000 = 0x9A +RF_FDEVMSB_160000 = 0x0A +RF_FDEVLSB_160000 = 0x3D +RF_FDEVMSB_170000 = 0x0A +RF_FDEVLSB_170000 = 0xE1 +RF_FDEVMSB_180000 = 0x0B +RF_FDEVLSB_180000 = 0x85 +RF_FDEVMSB_190000 = 0x0C +RF_FDEVLSB_190000 = 0x29 +RF_FDEVMSB_200000 = 0x0C +RF_FDEVLSB_200000 = 0xCD +RF_FDEVMSB_210000 = 0x0D +RF_FDEVLSB_210000 = 0x71 +RF_FDEVMSB_220000 = 0x0E +RF_FDEVLSB_220000 = 0x14 +RF_FDEVMSB_230000 = 0x0E +RF_FDEVLSB_230000 = 0xB8 +RF_FDEVMSB_240000 = 0x0F +RF_FDEVLSB_240000 = 0x5C +RF_FDEVMSB_250000 = 0x10 +RF_FDEVLSB_250000 = 0x00 +RF_FDEVMSB_260000 = 0x10 +RF_FDEVLSB_260000 = 0xA4 +RF_FDEVMSB_270000 = 0x11 +RF_FDEVLSB_270000 = 0x48 +RF_FDEVMSB_280000 = 0x11 +RF_FDEVLSB_280000 = 0xEC +RF_FDEVMSB_290000 = 0x12 +RF_FDEVLSB_290000 = 0x8F +RF_FDEVMSB_300000 = 0x13 +RF_FDEVLSB_300000 = 0x33 + +# RegFrf (MHz) - carrier frequency +# 315Mhz band +RF_FRFMSB_314 = 0x4E +RF_FRFMID_314 = 0x80 +RF_FRFLSB_314 = 0x00 +RF_FRFMSB_315 = 0x4E +RF_FRFMID_315 = 0xC0 +RF_FRFLSB_315 = 0x00 +RF_FRFMSB_316 = 0x4F +RF_FRFMID_316 = 0x00 +RF_FRFLSB_316 = 0x00 +# 433mhz band +RF_FRFMSB_433 = 0x6C +RF_FRFMID_433 = 0x40 +RF_FRFLSB_433 = 0x00 +RF_FRFMSB_434 = 0x6C +RF_FRFMID_434 = 0x80 +RF_FRFLSB_434 = 0x00 +RF_FRFMSB_435 = 0x6C +RF_FRFMID_435 = 0xC0 +RF_FRFLSB_435 = 0x00 +# 868Mhz band +RF_FRFMSB_863 = 0xD7 +RF_FRFMID_863 = 0xC0 +RF_FRFLSB_863 = 0x00 +RF_FRFMSB_864 = 0xD8 +RF_FRFMID_864 = 0x00 +RF_FRFLSB_864 = 0x00 +RF_FRFMSB_865 = 0xD8 +RF_FRFMID_865 = 0x40 +RF_FRFLSB_865 = 0x00 +RF_FRFMSB_866 = 0xD8 +RF_FRFMID_866 = 0x80 +RF_FRFLSB_866 = 0x00 +RF_FRFMSB_867 = 0xD8 +RF_FRFMID_867 = 0xC0 +RF_FRFLSB_867 = 0x00 +RF_FRFMSB_868 = 0xD9 +RF_FRFMID_868 = 0x00 +RF_FRFLSB_868 = 0x00 +RF_FRFMSB_869 = 0xD9 +RF_FRFMID_869 = 0x40 +RF_FRFLSB_869 = 0x00 +RF_FRFMSB_870 = 0xD9 +RF_FRFMID_870 = 0x80 +RF_FRFLSB_870 = 0x00 +# 915Mhz band +RF_FRFMSB_902 = 0xE1 +RF_FRFMID_902 = 0x80 +RF_FRFLSB_902 = 0x00 +RF_FRFMSB_903 = 0xE1 +RF_FRFMID_903 = 0xC0 +RF_FRFLSB_903 = 0x00 +RF_FRFMSB_904 = 0xE2 +RF_FRFMID_904 = 0x00 +RF_FRFLSB_904 = 0x00 +RF_FRFMSB_905 = 0xE2 +RF_FRFMID_905 = 0x40 +RF_FRFLSB_905 = 0x00 +RF_FRFMSB_906 = 0xE2 +RF_FRFMID_906 = 0x80 +RF_FRFLSB_906 = 0x00 +RF_FRFMSB_907 = 0xE2 +RF_FRFMID_907 = 0xC0 +RF_FRFLSB_907 = 0x00 +RF_FRFMSB_908 = 0xE3 +RF_FRFMID_908 = 0x00 +RF_FRFLSB_908 = 0x00 +RF_FRFMSB_909 = 0xE3 +RF_FRFMID_909 = 0x40 +RF_FRFLSB_909 = 0x00 +RF_FRFMSB_910 = 0xE3 +RF_FRFMID_910 = 0x80 +RF_FRFLSB_910 = 0x00 +RF_FRFMSB_911 = 0xE3 +RF_FRFMID_911 = 0xC0 +RF_FRFLSB_911 = 0x00 +RF_FRFMSB_912 = 0xE4 +RF_FRFMID_912 = 0x00 +RF_FRFLSB_912 = 0x00 +RF_FRFMSB_913 = 0xE4 +RF_FRFMID_913 = 0x40 +RF_FRFLSB_913 = 0x00 +RF_FRFMSB_914 = 0xE4 +RF_FRFMID_914 = 0x80 +RF_FRFLSB_914 = 0x00 +RF_FRFMSB_915 = 0xE4 # Default +RF_FRFMID_915 = 0xC0 # Default +RF_FRFLSB_915 = 0x00 # Default +RF_FRFMSB_916 = 0xE5 +RF_FRFMID_916 = 0x00 +RF_FRFLSB_916 = 0x00 +RF_FRFMSB_917 = 0xE5 +RF_FRFMID_917 = 0x40 +RF_FRFLSB_917 = 0x00 +RF_FRFMSB_918 = 0xE5 +RF_FRFMID_918 = 0x80 +RF_FRFLSB_918 = 0x00 +RF_FRFMSB_919 = 0xE5 +RF_FRFMID_919 = 0xC0 +RF_FRFLSB_919 = 0x00 +RF_FRFMSB_920 = 0xE6 +RF_FRFMID_920 = 0x00 +RF_FRFLSB_920 = 0x00 +RF_FRFMSB_921 = 0xE6 +RF_FRFMID_921 = 0x40 +RF_FRFLSB_921 = 0x00 +RF_FRFMSB_922 = 0xE6 +RF_FRFMID_922 = 0x80 +RF_FRFLSB_922 = 0x00 +RF_FRFMSB_923 = 0xE6 +RF_FRFMID_923 = 0xC0 +RF_FRFLSB_923 = 0x00 +RF_FRFMSB_924 = 0xE7 +RF_FRFMID_924 = 0x00 +RF_FRFLSB_924 = 0x00 +RF_FRFMSB_925 = 0xE7 +RF_FRFMID_925 = 0x40 +RF_FRFLSB_925 = 0x00 +RF_FRFMSB_926 = 0xE7 +RF_FRFMID_926 = 0x80 +RF_FRFLSB_926 = 0x00 +RF_FRFMSB_927 = 0xE7 +RF_FRFMID_927 = 0xC0 +RF_FRFLSB_927 = 0x00 +RF_FRFMSB_928 = 0xE8 +RF_FRFMID_928 = 0x00 +RF_FRFLSB_928 = 0x00 + + +# RegOsc1 +RF_OSC1_RCCAL_START = 0x80 +RF_OSC1_RCCAL_DONE = 0x40 + +# RegLowBat +RF_LOWBAT_MONITOR = 0x10 +RF_LOWBAT_ON = 0x08 +RF_LOWBAT_OFF = 0x00 # Default + +RF_LOWBAT_TRIM_1695 = 0x00 +RF_LOWBAT_TRIM_1764 = 0x01 +RF_LOWBAT_TRIM_1835 = 0x02 # Default +RF_LOWBAT_TRIM_1905 = 0x03 +RF_LOWBAT_TRIM_1976 = 0x04 +RF_LOWBAT_TRIM_2045 = 0x05 +RF_LOWBAT_TRIM_2116 = 0x06 +RF_LOWBAT_TRIM_2185 = 0x07 + + +# RegListen1 +RF_LISTEN1_RESOL_64 = 0x50 +RF_LISTEN1_RESOL_4100 = 0xA0 # Default +RF_LISTEN1_RESOL_262000 = 0xF0 + +RF_LISTEN1_CRITERIA_RSSI = 0x00 # Default +RF_LISTEN1_CRITERIA_RSSIANDSYNC = 0x08 + +RF_LISTEN1_END_00 = 0x00 +RF_LISTEN1_END_01 = 0x02 # Default +RF_LISTEN1_END_10 = 0x04 + + +# RegListen2 +RF_LISTEN2_COEFIDLE_VALUE = 0xF5 # Default + +# RegListen3 +RF_LISTEN3_COEFRX_VALUE = 0x20 # Default + +# RegPaLevel +RF_PALEVEL_PA0_ON = 0x80 # Default +RF_PALEVEL_PA0_OFF = 0x00 +RF_PALEVEL_PA1_ON = 0x40 +RF_PALEVEL_PA1_OFF = 0x00 # Default +RF_PALEVEL_PA2_ON = 0x20 +RF_PALEVEL_PA2_OFF = 0x00 # Default + +RF_PALEVEL_OUTPUTPOWER_00000 = 0x00 +RF_PALEVEL_OUTPUTPOWER_00001 = 0x01 +RF_PALEVEL_OUTPUTPOWER_00010 = 0x02 +RF_PALEVEL_OUTPUTPOWER_00011 = 0x03 +RF_PALEVEL_OUTPUTPOWER_00100 = 0x04 +RF_PALEVEL_OUTPUTPOWER_00101 = 0x05 +RF_PALEVEL_OUTPUTPOWER_00110 = 0x06 +RF_PALEVEL_OUTPUTPOWER_00111 = 0x07 +RF_PALEVEL_OUTPUTPOWER_01000 = 0x08 +RF_PALEVEL_OUTPUTPOWER_01001 = 0x09 +RF_PALEVEL_OUTPUTPOWER_01010 = 0x0A +RF_PALEVEL_OUTPUTPOWER_01011 = 0x0B +RF_PALEVEL_OUTPUTPOWER_01100 = 0x0C +RF_PALEVEL_OUTPUTPOWER_01101 = 0x0D +RF_PALEVEL_OUTPUTPOWER_01110 = 0x0E +RF_PALEVEL_OUTPUTPOWER_01111 = 0x0F +RF_PALEVEL_OUTPUTPOWER_10000 = 0x10 +RF_PALEVEL_OUTPUTPOWER_10001 = 0x11 +RF_PALEVEL_OUTPUTPOWER_10010 = 0x12 +RF_PALEVEL_OUTPUTPOWER_10011 = 0x13 +RF_PALEVEL_OUTPUTPOWER_10100 = 0x14 +RF_PALEVEL_OUTPUTPOWER_10101 = 0x15 +RF_PALEVEL_OUTPUTPOWER_10110 = 0x16 +RF_PALEVEL_OUTPUTPOWER_10111 = 0x17 +RF_PALEVEL_OUTPUTPOWER_11000 = 0x18 +RF_PALEVEL_OUTPUTPOWER_11001 = 0x19 +RF_PALEVEL_OUTPUTPOWER_11010 = 0x1A +RF_PALEVEL_OUTPUTPOWER_11011 = 0x1B +RF_PALEVEL_OUTPUTPOWER_11100 = 0x1C +RF_PALEVEL_OUTPUTPOWER_11101 = 0x1D +RF_PALEVEL_OUTPUTPOWER_11110 = 0x1E +RF_PALEVEL_OUTPUTPOWER_11111 = 0x1F # Default + + +# RegPaRamp +RF_PARAMP_3400 = 0x00 +RF_PARAMP_2000 = 0x01 +RF_PARAMP_1000 = 0x02 +RF_PARAMP_500 = 0x03 +RF_PARAMP_250 = 0x04 +RF_PARAMP_125 = 0x05 +RF_PARAMP_100 = 0x06 +RF_PARAMP_62 = 0x07 +RF_PARAMP_50 = 0x08 +RF_PARAMP_40 = 0x09 # Default +RF_PARAMP_31 = 0x0A +RF_PARAMP_25 = 0x0B +RF_PARAMP_20 = 0x0C +RF_PARAMP_15 = 0x0D +RF_PARAMP_12 = 0x0E +RF_PARAMP_10 = 0x0F + + +# RegOcp +RF_OCP_OFF = 0x0F +RF_OCP_ON = 0x1A # Default + +RF_OCP_TRIM_45 = 0x00 +RF_OCP_TRIM_50 = 0x01 +RF_OCP_TRIM_55 = 0x02 +RF_OCP_TRIM_60 = 0x03 +RF_OCP_TRIM_65 = 0x04 +RF_OCP_TRIM_70 = 0x05 +RF_OCP_TRIM_75 = 0x06 +RF_OCP_TRIM_80 = 0x07 +RF_OCP_TRIM_85 = 0x08 +RF_OCP_TRIM_90 = 0x09 +RF_OCP_TRIM_95 = 0x0A +RF_OCP_TRIM_100 = 0x0B # Default +RF_OCP_TRIM_105 = 0x0C +RF_OCP_TRIM_110 = 0x0D +RF_OCP_TRIM_115 = 0x0E +RF_OCP_TRIM_120 = 0x0F + + +# RegAgcRef +RF_AGCREF_AUTO_ON = 0x40 # Default +RF_AGCREF_AUTO_OFF = 0x00 + +RF_AGCREF_LEVEL_MINUS80 = 0x00 # Default +RF_AGCREF_LEVEL_MINUS81 = 0x01 +RF_AGCREF_LEVEL_MINUS82 = 0x02 +RF_AGCREF_LEVEL_MINUS83 = 0x03 +RF_AGCREF_LEVEL_MINUS84 = 0x04 +RF_AGCREF_LEVEL_MINUS85 = 0x05 +RF_AGCREF_LEVEL_MINUS86 = 0x06 +RF_AGCREF_LEVEL_MINUS87 = 0x07 +RF_AGCREF_LEVEL_MINUS88 = 0x08 +RF_AGCREF_LEVEL_MINUS89 = 0x09 +RF_AGCREF_LEVEL_MINUS90 = 0x0A +RF_AGCREF_LEVEL_MINUS91 = 0x0B +RF_AGCREF_LEVEL_MINUS92 = 0x0C +RF_AGCREF_LEVEL_MINUS93 = 0x0D +RF_AGCREF_LEVEL_MINUS94 = 0x0E +RF_AGCREF_LEVEL_MINUS95 = 0x0F +RF_AGCREF_LEVEL_MINUS96 = 0x10 +RF_AGCREF_LEVEL_MINUS97 = 0x11 +RF_AGCREF_LEVEL_MINUS98 = 0x12 +RF_AGCREF_LEVEL_MINUS99 = 0x13 +RF_AGCREF_LEVEL_MINUS100 = 0x14 +RF_AGCREF_LEVEL_MINUS101 = 0x15 +RF_AGCREF_LEVEL_MINUS102 = 0x16 +RF_AGCREF_LEVEL_MINUS103 = 0x17 +RF_AGCREF_LEVEL_MINUS104 = 0x18 +RF_AGCREF_LEVEL_MINUS105 = 0x19 +RF_AGCREF_LEVEL_MINUS106 = 0x1A +RF_AGCREF_LEVEL_MINUS107 = 0x1B +RF_AGCREF_LEVEL_MINUS108 = 0x1C +RF_AGCREF_LEVEL_MINUS109 = 0x1D +RF_AGCREF_LEVEL_MINUS110 = 0x1E +RF_AGCREF_LEVEL_MINUS111 = 0x1F +RF_AGCREF_LEVEL_MINUS112 = 0x20 +RF_AGCREF_LEVEL_MINUS113 = 0x21 +RF_AGCREF_LEVEL_MINUS114 = 0x22 +RF_AGCREF_LEVEL_MINUS115 = 0x23 +RF_AGCREF_LEVEL_MINUS116 = 0x24 +RF_AGCREF_LEVEL_MINUS117 = 0x25 +RF_AGCREF_LEVEL_MINUS118 = 0x26 +RF_AGCREF_LEVEL_MINUS119 = 0x27 +RF_AGCREF_LEVEL_MINUS120 = 0x28 +RF_AGCREF_LEVEL_MINUS121 = 0x29 +RF_AGCREF_LEVEL_MINUS122 = 0x2A +RF_AGCREF_LEVEL_MINUS123 = 0x2B +RF_AGCREF_LEVEL_MINUS124 = 0x2C +RF_AGCREF_LEVEL_MINUS125 = 0x2D +RF_AGCREF_LEVEL_MINUS126 = 0x2E +RF_AGCREF_LEVEL_MINUS127 = 0x2F +RF_AGCREF_LEVEL_MINUS128 = 0x30 +RF_AGCREF_LEVEL_MINUS129 = 0x31 +RF_AGCREF_LEVEL_MINUS130 = 0x32 +RF_AGCREF_LEVEL_MINUS131 = 0x33 +RF_AGCREF_LEVEL_MINUS132 = 0x34 +RF_AGCREF_LEVEL_MINUS133 = 0x35 +RF_AGCREF_LEVEL_MINUS134 = 0x36 +RF_AGCREF_LEVEL_MINUS135 = 0x37 +RF_AGCREF_LEVEL_MINUS136 = 0x38 +RF_AGCREF_LEVEL_MINUS137 = 0x39 +RF_AGCREF_LEVEL_MINUS138 = 0x3A +RF_AGCREF_LEVEL_MINUS139 = 0x3B +RF_AGCREF_LEVEL_MINUS140 = 0x3C +RF_AGCREF_LEVEL_MINUS141 = 0x3D +RF_AGCREF_LEVEL_MINUS142 = 0x3E +RF_AGCREF_LEVEL_MINUS143 = 0x3F + + +# RegAgcThresh1 +RF_AGCTHRESH1_SNRMARGIN_000 = 0x00 +RF_AGCTHRESH1_SNRMARGIN_001 = 0x20 +RF_AGCTHRESH1_SNRMARGIN_010 = 0x40 +RF_AGCTHRESH1_SNRMARGIN_011 = 0x60 +RF_AGCTHRESH1_SNRMARGIN_100 = 0x80 +RF_AGCTHRESH1_SNRMARGIN_101 = 0xA0 # Default +RF_AGCTHRESH1_SNRMARGIN_110 = 0xC0 +RF_AGCTHRESH1_SNRMARGIN_111 = 0xE0 + +RF_AGCTHRESH1_STEP1_0 = 0x00 +RF_AGCTHRESH1_STEP1_1 = 0x01 +RF_AGCTHRESH1_STEP1_2 = 0x02 +RF_AGCTHRESH1_STEP1_3 = 0x03 +RF_AGCTHRESH1_STEP1_4 = 0x04 +RF_AGCTHRESH1_STEP1_5 = 0x05 +RF_AGCTHRESH1_STEP1_6 = 0x06 +RF_AGCTHRESH1_STEP1_7 = 0x07 +RF_AGCTHRESH1_STEP1_8 = 0x08 +RF_AGCTHRESH1_STEP1_9 = 0x09 +RF_AGCTHRESH1_STEP1_10 = 0x0A +RF_AGCTHRESH1_STEP1_11 = 0x0B +RF_AGCTHRESH1_STEP1_12 = 0x0C +RF_AGCTHRESH1_STEP1_13 = 0x0D +RF_AGCTHRESH1_STEP1_14 = 0x0E +RF_AGCTHRESH1_STEP1_15 = 0x0F +RF_AGCTHRESH1_STEP1_16 = 0x10 # Default +RF_AGCTHRESH1_STEP1_17 = 0x11 +RF_AGCTHRESH1_STEP1_18 = 0x12 +RF_AGCTHRESH1_STEP1_19 = 0x13 +RF_AGCTHRESH1_STEP1_20 = 0x14 +RF_AGCTHRESH1_STEP1_21 = 0x15 +RF_AGCTHRESH1_STEP1_22 = 0x16 +RF_AGCTHRESH1_STEP1_23 = 0x17 +RF_AGCTHRESH1_STEP1_24 = 0x18 +RF_AGCTHRESH1_STEP1_25 = 0x19 +RF_AGCTHRESH1_STEP1_26 = 0x1A +RF_AGCTHRESH1_STEP1_27 = 0x1B +RF_AGCTHRESH1_STEP1_28 = 0x1C +RF_AGCTHRESH1_STEP1_29 = 0x1D +RF_AGCTHRESH1_STEP1_30 = 0x1E +RF_AGCTHRESH1_STEP1_31 = 0x1F + + +# RegAgcThresh2 +RF_AGCTHRESH2_STEP2_0 = 0x00 +RF_AGCTHRESH2_STEP2_1 = 0x10 +RF_AGCTHRESH2_STEP2_2 = 0x20 +RF_AGCTHRESH2_STEP2_3 = 0x30 # XXX wrong -- Default +RF_AGCTHRESH2_STEP2_4 = 0x40 +RF_AGCTHRESH2_STEP2_5 = 0x50 +RF_AGCTHRESH2_STEP2_6 = 0x60 +RF_AGCTHRESH2_STEP2_7 = 0x70 # default +RF_AGCTHRESH2_STEP2_8 = 0x80 +RF_AGCTHRESH2_STEP2_9 = 0x90 +RF_AGCTHRESH2_STEP2_10 = 0xA0 +RF_AGCTHRESH2_STEP2_11 = 0xB0 +RF_AGCTHRESH2_STEP2_12 = 0xC0 +RF_AGCTHRESH2_STEP2_13 = 0xD0 +RF_AGCTHRESH2_STEP2_14 = 0xE0 +RF_AGCTHRESH2_STEP2_15 = 0xF0 + +RF_AGCTHRESH2_STEP3_0 = 0x00 +RF_AGCTHRESH2_STEP3_1 = 0x01 +RF_AGCTHRESH2_STEP3_2 = 0x02 +RF_AGCTHRESH2_STEP3_3 = 0x03 +RF_AGCTHRESH2_STEP3_4 = 0x04 +RF_AGCTHRESH2_STEP3_5 = 0x05 +RF_AGCTHRESH2_STEP3_6 = 0x06 +RF_AGCTHRESH2_STEP3_7 = 0x07 +RF_AGCTHRESH2_STEP3_8 = 0x08 +RF_AGCTHRESH2_STEP3_9 = 0x09 +RF_AGCTHRESH2_STEP3_10 = 0x0A +RF_AGCTHRESH2_STEP3_11 = 0x0B # Default +RF_AGCTHRESH2_STEP3_12 = 0x0C +RF_AGCTHRESH2_STEP3_13 = 0x0D +RF_AGCTHRESH2_STEP3_14 = 0x0E +RF_AGCTHRESH2_STEP3_15 = 0x0F + + +# RegAgcThresh3 +RF_AGCTHRESH3_STEP4_0 = 0x00 +RF_AGCTHRESH3_STEP4_1 = 0x10 +RF_AGCTHRESH3_STEP4_2 = 0x20 +RF_AGCTHRESH3_STEP4_3 = 0x30 +RF_AGCTHRESH3_STEP4_4 = 0x40 +RF_AGCTHRESH3_STEP4_5 = 0x50 +RF_AGCTHRESH3_STEP4_6 = 0x60 +RF_AGCTHRESH3_STEP4_7 = 0x70 +RF_AGCTHRESH3_STEP4_8 = 0x80 +RF_AGCTHRESH3_STEP4_9 = 0x90 # Default +RF_AGCTHRESH3_STEP4_10 = 0xA0 +RF_AGCTHRESH3_STEP4_11 = 0xB0 +RF_AGCTHRESH3_STEP4_12 = 0xC0 +RF_AGCTHRESH3_STEP4_13 = 0xD0 +RF_AGCTHRESH3_STEP4_14 = 0xE0 +RF_AGCTHRESH3_STEP4_15 = 0xF0 + +RF_AGCTHRESH3_STEP5_0 = 0x00 +RF_AGCTHRESH3_STEP5_1 = 0x01 +RF_AGCTHRESH3_STEP5_2 = 0x02 +RF_AGCTHRESH3_STEP5_3 = 0x03 +RF_AGCTHRESH3_STEP5_4 = 0x04 +RF_AGCTHRESH3_STEP5_5 = 0x05 +RF_AGCTHRESH3_STEP5_6 = 0x06 +RF_AGCTHRESH3_STEP5_7 = 0x07 +RF_AGCTHRES33_STEP5_8 = 0x08 +RF_AGCTHRESH3_STEP5_9 = 0x09 +RF_AGCTHRESH3_STEP5_10 = 0x0A +RF_AGCTHRESH3_STEP5_11 = 0x0B # Default +RF_AGCTHRESH3_STEP5_12 = 0x0C +RF_AGCTHRESH3_STEP5_13 = 0x0D +RF_AGCTHRESH3_STEP5_14 = 0x0E +RF_AGCTHRESH3_STEP5_15 = 0x0F + + +# RegLna +RF_LNA_ZIN_50 = 0x00 +RF_LNA_ZIN_200 = 0x80 # Default + +RF_LNA_LOWPOWER_OFF = 0x00 # Default +RF_LNA_LOWPOWER_ON = 0x40 + +RF_LNA_CURRENTGAIN = 0x08 + +RF_LNA_GAINSELECT_AUTO = 0x00 # Default +RF_LNA_GAINSELECT_MAX = 0x01 +RF_LNA_GAINSELECT_MAXMINUS6 = 0x02 +RF_LNA_GAINSELECT_MAXMINUS12 = 0x03 +RF_LNA_GAINSELECT_MAXMINUS24 = 0x04 +RF_LNA_GAINSELECT_MAXMINUS36 = 0x05 +RF_LNA_GAINSELECT_MAXMINUS48 = 0x06 + + +# RegRxBw +RF_RXBW_DCCFREQ_000 = 0x00 +RF_RXBW_DCCFREQ_001 = 0x20 +RF_RXBW_DCCFREQ_010 = 0x40 # Default +RF_RXBW_DCCFREQ_011 = 0x60 +RF_RXBW_DCCFREQ_100 = 0x80 +RF_RXBW_DCCFREQ_101 = 0xA0 +RF_RXBW_DCCFREQ_110 = 0xC0 +RF_RXBW_DCCFREQ_111 = 0xE0 + +RF_RXBW_MANT_16 = 0x00 +RF_RXBW_MANT_20 = 0x08 +RF_RXBW_MANT_24 = 0x10 # Default + +RF_RXBW_EXP_0 = 0x00 +RF_RXBW_EXP_1 = 0x01 +RF_RXBW_EXP_2 = 0x02 +RF_RXBW_EXP_3 = 0x03 +RF_RXBW_EXP_4 = 0x04 +RF_RXBW_EXP_5 = 0x05 # Default +RF_RXBW_EXP_6 = 0x06 +RF_RXBW_EXP_7 = 0x07 + + +# RegAfcBw +RF_AFCBW_DCCFREQAFC_000 = 0x00 +RF_AFCBW_DCCFREQAFC_001 = 0x20 +RF_AFCBW_DCCFREQAFC_010 = 0x40 +RF_AFCBW_DCCFREQAFC_011 = 0x60 +RF_AFCBW_DCCFREQAFC_100 = 0x80 # Default +RF_AFCBW_DCCFREQAFC_101 = 0xA0 +RF_AFCBW_DCCFREQAFC_110 = 0xC0 +RF_AFCBW_DCCFREQAFC_111 = 0xE0 + +RF_AFCBW_MANTAFC_16 = 0x00 +RF_AFCBW_MANTAFC_20 = 0x08 # Default +RF_AFCBW_MANTAFC_24 = 0x10 + +RF_AFCBW_EXPAFC_0 = 0x00 +RF_AFCBW_EXPAFC_1 = 0x01 +RF_AFCBW_EXPAFC_2 = 0x02 +RF_AFCBW_EXPAFC_3 = 0x03 # Default +RF_AFCBW_EXPAFC_4 = 0x04 +RF_AFCBW_EXPAFC_5 = 0x05 +RF_AFCBW_EXPAFC_6 = 0x06 +RF_AFCBW_EXPAFC_7 = 0x07 + + +# RegOokPeak +RF_OOKPEAK_THRESHTYPE_FIXED = 0x00 +RF_OOKPEAK_THRESHTYPE_PEAK = 0x40 # Default +RF_OOKPEAK_THRESHTYPE_AVERAGE = 0x80 + +RF_OOKPEAK_PEAKTHRESHSTEP_000 = 0x00 # Default +RF_OOKPEAK_PEAKTHRESHSTEP_001 = 0x08 +RF_OOKPEAK_PEAKTHRESHSTEP_010 = 0x10 +RF_OOKPEAK_PEAKTHRESHSTEP_011 = 0x18 +RF_OOKPEAK_PEAKTHRESHSTEP_100 = 0x20 +RF_OOKPEAK_PEAKTHRESHSTEP_101 = 0x28 +RF_OOKPEAK_PEAKTHRESHSTEP_110 = 0x30 +RF_OOKPEAK_PEAKTHRESHSTEP_111 = 0x38 + +RF_OOKPEAK_PEAKTHRESHDEC_000 = 0x00 # Default +RF_OOKPEAK_PEAKTHRESHDEC_001 = 0x01 +RF_OOKPEAK_PEAKTHRESHDEC_010 = 0x02 +RF_OOKPEAK_PEAKTHRESHDEC_011 = 0x03 +RF_OOKPEAK_PEAKTHRESHDEC_100 = 0x04 +RF_OOKPEAK_PEAKTHRESHDEC_101 = 0x05 +RF_OOKPEAK_PEAKTHRESHDEC_110 = 0x06 +RF_OOKPEAK_PEAKTHRESHDEC_111 = 0x07 + + +# RegOokAvg +RF_OOKAVG_AVERAGETHRESHFILT_00 = 0x00 +RF_OOKAVG_AVERAGETHRESHFILT_01 = 0x40 +RF_OOKAVG_AVERAGETHRESHFILT_10 = 0x80 # Default +RF_OOKAVG_AVERAGETHRESHFILT_11 = 0xC0 + + +# RegOokFix +RF_OOKFIX_FIXEDTHRESH_VALUE = 0x06 # Default + + +# RegAfcFei +RF_AFCFEI_FEI_DONE = 0x40 +RF_AFCFEI_FEI_START = 0x20 +RF_AFCFEI_AFC_DONE = 0x10 +RF_AFCFEI_AFCAUTOCLEAR_ON = 0x08 +RF_AFCFEI_AFCAUTOCLEAR_OFF = 0x00 # Default + +RF_AFCFEI_AFCAUTO_ON = 0x04 +RF_AFCFEI_AFCAUTO_OFF = 0x00 # Default + +RF_AFCFEI_AFC_CLEAR = 0x02 +RF_AFCFEI_AFC_START = 0x01 + +# RegRssiConfig +RF_RSSI_FASTRX_ON = 0x08 +RF_RSSI_FASTRX_OFF = 0x00 # Default +RF_RSSI_DONE = 0x02 +RF_RSSI_START = 0x01 + + +# RegDioMapping1 +RF_DIOMAPPING1_DIO0_00 = 0x00 # Default +RF_DIOMAPPING1_DIO0_01 = 0x40 +RF_DIOMAPPING1_DIO0_10 = 0x80 +RF_DIOMAPPING1_DIO0_11 = 0xC0 + +RF_DIOMAPPING1_DIO1_00 = 0x00 # Default +RF_DIOMAPPING1_DIO1_01 = 0x10 +RF_DIOMAPPING1_DIO1_10 = 0x20 +RF_DIOMAPPING1_DIO1_11 = 0x30 + +RF_DIOMAPPING1_DIO2_00 = 0x00 # Default +RF_DIOMAPPING1_DIO2_01 = 0x04 +RF_DIOMAPPING1_DIO2_10 = 0x08 +RF_DIOMAPPING1_DIO2_11 = 0x0C + +RF_DIOMAPPING1_DIO3_00 = 0x00 # Default +RF_DIOMAPPING1_DIO3_01 = 0x01 +RF_DIOMAPPING1_DIO3_10 = 0x02 +RF_DIOMAPPING1_DIO3_11 = 0x03 + + +# RegDioMapping2 +RF_DIOMAPPING2_DIO4_00 = 0x00 # Default +RF_DIOMAPPING2_DIO4_01 = 0x40 +RF_DIOMAPPING2_DIO4_10 = 0x80 +RF_DIOMAPPING2_DIO4_11 = 0xC0 + +RF_DIOMAPPING2_DIO5_00 = 0x00 # Default +RF_DIOMAPPING2_DIO5_01 = 0x10 +RF_DIOMAPPING2_DIO5_10 = 0x20 +RF_DIOMAPPING2_DIO5_11 = 0x30 + +RF_DIOMAPPING2_CLKOUT_32 = 0x00 +RF_DIOMAPPING2_CLKOUT_16 = 0x01 +RF_DIOMAPPING2_CLKOUT_8 = 0x02 +RF_DIOMAPPING2_CLKOUT_4 = 0x03 +RF_DIOMAPPING2_CLKOUT_2 = 0x04 +RF_DIOMAPPING2_CLKOUT_1 = 0x05 +RF_DIOMAPPING2_CLKOUT_RC = 0x06 +RF_DIOMAPPING2_CLKOUT_OFF = 0x07 # Default + + +# RegIrqFlags1 +RF_IRQFLAGS1_MODEREADY = 0x80 +RF_IRQFLAGS1_RXREADY = 0x40 +RF_IRQFLAGS1_TXREADY = 0x20 +RF_IRQFLAGS1_PLLLOCK = 0x10 +RF_IRQFLAGS1_RSSI = 0x08 +RF_IRQFLAGS1_TIMEOUT = 0x04 +RF_IRQFLAGS1_AUTOMODE = 0x02 +RF_IRQFLAGS1_SYNCADDRESSMATCH = 0x01 + +# RegIrqFlags2 +RF_IRQFLAGS2_FIFOFULL = 0x80 +RF_IRQFLAGS2_FIFONOTEMPTY = 0x40 +RF_IRQFLAGS2_FIFOLEVEL = 0x20 +RF_IRQFLAGS2_FIFOOVERRUN = 0x10 +RF_IRQFLAGS2_PACKETSENT = 0x08 +RF_IRQFLAGS2_PAYLOADREADY = 0x04 +RF_IRQFLAGS2_CRCOK = 0x02 +RF_IRQFLAGS2_LOWBAT = 0x01 + +# RegRssiThresh +RF_RSSITHRESH_VALUE = 0xE4 # Default + +# RegRxTimeout1 +RF_RXTIMEOUT1_RXSTART_VALUE = 0x00 # Default + +# RegRxTimeout2 +RF_RXTIMEOUT2_RSSITHRESH_VALUE = 0x00 # Default + +# RegPreamble +RF_PREAMBLESIZE_MSB_VALUE = 0x00 # Default +RF_PREAMBLESIZE_LSB_VALUE = 0x03 # Default + + +# RegSyncConfig +RF_SYNC_ON = 0x80 # Default +RF_SYNC_OFF = 0x00 + +RF_SYNC_FIFOFILL_AUTO = 0x00 # Default -- when sync interrupt occurs +RF_SYNC_FIFOFILL_MANUAL = 0x40 + +RF_SYNC_SIZE_1 = 0x00 +RF_SYNC_SIZE_2 = 0x08 +RF_SYNC_SIZE_3 = 0x10 +RF_SYNC_SIZE_4 = 0x18 # Default +RF_SYNC_SIZE_5 = 0x20 +RF_SYNC_SIZE_6 = 0x28 +RF_SYNC_SIZE_7 = 0x30 +RF_SYNC_SIZE_8 = 0x38 + +RF_SYNC_TOL_0 = 0x00 # Default +RF_SYNC_TOL_1 = 0x01 +RF_SYNC_TOL_2 = 0x02 +RF_SYNC_TOL_3 = 0x03 +RF_SYNC_TOL_4 = 0x04 +RF_SYNC_TOL_5 = 0x05 +RF_SYNC_TOL_6 = 0x06 +RF_SYNC_TOL_7 = 0x07 + + +# RegSyncValue1-8 +RF_SYNC_BYTE1_VALUE = 0x00 # Default +RF_SYNC_BYTE2_VALUE = 0x00 # Default +RF_SYNC_BYTE3_VALUE = 0x00 # Default +RF_SYNC_BYTE4_VALUE = 0x00 # Default +RF_SYNC_BYTE5_VALUE = 0x00 # Default +RF_SYNC_BYTE6_VALUE = 0x00 # Default +RF_SYNC_BYTE7_VALUE = 0x00 # Default +RF_SYNC_BYTE8_VALUE = 0x00 # Default + + +# RegPacketConfig1 +RF_PACKET1_FORMAT_FIXED = 0x00 # Default +RF_PACKET1_FORMAT_VARIABLE = 0x80 + +RF_PACKET1_DCFREE_OFF = 0x00 # Default +RF_PACKET1_DCFREE_MANCHESTER = 0x20 +RF_PACKET1_DCFREE_WHITENING = 0x40 + +RF_PACKET1_CRC_ON = 0x10 # Default +RF_PACKET1_CRC_OFF = 0x00 + +RF_PACKET1_CRCAUTOCLEAR_ON = 0x00 # Default +RF_PACKET1_CRCAUTOCLEAR_OFF = 0x08 + +RF_PACKET1_ADRSFILTERING_OFF = 0x00 # Default +RF_PACKET1_ADRSFILTERING_NODE = 0x02 +RF_PACKET1_ADRSFILTERING_NODEBROADCAST = 0x04 + + +# RegPayloadLength +RF_PAYLOADLENGTH_VALUE = 0x40 # Default + +# RegBroadcastAdrs +RF_BROADCASTADDRESS_VALUE = 0x00 + + +# RegAutoModes +RF_AUTOMODES_ENTER_OFF = 0x00 # Default +RF_AUTOMODES_ENTER_FIFONOTEMPTY = 0x20 +RF_AUTOMODES_ENTER_FIFOLEVEL = 0x40 +RF_AUTOMODES_ENTER_CRCOK = 0x60 +RF_AUTOMODES_ENTER_PAYLOADREADY = 0x80 +RF_AUTOMODES_ENTER_SYNCADRSMATCH = 0xA0 +RF_AUTOMODES_ENTER_PACKETSENT = 0xC0 +RF_AUTOMODES_ENTER_FIFOEMPTY = 0xE0 + +RF_AUTOMODES_EXIT_OFF = 0x00 # Default +RF_AUTOMODES_EXIT_FIFOEMPTY = 0x04 +RF_AUTOMODES_EXIT_FIFOLEVEL = 0x08 +RF_AUTOMODES_EXIT_CRCOK = 0x0C +RF_AUTOMODES_EXIT_PAYLOADREADY = 0x10 +RF_AUTOMODES_EXIT_SYNCADRSMATCH = 0x14 +RF_AUTOMODES_EXIT_PACKETSENT = 0x18 +RF_AUTOMODES_EXIT_RXTIMEOUT = 0x1C + +RF_AUTOMODES_INTERMEDIATE_SLEEP = 0x00 # Default +RF_AUTOMODES_INTERMEDIATE_STANDBY = 0x01 +RF_AUTOMODES_INTERMEDIATE_RECEIVER = 0x02 +RF_AUTOMODES_INTERMEDIATE_TRANSMITTER = 0x03 + + +# RegFifoThresh +RF_FIFOTHRESH_TXSTART_FIFOTHRESH = 0x00 +RF_FIFOTHRESH_TXSTART_FIFONOTEMPTY = 0x80 # Default + +RF_FIFOTHRESH_VALUE = 0x0F # Default + + +# RegPacketConfig2 +RF_PACKET2_RXRESTARTDELAY_1BIT = 0x00 # Default +RF_PACKET2_RXRESTARTDELAY_2BITS = 0x10 +RF_PACKET2_RXRESTARTDELAY_4BITS = 0x20 +RF_PACKET2_RXRESTARTDELAY_8BITS = 0x30 +RF_PACKET2_RXRESTARTDELAY_16BITS = 0x40 +RF_PACKET2_RXRESTARTDELAY_32BITS = 0x50 +RF_PACKET2_RXRESTARTDELAY_64BITS = 0x60 +RF_PACKET2_RXRESTARTDELAY_128BITS = 0x70 +RF_PACKET2_RXRESTARTDELAY_256BITS = 0x80 +RF_PACKET2_RXRESTARTDELAY_512BITS = 0x90 +RF_PACKET2_RXRESTARTDELAY_1024BITS = 0xA0 +RF_PACKET2_RXRESTARTDELAY_2048BITS = 0xB0 +RF_PACKET2_RXRESTARTDELAY_NONE = 0xC0 +RF_PACKET2_RXRESTART = 0x04 + +RF_PACKET2_AUTORXRESTART_ON = 0x02 # Default +RF_PACKET2_AUTORXRESTART_OFF = 0x00 + +RF_PACKET2_AES_ON = 0x01 +RF_PACKET2_AES_OFF = 0x00 # Default + + +# RegAesKey1-16 +RF_AESKEY1_VALUE = 0x00 # Default +RF_AESKEY2_VALUE = 0x00 # Default +RF_AESKEY3_VALUE = 0x00 # Default +RF_AESKEY4_VALUE = 0x00 # Default +RF_AESKEY5_VALUE = 0x00 # Default +RF_AESKEY6_VALUE = 0x00 # Default +RF_AESKEY7_VALUE = 0x00 # Default +RF_AESKEY8_VALUE = 0x00 # Default +RF_AESKEY9_VALUE = 0x00 # Default +RF_AESKEY10_VALUE = 0x00 # Default +RF_AESKEY11_VALUE = 0x00 # Default +RF_AESKEY12_VALUE = 0x00 # Default +RF_AESKEY13_VALUE = 0x00 # Default +RF_AESKEY14_VALUE = 0x00 # Default +RF_AESKEY15_VALUE = 0x00 # Default +RF_AESKEY16_VALUE = 0x00 # Default + + +# RegTemp1 +RF_TEMP1_MEAS_START = 0x08 +RF_TEMP1_MEAS_RUNNING = 0x04 +RF_TEMP1_ADCLOWPOWER_ON = 0x01 # Default +RF_TEMP1_ADCLOWPOWER_OFF = 0x00 + +# RegTestDagc 0x6F: demodulator config and IO mode config +RF_DAGC_NORMAL = 0x00 # Reset value +RF_DAGC_IMPROVED_LOWBETA1 = 0x20 # +RF_DAGC_IMPROVED_LOWBETA0 = 0x30 # Recommended default + +#settings pulled from RFM69.h +RF69_315MHZ = 31 # non trivial values to avoid misconfiguration +RF69_433MHZ = 43 +RF69_868MHZ = 86 +RF69_915MHZ = 91 + +RF69_MAX_DATA_LEN = 61 # to take advantage of the built in AES/CRC we want to limit the frame size to the internal FIFO size (66 bytes - 3 bytes overhead) + +CSMA_LIMIT = -90 # upper RX signal sensitivity threshold in dBm for carrier sense access +RF69_MODE_SLEEP = 0 # XTAL OFF +RF69_MODE_STANDBY = 1 # XTAL ON +RF69_MODE_SYNTH = 2 # PLL ON +RF69_MODE_RX = 3 # RX MODE +RF69_MODE_TX = 4 # TX MODE + +COURSE_TEMP_COEF = -90 # puts the temperature reading in the ballpark, user can fine tune the returned value +RF69_BROADCAST_ADDR = 255 +RF69_CSMA_LIMIT_MS = 1000 +RF69_CSMA_LIMIT_S = 1 + +powerLevel = 31 diff --git a/sensord/RRD.py b/sensord/RRD.py new file mode 100644 index 0000000..78f915b --- /dev/null +++ b/sensord/RRD.py @@ -0,0 +1,157 @@ +""" Module to Store Sensor Data into an RRD Database and update Corresponding Graphs """ + +import _thread, time +import rrdtool + +class RRDDB(object): + """ Class to Represent the RRD Data of a Single Multi-Value Sensor """ + + def __init__(self, rrdfile, sid, imgpath=None, sensor_type="thp"): + """ Initialize Class + + Arguments: + rrdfile: Path to the RRD Database File (mandatory) + sid: Sensor ID which this RRD Database is for (mandatory) + imgpath: Path to Store Images, if set Images will be updated on call of notify() + sensor_type: Any combination of the following Letters: + t - Temperature + h - Humidity + p - Pressure + Default is 'thp' for a sensor sending all 3 values + """ + + self.__rrdfile = rrdfile + self.__sid = sid + self.__imgpath = imgpath + self.__t = bool('t' in sensor_type) + self.__h = bool('h' in sensor_type) + self.__p = bool('p' in sensor_type) + + def create(self, interval): + """ Create a new RRD Database According to the Specified Sensor Types + + Arguments: + interval: Seconds between Sensor Updates + """ + + datasrc = [] + datasrc.append('DS:v:GAUGE:' + str(interval*2) + ':U:U') + datasrc.append('DS:r:GAUGE:' + str(interval*2) + ':U:U') + if self.__t: + datasrc.append('DS:t:GAUGE:' + str(interval*2) + ':U:U') + if self.__h: + datasrc.append('DS:h:GAUGE:' + str(interval*2) + ':U:U') + if self.__p: + datasrc.append('DS:p:GAUGE:' + str(interval*2) + ':U:U') + + rrdtool.create(self.__rrdfile, + '--step', str(interval), + datasrc, + 'RRA:LAST:0.5:1:105120', + 'RRA:AVERAGE:0.5:6:175680', + 'RRA:MAX:0.5:6:175680', + 'RRA:MIN:0.5:6:175680') + + def notify(self, msg): + """ Notify Method Called from the Receiver Class, Inserts Values into the RRD Database + + Arguments: + msg: Object Representation of the Sensor Data + """ + + if msg['sid'] == self.__sid: + updstr = 'N:' + str(msg['v']) + ':' + str(msg['rssi']) + if self.__t: + updstr = updstr + ':' + str(msg['t']) + if self.__h: + updstr = updstr + ':' + str(msg['h']) + if self.__p: + updstr = updstr + ':' + str(msg['p']) + rrdtool.update(self.__rrdfile, updstr) + + if self.__imgpath != None: + _thread.start_new_thread(self.update_web, ()) + + def _graph(self, value, start): + """ Draw a Graph for a Specified Sensor Value + + Arguments: + value: Sensor Identifier (t, h, p, v or r) + start: When to Start the Graph (24h, 7d, 30d, 1y) + """ + + types = {'t': ['Temperatur', '#FF0000', '°C'], + 'h': ['Luftfeuchte', '#0000FF', '%%'], + 'p': ['Luftdruck', '#00FF00', 'hPa'], + 'v': ['Batterie', '#FFFF00', 'V'], + 'r': ['RSSI', '#00FFFF', '']} + times = {'24h': ['Letzte 24h', 'LAST'], + '7d': ['Letzte 7 Tage', 'AVERAGE'], + '30d': ['Letzte 30 Tage', 'AVERAGE'], + '1y': ['Letzte 12 Monate', 'AVERAGE']} + + rrdtool.graph(self.__imgpath + '/graph_' + value + '_' + start + '.png', + '-s', 'now-' + start, '-e', 'now', + '-t', times[start][0], '-z', '-A', + 'DEF:val=' + self.__rrdfile + ':'+ value + ':' + times[start][1], + 'VDEF:valmax=val,MAXIMUM', + 'VDEF:valmin=val,MINIMUM', + 'VDEF:valavg=val,AVERAGE', + 'LINE2:val'+types[value][1]+':'+types[value][0], + 'GPRINT:valavg:Avg %2.2lf '+types[value][2], + 'GPRINT:valmin:Min %2.2lf '+types[value][2], + 'GPRINT:valmax:Max %2.2lf '+types[value][2]) + + def update_web(self): + """ Renew RRD Graphs and HTML for the Sensor Values """ + + if self.__t: + self._graph('t', '24h') + self._graph('t', '7d') + self._graph('t', '30d') + self._graph('t', '1y') + if self.__h: + self._graph('h', '24h') + self._graph('h', '7d') + self._graph('h', '30d') + self._graph('h', '1y') + if self.__p: + self._graph('p', '24h') + self._graph('p', '7d') + self._graph('p', '30d') + self._graph('p', '1y') + + last = rrdtool.lastupdate(self.__rrdfile) + + with open(self.__imgpath + '/index.html', 'w') as html: + html.write('\n\n') + html.write('\n') + html.write('') + html.write('Sensorlog RBS 3.OG\n') + html.write('\n\n
') + if self.__t: + html.write('

Temperatur

') + html.write('

Aktuell ' + str(last['ds']['t']) + ' °C

') + html.write('Daily Graph') + html.write('Weekly Graph') + html.write('Monthly Graph') + html.write('Yearly Graph
\n') + if self.__h: + html.write('

Luftfeuchte

') + html.write('

Aktuell ' + str(last['ds']['h']) + ' %

') + html.write('Daily Graph') + html.write('Weekly Graph') + html.write('Monthly Graph') + html.write('Yearly Graph
\n') + if self.__p: + html.write('

Luftdruck

') + html.write('

Aktuell ' + str(last['ds']['p']) + ' hPa

') + html.write('Daily Graph') + html.write('Weekly Graph') + html.write('Monthly Graph') + html.write('Yearly Graph
\n') + if float(last['ds']['v']) < 2.5: + html.write('

Batterien sind fast leer, bitte Wechseln!

') + html.write('

Letztes Update: '+time.strftime('%d.%m.%Y %H:%M:%S')+' | Batteriespannung: '+str(last['ds']['v'])+'V

') + html.write('
')