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('
')
+ html.write('
')
+ html.write('
')
+ html.write('
\n')
+ if self.__h:
+ html.write('
Luftfeuchte
')
+ html.write('
Aktuell ' + str(last['ds']['h']) + ' %
')
+ html.write('
')
+ html.write('
')
+ html.write('
')
+ html.write('
\n')
+ if self.__p:
+ html.write('
Luftdruck
')
+ html.write('
Aktuell ' + str(last['ds']['p']) + ' hPa
')
+ html.write('
')
+ html.write('
')
+ html.write('
')
+ html.write('
\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('
')