clean up & project reorganisation

installable pythonmodule
This commit is contained in:
Stefan Seegel 2020-01-22 22:09:15 +01:00
parent b3f24766b0
commit 7aa238d029
20 changed files with 1000 additions and 418 deletions

3
.gitignore vendored
View file

@ -1 +1,4 @@
*.pyc *.pyc
/dist/
/build/
/*.egg-info

View file

@ -1,11 +1,39 @@
# Python files for RaspyRFM # Python module & examples for RaspyRFM
![RaspyRFM](img/raspberry-rfm69-ii_6_z4.jpg?raw=true "RasyRFM")
#Installation
```sh
sudo su
apt-get install git-core python-dev
apt-get install python-pip
cd <downloaded RaspyRFM code> #folder in which setup.py is located
pip install -e .
```
#Using python module and set parameters
```python
import RasyRFM
rfm = RaspyRFM.RaspyRFM()
rfm.SetParams(
Freq = 868.300, #MHz center frequency
ModulationType = rfm69.FSK, #modulation
Datarate = 9.992, #kbit/s baudrate
Deviation = 19.042, #kHz frequency deviation
SyncPattern = [0xc6, 0x26, 0xc6, 0x26], #syncword
Bandwidth = 100, #kHz bandwidth
RssiThresh = -105, #dBm RSSI threshold
TXPower = 13, #dBm
Preamble = 2, #bytes
)
```
## connair.py ## connair.py
emulate a gateway for controlling RC socket. Compatible to "Brennenstuhl Brematic", Intertechno "ITGW-433", "ConnAir" emulate a gateway for controlling RC sockets via the app power-switch. Compatible to "Brennenstuhl Brematic", Intertechno "ITGW-433", "ConnAir"
see https://power-switch.eu/ see https://power-switch.eu/
## emoncms.py ## emoncms.py
receive lacrosse-sensors and post them to the open energy monitor, see https://openenergymonitor.org/ receive lacrosse-sensors with the RaspyRFM and post them to the open energy monitor, see https://openenergymonitor.org/
## fs20tx.py ## fs20tx.py
controlling FS20 RX sockets controlling FS20 RX sockets
@ -16,19 +44,20 @@ sudo ./fs20tx <housecode> <address> <command>
## intertechno.py ## intertechno.py
controlling remote control sockets controlling remote control sockets
```sh ```sh
intertechno <HOUSECODE A-P> <GROUP 1-4> <CHANNEL 1-4> on|off rcpulse <HOUSECODE A-P> <GROUP 1-4> <CHANNEL 1-4> on|off #control old intertechno sockets
intertechno <12 symbols tristate code> rcpulse <12 symbols tristate code> #control old intertechno sockets
intertechno <26 bit address> <1 goup bit> <4 bit unit> on|off rcpulse <26 bit address 0|1> <1 goup bit 0|1> <4 bit unit 0|1> on|off #control intertechno self learning
intertechno <32 bit code> rcpulse <32 bit code 0|1> #control intertechno and compatible (HAMA, REV)
rcpulse <5 DIP 0|1> <channel 1-4> on|off #control Brennenstuhl RC1000
usage example: usage example:
intertechno A 1 1 on ./rcpulse A 1 1 on
intertechno 0000FFFF0FFF ./rcpulse 0000FFFF0FFF
interttechno 11110000111100001111000010 0 1110 on ./rcpulse 11110000111100001111000010 0 1110 on
interttechno 11110000111100001111000010010000 ./rcpulse 11110000111100001111000010010000
``` ```
## lacrosse.py ## lacrosse.py
receiving temperature sensors receiving lacrosse temperature sensors IT29-T, IT35-T, ...
```sh ```sh
sudo ./lacrosse.py sudo ./lacrosse.py
RFM69 found on CS 1 RFM69 found on CS 1
@ -44,19 +73,5 @@ La crosse {'batlo': False, 'AFC': 308, 'init': False, 'T': (19.5, 'C'), 'RSSI':
La crosse {'batlo': False, 'AFC': 376, 'init': False, 'T': (19.7, 'C'), 'RSSI': -103, 'RH': (57, '%'), 'ID': '24'} La crosse {'batlo': False, 'AFC': 376, 'init': False, 'T': (19.7, 'C'), 'RSSI': -103, 'RH': (57, '%'), 'ID': '24'}
``` ```
## rcs1000.py
controll Brennenstuhl RC1000 RC sockets
```sh
sudo ./rcs1000.py <5xDIPSWITCH> <CHANNEL 1-4> on|off
```
## rfm69.py
interfacing the RFM69
# Installation
* [Install GPIO](http://sourceforge.net/projects/raspberry-gpio-python/)
Version has to be >= 0.5.4
* [Install SPI for Python](http://www.100randomtasks.com/simple-spi-on-raspberry-pi)
## Product ## Product
[Module RaspbyRFM Seegel Systeme](http://www.seegel-systeme.de/produkt/raspyrfm-ii/) [Module RaspbyRFM Seegel Systeme](http://www.seegel-systeme.de/produkt/raspyrfm-ii/)

1
RaspyRFM/__init__.py Normal file
View file

@ -0,0 +1 @@
from .raspyrfm import *

558
RaspyRFM/raspyrfm.py Normal file
View file

@ -0,0 +1,558 @@
import RPi.GPIO as GPIO
import spidev
import threading
import time
FXOSC = 32E6
FSTEP = FXOSC / (1<<19)
#------ Raspberry RFM Module connection -----
# RaspyRFM single module
# Connect to pins 17-26 on raspberry pi
#-------------------------------------------------#
# Raspi | Raspi | Raspi | RFM69 | RFM12 | PCB con #
# Name | GPIO | Pin | Name | Name | Pin #
#-------------------------------------------------#
# 3V3 | - | 17 | 3.3V | VDD | 1 #
# - | 24 | 18 | DIO1 | FFIT | 2 # only when PCB jumper closed
# MOSI | 10 | 19 | MOSI | SDI | 3 #
# GND | - | 20 | GND | GND | 4 #
# MISO | 9 | 21 | MISO | SDO | 5 #
# - | 25 | 22 | DIO0 | nIRQ | 6 #
# SCKL | 11 | 23 | SCK | SCK | 7 #
# CE0 | 8 | 24 | NSS | nSEL | 8 #
# CE1 | 7 | 26 | DIO2 | nFFS | 10 # only when PCB jumper closed
#-------------------------------------------------#
# RaspyRFM twin module with 10-pin connector
# Connect to pins 17-26 on raspberry pi
#-------------------------------------------------#
# Raspi | Raspi | Raspi | RFM69 | RFM12 | PCB con #
# Name | GPIO | Pin | Name | Name | Pin #
#-------------------------------------------------#
# 3V3 | - | 17 | 3.3V | VDD | 1 #
# - | 24 | 18 | DIO0_2| FFIT | 2 #
# MOSI | 10 | 19 | MOSI | SDI | 3 #
# GND | - | 20 | GND | GND | 4 #
# MISO | 9 | 21 | MISO | SDO | 5 #
# - | 25 | 22 | DIO0_1| nIRQ | 6 #
# SCKL | 11 | 23 | SCK | SCK | 7 #
# CE0 | 8 | 24 | NSS1 | nSEL | 8 #
# CE1 | 7 | 26 | NSS2 | nFFS | 10 #
#-------------------------------------------------#
# RaspyRFM twin module with 12-pin connector
# Connect to pins 15-26 on raspberry pi
#-------------------------------------------------#
# Raspi | Raspi | Raspi | RFM69 | RFM12 | PCB con #
# Name | GPIO | Pin | Name | Name | Pin #
#-------------------------------------------------#
# - | 22 | 15 | DIO2_2| | 1 #
# - | 23 | 16 | DIO2_1| | 2 #
# 3V3 | - | 17 | 3.3V | VDD | 3 #
# - | 24 | 18 | DIO0_2| FFIT | 4 #
# MOSI | 10 | 19 | MOSI | SDI | 5 #
# GND | - | 20 | GND | GND | 6 #
# MISO | 9 | 21 | MISO | SDO | 7 #
# - | 25 | 22 | DIO0_1| nIRQ | 8 #
# SCKL | 11 | 23 | SCK | SCK | 9 #
# CE0 | 8 | 24 | NSS1 | nSEL | 10 #
# CE1 | 7 | 26 | NSS2 | nFFS | 12 #
#-------------------------------------------------#
#RFM69 registers
#common registers
RegFifo = 0x00
RegOpMode = 0x01
RegDataModul = 0x02
RegBitrateMsb = 0x03
RegBitrateLsb = 0x04
RegFdevMsb = 0x05
RegFdevLsb = 0x06
RegFrfMsb = 0x07
RegFrfMid = 0x08
RegFrfLsb = 0x09
RegOsc1 = 0x0A
RegAfcCtrl = 0x0B
RegListen1 = 0x0D
RegListen2 = 0x0E
RegListen3 = 0x0F
RegVersion = 0x10
#TX registers
RegPaLevel = 0x11
RegPaRamp = 0x12
RegOcp = 0x13
#RX registers
RegLna = 0x18
RegRxBw = 0x19
RegAfcBw = 0x1A
RegOokPeak = 0x1B
RegOokAvg = 0x1C
RegOokFix = 0x1D
RegAfcFei = 0x1E
RegAfcMsb = 0x1F
RegAfcLsb = 0x20
RegFeiMsb = 0x21
RegFeiLsb = 0x22
RegRssiConfig = 0x23
RegRssiValue = 0x24
#IRQ & pin mapping registers
RegDioMapping1 = 0x25
RegDioMapping2 = 0x26
RegIrqFlags1 = 0x27
RegIrqFlags2 = 0x28
RegRssiThresh = 0x29
RegRxTimeout1 = 0x2A
RegRxTimeout2 = 0x2B
#packet engine registers
RegPreambleMsb = 0x2C
RegPreambleLsb = 0x2D
RegSyncConfig = 0x2E
RegSyncValue1 = 0x2F
RegPacketConfig1 = 0x37
RegPayloadLength = 0x38
RegNodeAdrs = 0x39
RegBroadcastAdrs = 0x3A
RegAutoModes = 0x3B
RegFifoThresh = 0x3C
RegPacketConfig2 = 0x3D
RegTemp1 = 0x4E
RegTemp2 = 0x4F
RegTestLna = 0x58
RegTestDagc = 0x6F
RegTestAfc = 0x71
InterPacketRxDelay = 4 #Bitposition
RestartRx = 2
AutoRxRestartOn = 1
AesOn = 0
#Modulation type
OOK = 1
FSK = 0
#DcFree
DcFree_None = 0
DcFree_Manchester = 1
DcFree_Whitening = 2
#RFM69 modes
MODE_SLEEP = 0
MODE_STDBY = 1
MODE_FS = 2
MODE_TX = 3
MODE_RX = 4
#DIO packet mode
DIO0_PM_CRC = 0
DIO0_PM_PAYLOAD = 1
DIO0_PM_SYNC = 2
DIO0_PM_RSSI = 3
DIO0_PM_SENT = 0
DIO0_PM_TXDONE = 1
DIO0_PM_PLLLOCK = 3
class RaspyRFM(threading.Thread):
@staticmethod
def Test(cs):
spi = spidev.SpiDev()
spi.open(0, cs)
spi.max_speed_hz = 5000
#Testing presence of module
err = False
for i in range(0, 8):
spi.xfer2([(RegSyncValue1 + i) | 0x80, 0x55])
test = spi.xfer2([(RegSyncValue1 + i), 0x00])[1]
if test != 0x55:
err = True
break
temp = spi.xfer2([(RegSyncValue1 + i) | 0x80, 0xAA])
test = spi.xfer2([(RegSyncValue1 + i), 0x00])[1]
if test != 0xAA:
err = True
break
spi.close()
return not err
def __init__(self, cs = 0, gpio_int = 25):
if not self.Test(cs):
print("ERROR! RFM69 not found")
return
self.__event = threading.Event()
self.__spi = spidev.SpiDev()
self.__spi.open(0, cs)
self.__spi.max_speed_hz=int(5E6)
self.__gpio_int = gpio_int
self.__mutex = threading.Lock()
self.__syncsize = 4
self.__fifothresh = 32
print("RFM69 found on CS " + str(cs))
GPIO.setmode(GPIO.BCM)
GPIO.setup(gpio_int, GPIO.IN)
GPIO.add_event_detect(gpio_int, GPIO.RISING, callback=self.__RfmIrq)
self.__SetMode(MODE_STDBY)
config = {}
#SET DEFAULTS
config[RegOpMode] = 0x04
config[RegDataModul] = 0x00
config[RegBitrateMsb] = 0x1A
config[RegBitrateMsb + 1] = 0x0B
config[RegFdevMsb] = 0x00
config[RegFdevMsb + 1] = 0x52
config[RegFrfMsb] = 0xE4
config[RegFrfMsb + 1] = 0xC0
config[RegFrfMsb + 2] = 0x00
config[RegOsc1] = 0x41
config[RegAfcCtrl] = 0x00
config[0x0C] = 0x02 # reserved
config[RegListen1] = 0x92
config[RegListen2] = 0xF5
config[RegListen3] = 0x20
config[RegVersion] = 0x24
config[RegPaLevel] = 0x9F
config[RegPaRamp] = 0x09
config[RegOcp] = 0x1A
config[0x17] = 0x9B # reserved
config[RegLna] = 0x88
config[RegRxBw] = 0x55
config[RegAfcBw] = 0x8B
config[RegOokPeak] = 0x40
config[RegOokAvg] = 0x80
config[RegOokFix] = 0x06
config[RegAfcFei] = 0x00
config[RegAfcMsb] = 0x00
config[RegAfcLsb] = 0x00
config[RegFeiMsb] = 0x00
config[RegFeiLsb] = 0x00
config[RegRssiConfig] = 0x02
config[RegDioMapping1] = 0x00
config[RegDioMapping2] = 0x05
config[RegIrqFlags1] = 0x80
config[RegIrqFlags2] = 0x10
config[RegRssiThresh] = 0xE4
config[RegRxTimeout1] = 0x00
config[RegRxTimeout2] = 0x00
config[RegPreambleMsb] = 0x00
config[RegPreambleLsb] = 0x00
config[RegSyncConfig] = 0x98
config[RegPacketConfig1] = 0x10
config[RegPayloadLength] = 0x40
config[RegNodeAdrs] = 0x00
config[RegBroadcastAdrs] = 0x00
config[RegAutoModes] = 0
config[RegFifoThresh] = 0x8F
config[RegPacketConfig2] = 0x02
config[RegTemp1] = 0x01
config[RegTemp2] = 0x00
config[RegTestLna] = 0x1B
config[RegTestDagc] = 0x30 #low beta 0
config[RegTestAfc] = 0x00
config[RegPacketConfig1] = 0x00 #Fixed length, CRC off, no adr
for key in config:
self.__WriteReg(key, config[key])
self.ModeStandBy()
threading.Thread.__init__(self)
print("Init complete.")
def run(self):
while True:
time.sleep(0.5)
def __RfmIrq(self, ch):
self.__event.set()
def __WriteReg(self, reg, val):
temp = self.__spi.xfer2([(reg & 0x7F) | 0x80, val & 0xFF])
def __WriteRegWord(self, reg, val):
self.__WriteReg(reg, (val >> 8) & 0xFF)
self.__WriteReg(reg + 1, val & 0xFF)
def __SetReg(self, reg, mask, val):
temp = self.ReadReg(reg) & (~mask)
temp |= val & mask
self.__WriteReg(reg, temp)
def __SetDioMapping(self, dio, mapping):
if ((dio >= 0) and (dio <=3)):
self.__SetReg(RegDioMapping1, 0xC0 >> (dio * 2), mapping << (6 - dio * 2))
elif (dio == 5):
self.__SetReg(RegDioMapping2, 0x03 << 4, mapping << 4)
def __SetMode(self, mode):
self.__WriteReg(RegOpMode, mode << 2)
self.__mode = mode
while ((self.ReadReg(RegIrqFlags1) & (1<<7)) == 0):
pass
def ReadReg(self, reg):
temp = self.__spi.xfer2([reg & 0x7F, 0x00])
return temp[1]
def ReadFifoBurst(self, len):
temp = self.__spi.xfer2([0x00] + [0x00] * len)
return temp[1:]
def WriteFifoBurst(self, data):
self.__spi.xfer2([0x80] + list(data))
def ReadRegWord(self, reg):
temp = self.__spi.xfer2([reg & 0x7F, 0x00, 0x00])
return (temp[1] << 8) | (temp[2])
def ReadRssiValue(self):
return self.ReadReg(RegRssiValue)
def ModeStandBy(self):
self.__SetMode(MODE_STDBY)
def SetParams(self, **params):
self.__mutex.acquire()
self.__event.set()
for key in params:
value = params[key]
if key == "Freq":
fword = int(round(value * 1E6 / FSTEP))
self.__WriteReg(RegFrfMsb, fword >> 16)
self.__WriteReg(RegFrfMid, fword >> 8)
self.__WriteReg(RegFrfLsb, fword)
elif key == "TXPower":
pwr = int(value + 18)
self.__WriteReg(RegPaLevel, 0x80 | (pwr & 0x1F))
elif key == "Datarate":
rate = int(round(FXOSC / (value * 1000)))
self.__WriteRegWord(RegBitrateMsb, rate)
elif key == "Deviation":
dev = int(round(value * 1000 / FSTEP))
self.__WriteRegWord(RegFdevMsb, dev)
elif key == "ModulationType":
self.__SetReg(RegDataModul, 0x18, value << 3)
elif key == "ModulationsShaping":
self.__SetReg(RegDataModul, 0x03, value)
elif key == "SyncPattern":
conf = 0
self.__syncsize = len(value)
if (len(value)) > 0:
conf = ((len(value) - 1) & 0x07) << 3
conf |= 1<<7
else:
conf = 1<<6
self.__WriteReg(RegSyncConfig, conf)
for i, d in enumerate(value):
self.__WriteReg(RegSyncValue1 + i, d)
elif key == "Bandwidth":
RxBw = FXOSC / value / 1000 / 4
e = 0
while (RxBw > 32) and (e < 7):
e += 1
RxBw /= 2
RxBw = RxBw / 4 - 4
RxBw = max(RxBw, 0)
m = int(RxBw)
self.__SetReg(RegRxBw, 0x1F, m<<3 | e)
elif key == "AfcBandwidth":
RxBw = FXOSC / value / 1000 / 4
e = 0
while (RxBw > 32) and (e < 7):
e += 1
RxBw /= 2
RxBw = RxBw / 4 - 4
RxBw = max(RxBw, 0)
m = int(RxBw)
self.__SetReg(RegAfcBw, 0x1F, m<<3 | e)
elif key == "Preamble":
self.__WriteRegWord(RegPreambleMsb, value)
elif key == "LnaGain":
self.__SetReg(RegLna, 0x07, value)
elif key == "RssiThresh":
th = -(value * 2)
self.__WriteReg(RegRssiThresh, th)
elif key == "Dagc":
self.__WriteReg(RegDagc, value)
elif key == "AfcFei":
self.__WriteReg(RegAfcFei, value)
elif key == "Callback":
self.__Callback = value
elif key == "DcFree":
self.__SetReg(RegPacketConfig1, 3<<5, value<<5)
elif key == "OokThreshType":
self.__SetReg(RegOokPeak, 3<<6, value<<6)
elif key == "OokFixedThresh":
self.__WriteReg(RegOokFix, value)
elif key == "OokPeakThreshDec":
self.__SetReg(RegOokPeak, 7<<0, value)
else:
print("Unrecognized option >>" + key + "<<")
self.ModeStandBy();
self.__mutex.release()
def __WaitInt(self):
self.__event.clear()
if GPIO.input(self.__gpio_int):
return
while not self.__event.wait(0.5):
if GPIO.input(self.__gpio_int):
break
def WhitenHope(self, data):
lfsr = 0x3fe
for i, d in enumerate(data):
data[i] = data[i] ^ ((lfsr >> 2) & 0xFF)
#roll LFSR
for j in range(8):
if ((lfsr >> 5) ^ lfsr) & 0x10 != 0:
lfsr |= 1<<0
lfsr <<= 1
lfsr &= 0x3ff
def WhitenTI(self, data):
lfsr = 0x1ff
for i, d in enumerate(data):
data[i] = data[i] ^ (lfsr & 0xFF)
for i in range(8):
if ((lfsr >> 5) ^ lfsr) & 0x01 != 0:
lfsr |= 1<<9
lfsr >>= 1
def SendPacket(self, data):
self.__mutex.acquire()
self.__event.set()
self.ModeStandBy()
#flush FIFO
status = self.ReadReg(RegIrqFlags2)
while (status & 0x40 == 0x40):
self.ReadReg(RegFifo)
status = self.ReadReg(RegIrqFlags2)
self.__WriteReg(RegPayloadLength, 0) #unlimited length
self.__WriteReg(RegFifoThresh, 0x80 | self.__fifothresh) #start TX with 1st byte in FIFO
self.__SetDioMapping(0, DIO0_PM_SENT) #DIO0 -> PacketSent
self.__SetMode(MODE_TX)
l = min(len(data), 64)
while True:
self.WriteFifoBurst(data[:l])
data = data[l:]
if len(data) == 0:
break
while True:
status = self.ReadReg(RegIrqFlags2)
if (status & (1<<5)) == 0: #below fifothresh
l = min(len(data), self.__fifothresh)
break
if (status & (1<<7)) == 0: #space for at least 1 bytearray
l = 1
break
self.__WaitInt()
self.ModeStandBy()
self.__mutex.release()
def ReadFifoWait(self, length):
ret = []
while length > 0:
flags = self.ReadReg(RegIrqFlags2)
if ((flags & (1<<5)) != 0) and (length >= 32): #FIFO level?
ret += self.ReadFifoBurst(self.__fifothresh)
length -= self.__fifothresh
if (flags & (1<<6)) != 0: #FIFO not empty?
ret.append(self.ReadReg(RegFifo))
length -= 1
return ret
def GetNoiseFloor(self):
self.__mutex.acquire()
#save values
rssithresh = self.ReadReg(RegRssiThresh)
ookthresh = self.ReadReg(RegOokFix)
sync = self.ReadReg(RegSyncConfig)
self.__WriteReg(RegRssiThresh, 240)
self.__WriteReg(RegSyncConfig, 1<<6) #no sync, always fill FIFO
self.__WriteReg(RegPayloadLength, 0) #unlimited length
self.__SetMode(MODE_RX)
thresh = 40
while True:
self.__WriteReg(RegOokFix, thresh)
for i in range(150):
b = self.ReadFifoWait()
if b != 0:
thresh += 1
break;
if i == 149:
break;
#restore registers
self.__WriteReg(RegRssiThresh, rssithresh)
self.__WriteReg(RegOokFix, ookthresh)
self.__WriteReg(RegSyncConfig, sync)
self.ModeStandBy()
self.__mutex.release()
return thresh
def __StartRx(self):
self.__SetDioMapping(2, 1) #DIO2 -> DATA
self.__mutex.acquire()
while True:
self.__WriteReg(RegPayloadLength, 0) #unlimited length
self.__WriteReg(RegFifoThresh, self.__fifothresh)
if self.__syncsize > 0:
self.__SetDioMapping(0, DIO0_PM_SYNC) #DIO0 -> SyncAddress
else:
self.__SetDioMapping(0, DIO0_PM_RSSI) #DIO0 -> RSSI
self.__SetMode(MODE_RX)
self.__mutex.release()
self.__WaitInt()
self.__mutex.acquire()
if self.__mode == MODE_RX:
break;
def StartRx(self, cb):
self.__StartRx()
cb()
self.ModeStandBy()
self.__mutex.release()
def ReceivePacket(self, length):
self.__StartRx()
result = self.ReadFifoWait(length)
rssi = -self.ReadReg(RegRssiValue) / 2
afc = self.ReadReg(RegAfcMsb) << 8
afc = afc | self.ReadReg(RegAfcLsb)
if afc >= 0x8000:
afc = afc - 0x10000
self.ModeStandBy()
self.__mutex.release()
return (result, rssi, afc)

62
apps/bistate24.py Executable file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env python2.7
import re
def Decode(pulses):
if len(pulses) != 50:
return
sym = ""
s = 0
for p in pulses:
if (p >= 200) and (p <= 450):
sym += 's'
s += p
elif (p >= 600) and (p <= 1350):
sym += 'l'
s += p
else:
sym += '?'
code = ""
for i in range(24):
if sym[:2] == 'sl':
code += "0"
elif sym[:2] == "ls":
code += "1"
else:
return
sym = sym[2:]
return ("bistate24", code, int(round(s / (24.0 * 4 + 1))))
def Encode(args):
code = ' '.join(args)
if re.match("^[01]{24}$", code):
data = []
d = 0
pos = 4
for c in code:
d |= (0x8 if c == '0' else 0xe) << pos
if pos == 0:
data.append(d)
d = 0
pos = 4
else:
pos = 0
data += [0x80, 0x00, 0x00, 0x00]
return (data, 5, 330)
elif re.match("^([01]{20}) ([A])$", code):
g = re.match("^([01]{20}) ([ABCD])$", code).groups()
bits = g[0]
if g[1] == 'A':
bits += '0001'
elif g[1] == 'B':
bits += '0010'
elif g[1] == 'C':
bits += '0100'
elif g[1] == 'D':
bits += '1000'
return Encode([bits])

63
apps/connair.py Executable file
View file

@ -0,0 +1,63 @@
#!/usr/bin/env python2.7
import socket
import RaspyRFM
import sys
UDP_IP = "0.0.0.0"
UDP_PORT = 49880
HELLO_MESSAGE = "HCGW:VC:Seegel Systeme;MC:RaspyRFM;FW:1.00;IP:0.0.0.0;;"
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
rfm = RaspyRFM.RaspyRFM()
rfm.SetParams(
Freq = 433.92,
TXPower = 13,
ModulationType = rfm69.OOK,
SyncPattern = [],
)
print("Listening...")
while True:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print("received message from " + addr[0] + ': ' + str(data))
print(sock)
msg = str(data).split(":")
if msg[0] == "SEARCH HCGW":
print("Hello message")
sock.sendto(HELLO_MESSAGE, addr)
if msg[0] == "TXP":
msg[1] = msg[1].replace(";", "")
cmd = msg[1].split(",")
rep = int(cmd[2])
pauselen = int(cmd[3])
steplen = int(cmd[4])
numpulse = int(cmd[5])
pulsedata = cmd[6:]
pulsedata[numpulse * 2 - 1] = int(pulsedata[numpulse * 2 - 1]) + pauselen / steplen
bindata = []
bit = True
numbit = 0
bitleft = 0
for i in range(numpulse * 2):
for bits in range(int(pulsedata[i])):
if bitleft == 0:
bitleft = 8
bindata.append(0x00)
bindata[len(bindata) - 1] <<= 1
if bit:
bindata[len(bindata) - 1] |= 0x01
bitleft -= 1
bit = not bit
for i in range(bitleft):
bindata[len(bindata) - 1] <<= 1
rfm.SetParams(Datarate = 1000.0 / steplen)
rfm.SendPacket(bindata * rep)

48
apps/it32.py Normal file
View file

@ -0,0 +1,48 @@
import re
def Decode(pulses):
if len(pulses) != 130:
return
sym = ""
s = 0
for p in pulses:
if (p >= 150) and (p <= 500):
s += p
sym += 's'
elif (p >= 1000) and (p <= 1500):
s += p
sym += 'l'
else:
sym += '?'
it = ""
temp = sym
for i in range(32):
if temp[:4] == 'sssl':
it += "0"
elif temp[:4] == "slss":
it += "1"
else:
return
temp = temp[4:]
return("it32", it, int(round(s / (32.0 * 8 + 1))))
def Encode(args):
code = ' '.join(args)
if re.match("^[01]{32}$", code):
data = [0x08, 0x00] #sync
for c in args[0]:
if c == '0':
data.append(0xA0)
elif c == '1':
data.append(0x82)
data += [0x80, 0x00, 0x00, 0x00, 0x00]
return (data, 6, 275)
elif re.match("^[01]{26} ([0][1-9]|[1-9]|[1][0-6]) (on|off)$", code):
g = re.match("^([01]{26}) ([0][1-9]|[1-9]|[1][0-6]) (on|off)$", code).groups()
bits = g[0] + '0'
bits += '1' if g[2] == 'on' else '0'
bits += "{0:04b}".format(int(g[1]) - 1)
return Encode([bits])

View file

@ -1,19 +1,18 @@
#!/usr/bin/env python2.7 #!/usr/bin/env python2.7
from rfm69 import Rfm69 import RaspyRFM
import rfm69
import sensors import sensors
from sensors import rawsensor from sensors import rawsensor
import sys import sys
import time import time
import threading import threading
if Rfm69.Test(1): if RaspyRFM.RaspyRFM.Test(1):
print("Found RaspyRFM twin") print("Found RaspyRFM twin")
rfm = Rfm69(1, 24) #when using the RaspyRFM twin rfm = RaspyRFM.RaspyRFM(1, 24) #when using the RaspyRFM twin
elif Rfm69.Test(0): elif RaspyRFM.RaspyRFM.Test(0):
print("Found RaspyRFM single") print("Found RaspyRFM single")
rfm = Rfm69() #when using a single single 868 MHz RaspyRFM rfm = RaspyRFM.RaspyRFM() #when using a single single 868 MHz RaspyRFM
else: else:
print("No RFM69 module found!") print("No RFM69 module found!")
exit() exit()
@ -21,7 +20,7 @@ else:
rfm.SetParams( rfm.SetParams(
Freq = 868.30, #MHz center frequency Freq = 868.30, #MHz center frequency
Datarate = 9.579, #kbit/s baudrate Datarate = 9.579, #kbit/s baudrate
ModulationType = rfm69.FSK, #modulation ModulationType = RaspyRFM.FSK, #modulation
Deviation = 30, #kHz frequency deviation Deviation = 30, #kHz frequency deviation
SyncPattern = [0x2d, 0xd4], #syncword SyncPattern = [0x2d, 0xd4], #syncword
Bandwidth = 150, #kHz bandwidth Bandwidth = 150, #kHz bandwidth

106
apps/rcpulse.py Executable file
View file

@ -0,0 +1,106 @@
#!/usr/bin/env python2.7
import RaspyRFM
import sys
import time
import it32
import tristate
import bistate24
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-t", "--timebase", type=int, help=u"timebase in \u03bcs")
parser.add_argument("-r", "--repeats", type=int, help=u"number of repetitions")
parser.add_argument("code", nargs='*', help="code, e. g. '000000000FFF', 'A 1 2 on' or '10111100011101011111111110001110'")
args = parser.parse_args()
protos = [
it32,
tristate,
bistate24,
]
txdata = None
if len(args.code) > 0:
txdata = None
for proto in protos:
data = proto.Encode(args.code)
if data:
txdata = data
break
if txdata is None:
print("invalid code!")
exit()
rfm = RaspyRFM.RaspyRFM()
rfm.SetParams(
Freq = 433.92, #MHz
Datarate = 20.0, #kbit/s
Bandwidth = 200, #kHz
SyncPattern = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F],
RssiThresh = -105, #dBm
ModulationType = RaspyRFM.OOK,
OokThreshType = 1, #peak thresh
OokPeakThreshDec = 3,
)
if txdata:
rfm.SetParams(
SyncPattern = [],
Datarate = 1000.0 / (args.timebase if args.timebase else txdata[2])
)
rep = (args.repeats if args.repeats else txdata[1])
rfm.SendPacket(txdata[0] * rep)
print("Code sent!")
exit()
def Decode(pulses):
for i in range(len(pulses)):
pulses[i] *= 50
dec = None
for proto in protos:
dec = proto.Decode(pulses)
if dec:
print(dec)
if not dec:
print("Len " + str(len(pulses)) + ": " + str(pulses))
while True:
data = rfm.ReceivePacket(260)
s = ""
pulsecount = 7
glitchcount = 0
bit = True
pulses = []
for d in data[0]:
s += format(d, '08b')
mask = 0x80
while mask > 0:
newbit = (d & mask) > 0
if glitchcount > 0:
glitchcount += 1
if newbit == bit:
pulsecount += glitchcount
glitchcount = 0
else:
if glitchcount == 3:
pulses.append(pulsecount)
if pulsecount > 50:
Decode(pulses)
pulses = []
bit = newbit
pulsecount = 3
glitchcount = 0
else:
if newbit == bit:
pulsecount += 1
else:
glitchcount = 1
mask >>= 1

91
apps/tristate.py Executable file
View file

@ -0,0 +1,91 @@
#!/usr/bin/env python2.7
import re
def Decode(pulses):
if len(pulses) != 50:
return
sym = ""
s = 0
for p in pulses:
if (p >= 200) and (p <= 450):
sym += 's'
s += p
elif (p >= 600) and (p <= 1350):
sym += 'l'
s += p
else:
sym += '?'
code = ""
temp = sym
for i in range(12):
if temp[:4] == 'slsl':
code += "0"
elif temp[:4] == "lsls":
code += "1"
elif temp[:4] == "slls":
code += "f"
else:
return
temp = temp[4:]
return ("tristate", code, int(round(s / (12.0 * 8 + 1))))
def encodeBits(val, num):
result = ''
for i in range(num):
if (val & 0x01):
result += 'F'
else:
result += '0'
val >>= 1
return result
def Encode(args):
code = ' '.join(args)
if re.match("^[01Ff]{12}$", code):
data = []
for c in code:
if c == '0':
data.append(0x88)
elif c == '1':
data.append(0xEE)
elif c in ['F', 'f']:
data.append(0x8E)
data += [0x80, 0x00, 0x00, 0x00] #sync
return (data, 5, 360)
elif re.match('^[A-P] [1-4] [1-4] (on|off)$', code):
g = re.match('^([A-P]) ([1-4]) ([1-4]) (on|off)$', code).groups()
tristate = ""
tristate += encodeBits(ord(g[0]) - ord('A'), 4) #housecode
tristate += encodeBits(ord(g[2]) - 1, 2) #channel
tristate += encodeBits(ord(g[1]) - 1, 2) #group
tristate += "0F"
tristate += 'FF' if g[3] == 'on' else 'F0'
return Encode([tristate])
elif re.match('^([01]{5}) ([1-4]) (on|off)$', code): #Brennenstuhl
g = re.match('^([01]{5}) ([1-4]) (on|off)$', code).groups()
tristate = ""
for c in g[0]:
tristate += '0' if c == '1' else 'F'
for i in range(4):
tristate += '0' if int(g[1]) - 1 == i else 'F'
tristate += 'F'
tristate += '0F' if g[2] == 'on' else 'F0'
return Encode([tristate])
elif re.match('^[1-4] [1-4] (on|off)$', code):
g = re.match('^([1-4]) ([1-4]) (on|off)$', code).groups()
tristate = ""
for i in range(4):
tristate += "0" if int(g[0]) - 1 == i else "F"
for i in range(4):
tristate += "0" if int(g[1]) - 1 == i else "F"
tristate += "FFF"
tristate += 'F' if g[2] == 'on' else '0'
return Encode([tristate])

View file

@ -67,7 +67,7 @@ class Connection(threading.Thread):
lines = str.splitlines(data) lines = str.splitlines(data)
for line in lines: for line in lines:
#print("FROM FHEM: " + line) print("FROM FHEM: " + line)
if line == 'V': if line == 'V':
self.sendHost("V 1.0 CULEMU") self.sendHost("V 1.0 CULEMU")
@ -146,12 +146,9 @@ cul.start()
class RxThread(threading.Thread): class RxThread(threading.Thread):
def __init__(self, rfm): def __init__(self, rfm):
self.__rfm = rfm self.__rfm = rfm
rfm.SetParams(
CallbackSync = self.__callback
)
threading.Thread.__init__(self) threading.Thread.__init__(self)
def __callback(self): def callback(self):
lfsr = 0x1ff lfsr = 0x1ff
frame = rfm.ReadFifoWait(1) frame = rfm.ReadFifoWait(1)
len = frame[0] ^ 0xFF #invert due to whitening len = frame[0] ^ 0xFF #invert due to whitening
@ -192,4 +189,4 @@ rxthread.daemon = True
rxthread.start() rxthread.start()
while True: while True:
data = rfm.ReceivePacket(0) data = rfm.StartRx(rxthread.callback)

View file

@ -1,77 +0,0 @@
#!/usr/bin/env python2.7
from rfm69 import Rfm69
import rfm69
import sys
import time
#import types
#import os
rfm = Rfm69()
rfm.SetParams(
Freq = 433.944,
Datarate = 4.0, #1 / 250E-06 / 1000,
Bandwidth = 24000,
SyncPattern = [0x00, 0x08, 0x00],
RssiThresh = -80,
ModulationType = rfm69.OOK
)
def staff(byte):
res = 0
res |= (byte & 1<<7) >> 4
res |= (byte & 1<<5) >> 3
res |= (byte & 1<<3) >> 2
res |= (byte & 1<<1) >> 1
return res
def decode(bindata):
netdata = [0x00, 0x00, 0x00, 0x00]
for i in range(0, 64, 2):
if (bindata[i / 8] >> (i % 8)) & 0x01 == (bindata[i / 8] >> (i % 8 + 1)) & 0x01:
print "Error", i, hex(bindata[i / 8]), hex(bindata[i / 8] >> (i % 8))
for i in range(4):
netdata[i] = staff(bindata[i * 2]) << 4 | staff(bindata[i * 2 + 1])
print "decode: ",
for i in range(4):
print "{0:{fill}2x}".format(netdata[i], fill='0'),
print ""
while True:
data = rfm.ReceivePacket(60)
zcount = 0
bindata = []
binval = 0
binmask = 0x80
for d in data[0]:
rawmask = 0x80
while (rawmask > 0) and (len(bindata) < 8):
if (d & rawmask) > 0:
if zcount == 1:
binval |= binmask
binmask >>= 1
if zcount == 5:
binmask >>= 1
if zcount == 11:
print "Received pause"
if zcount == 41:
print "SYNC"
zcount = 0
else:
zcount += 1
rawmask >>= 1
if binmask == 0:
bindata.append(binval)
binmask = 0x80
binval = 0
if len(bindata) == 8:
decode(bindata)
break;

View file

@ -1,91 +0,0 @@
#!/usr/bin/env python2.7
import rfm69
from rfm69 import Rfm69
import xx2262
import it32
import sys
import re
def encodeBits(val, num, pol, inv):
result = ''
for i in range(num):
if (val & 0x01) ^ inv:
result += 'F'
else:
result += pol
val >>= 1
return result
def usage():
print "usage:"
print "intertechno <HOUSECODE A-P> <GROUP 1-4> <CHANNEL 1-4> on|off" #12-digit code 12 * ['0' | '1' | 'f']
print "intertechno <12 symbols tristate code>"
print "intertechno <26 bit address> <1 goup bit> <4 bit unit> on|off"
print "intertechno <32 bit code>"
print "Examples:"
print "intertechno A 1 1 on"
print "intertechno 0000FFFF0FFF"
print "intertechno 11110000111100001111000010 0 1110 on"
print "intertechno 11110000111100001111000010010000"
sys.exit(1)
if __name__ == "__main__":
import sys
cmd = ""
data = None
for i in range(1, len(sys.argv)):
cmd += " " + sys.argv[i]
cmd = cmd.strip()
if re.match('^[01]{32}$', cmd) is not None:
data = it32.MakeFrame(cmd, 5)
datarate = 4
if re.match('^[01]{26} [01] [01]{4} (on|off)$', cmd) is not None:
tmp = cmd[0:26] + cmd[27]
if cmd[-2:] == 'on':
tmp += '1'
else:
tmp += '0'
tmp += cmd[29:33]
data = it32.MakeFrame(tmp, 5)
datarate = 1/275E-6/1000
if re.match('^[A-P] [1-4] [1-4] (on|off)$', cmd) is not None:
housecode = ord(cmd[0]) - ord('A')
itstr = ''
itstr += encodeBits(housecode, 4, '0', False)
ch = ord(cmd[4]) - 1
itstr += encodeBits(ch, 2, '0', False)
group = ord(cmd[2]) - 1
itstr += encodeBits(group, 2, '0', False)
itstr += '0F'
if cmd[-2:] == 'on':
itstr += 'FF'
else:
itstr += 'F0'
data = xx2262.MakeFrame(itstr, 5)
datarate = 2.66666666
if re.match('^[01Ff]{12}$', cmd) is not None:
data = xx2262.MakeFrame(cmd, 5)
datarate = 2.66666666
if data is not None:
rfm = Rfm69()
rfm.SetParams(
Freq = 433.92,
Datarate = datarate, #2.666666,
TXPower = 13,
ModulationType = rfm69.OOK,
SyncPattern = []
)
rfm.SendPacket(data)
else:
usage()

10
it32.py
View file

@ -1,10 +0,0 @@
def MakeFrame(code, rep):
data = [0x08, 0x00] #sync
for c in code:
if c == '0':
data.append(0xA0)
elif c == '1':
data.append(0x82)
data += [0x80, 0x00, 0x00, 0x00, 0x00]
return data * rep

View file

@ -1,53 +0,0 @@
#!/usr/bin/env python2.7
from rfm69 import Rfm69
import rfm69
import sys
import time
#import types
#import os
rfm = Rfm69()
rfm.SetParams(
Freq = 433.92,
Datarate = 1 / 275E-06 / 1000,
Bandwidth = 4000,
SyncPattern = [0x80, 0x00, 0x00, 0x00],
RssiThresh = -80,
ModulationType = rfm69.OOK
)
def Decode(bitpos, data):
frame = 0 #space for decoded logilink frame
for i in range(bitpos, bitpos + 24 * 4, 4):
bitpattern = (data[i / 8] << 8) | (data[i / 8 + 1])
bitpattern <<= i % 8
bitpattern &= 0xF000
frame <<= 1
if bitpattern == 0xe000:
frame |= 1
elif bitpattern == 0x8000:
pass
else:
return
systemcode = frame >> 4
onoff = (frame >> 3) & 0x01
ch = frame & 0x07
return systemcode, onoff, ch
while True:
data = rfm.ReceivePacket(60)
zcount = 0
binstr = ""
bitcount = 0
#print "received raw data:", data[0]
sync = 0
for bit in range(len(data[0]) * 8):
sync <<= 1
sync |= ((data[0][bit / 8] >> (7 - (bit % 8)))) & 0x01
sync &= 0xFFFFFFFF
if sync == 0x80000000: #sync found in frame
if (bit >= 24 * 4 + 32 - 1): #logilinkframe has 24 bit, 1 logilink-bit = 4 raw-bits; + 32 raw bits sync
res = Decode(bit - 24 * 4 - 32 + 1, data[0])
if res is not None:
print "Systemcode", res[0], "onoff", res[1], "ch", res[2]

View file

@ -1,37 +0,0 @@
#!/usr/bin/env python2.7
from rfm69 import Rfm69
import rfm69
import sys
if len(sys.argv) != 5:
print "usage: logiloghttx.py <systemcode> <channel> <on/off> <repetitions>"
print "Example: logilighttx.py 65565 7 1 4"
sys.exit(1)
rfm = Rfm69()
rfm.SetParams(
Freq = 433.92,
Datarate = 2.666666,
TXPower = 13,
ModulationType = rfm69.OOK,
SyncPattern = []
)
#Frame generation
def MakeFrame(systemcode, onoff, channel, rep):
data = systemcode << 4 | onoff << 3 | channel
frame = [0x00] * 12
for i in range(24):
if (data & (0x800000>>i)):
nibble = 0xE0
else:
nibble = 0x80
frame[i / 2] |= nibble >> (4 * (i % 2))
frame += [0x80, 0x00, 0x00, 0x00] #sync
return frame * rep
data = MakeFrame(int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3]), int(sys.argv[4]))
rfm.SendPacket(data)

View file

@ -1,63 +0,0 @@
#!/usr/bin/env python2.7
import xx2262
import sys
import re
import rfm69
from rfm69 import Rfm69
def usage():
print "usage:", sys.argv[0], "<5xDIPSWITCH> <CHANNEL 1-4> on|off"
print "Example:", sys.argv[0], "brennenstuhl 00101 2 on"
sys.exit(1)
if len(sys.argv) != 4:
usage()
str = ''
if re.match('[01]{5}$', sys.argv[1]) is None:
print "Invalid dipswitches"
usage()
for c in sys.argv[1]:
if c == '1':
str += '0'
else:
str += 'F'
if re.match('[1-4]$', sys.argv[2]) is None:
print "Invalid channel"
usage()
ch = ord(sys.argv[2][0]) - ord('1')
for i in range(4):
if i == ch:
str += '0'
else:
str += 'F'
str += 'FF'
if sys.argv[3] == 'on':
str += 'F'
else:
str += '0'
print "Sending", str
data = xx2262.MakeFrame(str, 8)
if not Rfm69.Test(0):
print "RFM69 not found!"
exit()
rfm = Rfm69()
rfm.SetParams(
Freq = 433.92,
Datarate = 2.666666,
TXPower = 13,
ModulationType = rfm69.OOK,
SyncPattern = []
)
rfm.SendPacket(data)

18
setup.py Executable file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env python2.7
import setuptools
setuptools.setup(
name="RaspyRFM",
version="1.1",
author="S. Seegel",
author_email="post@seegel-systeme.de",
description="Package for interfacing the RaspyRFM module",
packages=["RaspyRFM"],
license="MIT",
zip_safe=False,
install_requires=[
"spidev",
"RPi.GPIO"
]
)

View file

@ -1,48 +0,0 @@
#!/usr/bin/env python2.7
from rfm69 import Rfm69
import rfm69
import re
#Frame generation
def MakeFrame(code, rep):
data = [0x80, 0x00, 0x00, 0x00] #sync
b = 0;
data = []
for c in code:
if c == '0':
data.append(0x88)
elif c == '1':
data.append(0xEE)
elif c == 'F' or c == 'f':
data.append(0x8E)
data += [0x80, 0x00, 0x00, 0x00] #sync
return data * rep
def usage():
print "usage: xx2262.py <CODE>" #12-digit code 12 * ['0' | '1' | 'f']
print "Example: sudo ./intertechno.py 0FF0F0F00FF0"
sys.exit(1)
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
usage()
if re.match('[01fF]{12}$', sys.argv[1]) is None:
print "Invalid code"
usage()
data = MakeFrame(sys.argv[1], 5)
rfm = Rfm69()
rfm.SetParams(
Freq = 433.92,
Datarate = 2.666666,
TXPower = 13,
ModulationType = rfm69.OOK,
SyncPattern = []
)
rfm.SendPacket(data)