refactor protocols

This commit is contained in:
S. Seegel 2020-11-30 19:25:11 +01:00
parent fb3c4e7b9f
commit 5aa901b68b
5 changed files with 240 additions and 366 deletions

View file

@ -2,6 +2,7 @@ import re
from argparse import ArgumentParser
from raspyrfm import *
import threading
import time
class RcProtocol:
def __init__(self):
@ -10,6 +11,8 @@ class RcProtocol:
self.__bbuf = 0
self.__bval = 0
self._parser = ArgumentParser()
self._lastdecode = None
self._lastdecodetime = 0
sympulses = []
for i in self._symbols:
@ -88,12 +91,18 @@ class RcProtocol:
break
if not match:
return None, None
return None, None, None
print("code: ", dec)
if re.match("^" + self._pattern + "$", dec):
return dec, int(1.0 * sumpulse / sumstep)
rep = True
if (self._lastdecode != dec) or (time.time() - self._lastdecodetime > 0.5):
self._lastdecode = dec
rep = False
self._lastdecodetime = time.time()
return dec, int(1.0 * sumpulse / sumstep), rep
return None, None
return None, None, None
def decode(self, pulsetrain):
pass
@ -123,7 +132,7 @@ class IT32(RcProtocol): #switch1
self._add_symbols(code)
self._add_pulses([1, 39])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, self._repetitions
def encode(self, args):
if hasattr(args, "code") and args.code:
@ -138,7 +147,7 @@ class IT32(RcProtocol): #switch1
return self.__encode(code)
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[0:-2])
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
if code:
return {
"protocol": self._name,
@ -148,7 +157,7 @@ class IT32(RcProtocol): #switch1
"group": int(code[26:27], 2),
"state": int(code[27:28], 2),
"unit": int(code[28:32], 2) + 1,
}
}, rep
class ITTristate(RcProtocol): #old intertechno systems
def __init__(self):
@ -170,20 +179,20 @@ class ITTristate(RcProtocol): #old intertechno systems
self._parser.add_argument("-s", "--state", type=int)
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[0:-2])
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
if code:
return {
"protocol": self._name,
"code": code,
"timebase": tb,
}
}, rep
def __encode(self, code):
self._reset()
self._add_symbols(code)
self._add_pulses([1, 31])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, self._repetitions
def __encode_int(self, ival, digits):
code = ""
@ -222,20 +231,20 @@ class Bistate24(RcProtocol):
self._parser.add_argument("-c", "--code", required=True)
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[0:-2])
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
if code:
return {
"protocol": self._name,
"code": code,
"timebase": tb,
}
}, rep
def encode(self, args):
self._reset()
self._add_symbols(args.code)
self._add_pulses([1, 31])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, self._repetitions
class Switch15(Bistate24): #e. g. logilight
def __init__(self):
@ -249,7 +258,7 @@ class Switch15(Bistate24): #e. g. logilight
self._parser.add_argument("-s", "--state", type=int, required=True)
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[0:-2])
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
if code:
state = int(code[20:21])
unit = int(code[21:24], 2)
@ -273,7 +282,7 @@ class Switch15(Bistate24): #e. g. logilight
"id": int(code[0:20], 2),
"unit": unit,
"state": state,
}
}, rep
def encode(self, args):
self._reset()
@ -294,7 +303,7 @@ class Switch15(Bistate24): #e. g. logilight
self._add_symbols(sym)
self._add_pulses([1, 31])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, 10 if args.state == 2 else self._repetitions
class Emylo(Bistate24):
def __init__(self):
@ -307,7 +316,7 @@ class Emylo(Bistate24):
self._parser.add_argument("-k", "--key", required=True)
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[0:-2])
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
if code:
key = int(code[20:24], 2)
if key == 1:
@ -326,7 +335,7 @@ class Emylo(Bistate24):
"timebase": tb,
"id": int(code[0:20], 2),
"key": key,
}
}, rep
def encode(self, args):
self._reset()
@ -345,7 +354,7 @@ class Emylo(Bistate24):
self._add_symbols(sym)
self._add_pulses([1, 31])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, self._repetitions
class FS20(RcProtocol):
def __init__(self):
@ -381,10 +390,10 @@ class FS20(RcProtocol):
self.__encode_byte(q & 0xFF)
self._add_pulses([1, 100])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, self._repetitions
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[0:-2])
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
if code:
return {
"protocol": self._name,
@ -393,7 +402,7 @@ class FS20(RcProtocol):
"housecode": int(code[13:21] + code[22:30] , 2),
"address": int(code[31:39], 2),
"cmd": int(code[40:48], 2),
}
}, rep
class Voltcraft(RcProtocol):
def __init__(self):
@ -411,7 +420,7 @@ class Voltcraft(RcProtocol):
self._parser.add_argument("-s", "--state", type=int, required=True)
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[1:-1])
code, tb, rep = self._decode_symbols(pulsetrain[1:-1])
if code:
return {
"protocol": self._name,
@ -420,7 +429,7 @@ class Voltcraft(RcProtocol):
"id": int(code[0:12][::-1], 2),
"unit": int(code[12:14][::-1], 2) + 1,
"state": int(code[14:17][::-1], 2) #0=off, 1=alloff, 2=on, 3=allon, 5=bright, 7=dim
}
}, rep
def encode(self, args):
if not (args.state in [0, 2]):
@ -438,7 +447,7 @@ class Voltcraft(RcProtocol):
self._add_symbols(symbols)
self._add_pulses([132])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, self._repetitions
class PDM32(RcProtocol):
def __init__(self):
@ -454,20 +463,20 @@ class PDM32(RcProtocol):
self._parser.add_argument("-c", "--code")
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[0:-2])
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
if code:
return {
"protocol": self._name,
"code": code,
"timebase": tb,
}
}, rep
def encode(self, args):
self._reset()
self._add_symbols(args.code)
self._add_pulses([1, 11])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, self._repetitions
class PCPIR(RcProtocol): #pilota casa PIR sensor
def __init__(self):
@ -483,68 +492,83 @@ class PCPIR(RcProtocol): #pilota casa PIR sensor
self._parser.add_argument("-c", "--code", required=True)
def decode(self, pulsetrain):
code, tb = self._decode_symbols(pulsetrain[0:-2])
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
if code:
return {
"protocol": self._name,
"code": code,
"timebase": tb,
}
}, rep
def encode(self, args):
self._reset()
self._add_symbols(args.code)
self._add_pulses([1, 12])
self._add_finish()
return self._ookdata
return self._ookdata, self._timebase, self._repetitions
class PDM1(RcProtocol):
'''
PDM1: Pulse Distance Modulation
Every bit consists of 2 shortpulses. Long distance between these pulses 2 pulses -> 1, else -> 0
Frame: header, payload, footer
Used by Intertechno self learning, Hama, ...
'''
def __init__(self):
self._name = "pdm1"
self._timebase = 275
self._repetitions = 4
self._pattern = "[01]*"
self._symbols = {
'0': [1, 1, 1, 5],
'1': [1, 5, 1, 1],
}
self._header = [1, 11]
self._footer = [1, 39]
#self._values = [
# ("c", "code")
#]
RcProtocol.__init__(self)
self._parser.add_argument("-c", "--code", required=True)
def decode(self, pulsetrain):
print("decoding pwm 1")
code, tb, rep = self._decode_symbols(pulsetrain[0:-2])
print(code)
if code:
return {
"protocol": self._name,
"code": code,
"timebase": tb,
}, rep
def encode(self, args):
self._reset()
self._add_symbols(args.code)
self._add_pulses([1, 31])
self._add_finish()
return self._ookdata, self._timebase, self._repetitions
protocols = [
IT32(),
Switch15(),
ITTristate(),
Voltcraft(),
FS20(),
Emylo(),
PDM32(),
Bistate24(),
PCPIR(),
#IT32(),
#Switch15(),
#ITTristate(),
#Voltcraft(),
#FS20(),
#Emylo(),
#PDM32(),
#Bistate24(),
#PCPIR(),
PDM1(),
]
def encode(protocol, args):
for p in protocols:
if (protocol):
if p._name == protocol:
return (p.encode(p._parser.parse_args(args)), p._timebase, p._repetitions)
class RCStruct:
def __init__(self, **entries):
self.__dict__.update(entries)
def encode_dict(dict):
s = RCStruct(**dict)
for p in protocols:
if p._name == s.protocol:
return (p.encode(s), p._timebase, p._repetitions)
def decode(pulsetrain):
dec = None
res = []
for p in protocols:
dec = p.decode(pulsetrain)
if dec:
res.append(dec)
if len(res) > 0:
return res
class PulseTRX(threading.Thread):
def __init__(self, module):
self.__trainbuf = []
RXDATARATE = 20.0 #kbit/s
class RfmPulseTRX(threading.Thread):
def __init__(self, module, rxcb, frequency):
self.__rfm = RaspyRFM(module, RFM69)
self.__rfm.set_params(
Freq = 433.92, #MHz
Datarate = 20.0, #kbit/s
Bandwidth = 300, #kHz
Freq = frequency, #MHz
Bandwidth = 200, #kHz
SyncPattern = [],
RssiThresh = -105, #dBm
ModulationType = rfm69.OOK,
@ -553,58 +577,148 @@ class PulseTRX(threading.Thread):
Preamble = 0,
TxPower = 13
)
self.__rxtraincb = rxcb
self.__event = threading.Event()
self.__event.set()
threading.Thread.__init__(self)
self.daemon = True
self.start()
def getEvents(self):
if len(self.__trainbuf) > 0:
train = self.__trainbuf.pop()
for i, v in enumerate(train):
train[i] = 50 * v
return decode(train)
def run(self):
while True:
self.__event.wait()
rfm.set_params(
Datarate = 20.0 #kbit/s
self.__rfm.set_params(
Datarate = RXDATARATE #kbit/s
)
rfm.start_receive(self.__rxcb)
self.__rfm.start_receive(self.__rxcb)
def __cb(self, rfm):
def __rxcb(self, rfm):
bit = False
cnt = 1
train = []
while True:
bitfifo = 0
while self.__event.isSet():
fifo = rfm.read_fifo_wait(64)
ba = bytearray()
for b in fifo:
mask = 0x80
while mask != 0:
if (b & mask) != 0:
ba.append(245)
else:
ba.append(10)
#bitfifo <<= 1
#if (b & mask != 0):
# bitfifo |= 1
#v = bitfifo & 0x3
#c = 0
#while v > 0:
# v &= v - 1
# c += 1
newbit = (b & mask) != 0
if ((b & mask) != 0) == bit:
if newbit == bit:
cnt += 1
else:
if cnt < 3: #<150 us
if cnt < 150*RXDATARATE/1000: #<150 us
train *= 0 #clear
elif cnt > 50:
elif cnt > 3000*RXDATARATE/1000:
if not bit:
train.append(cnt)
if len(train) > 20:
self.__trainbuf.append(list(train))
self.__rxtraincb(train)
train *= 0 #clear
elif len(train) > 0 or bit:
train.append(cnt)
cnt = 1
bit = not bit
mask >>= 1
def send(self, train, timebase, repetitions):
self.__event.clear()
self.__rfm.set_params(
Datarate = 1000.0 / timebase
)
self.__event.set()
self.__rfm.send(train * repetitions)
class RCStruct:
def __init__(self, **entries):
self.__dict__.update(entries)
class RcTransceiver(threading.Thread):
def __init__(self, module, frequency, rxcallback):
threading.Thread.__init__(self)
self.__lock = threading.Lock()
self.__event = threading.Event()
self.__trainbuf = []
self.daemon = True
self.start()
self.__rxcb = rxcallback
self.__rfmtrx = RfmPulseTRX(module, self.__pushPulseTrain, frequency)
def __del__(self):
del self.__rfmtrx
def __pushPulseTrain(self, train):
self.__lock.acquire()
self.__trainbuf.append(list(train))
self.__event.set()
self.__lock.release()
def __decode(self, train):
dec = None
res = []
succ = False
for p in protocols:
dec = p.decode(train)
if dec:
succ = True
if not dec[1]: #repeated?
res.append(dec[0])
return res, succ
def send_dict(self, dict):
s = RCStruct(**dict)
for p in protocols:
if p._name == s.protocol:
try:
txdata, tb, rep = p.encode(s)
if hasattr(s, "timebase"):
tb = s.timebase
if hasattr(s, "repeats"):
rep = s.repeats
print("repeats", rep)
self.__rfmtrx.send(txdata, tb, rep)
except:
print("Error in args!")
break
def send_args(self, protocol, args, timebase=None, repeats=None):
for p in protocols:
if p._name == protocol:
try:
txdata, tb, rep = p.encode(p._parser.parse_args(args))
if timebase:
tb = timebase
if repeats:
rep = repeats
self.__rfmtrx.send(txdata, tb, rep)
except:
print("Error in args!")
break
def run(self):
while True:
self.__event.wait()
self.__lock.acquire()
train = None
if len(self.__trainbuf) > 0:
train = self.__trainbuf.pop()
else:
self.__event.clear()
self.__lock.release()
if (train != None):
for i, v in enumerate(train):
train[i] = int(v * 1000 / RXDATARATE) #convert to microseconds
dec = self.__decode(train)
#dec[0]: array of decoded protocols
#dec[1]: true if at least 1 protocol was decoded
if (not dec[1]) or (len(dec[0]) > 0):
self.__rxcb(dec[0], train)

View file

@ -3,7 +3,6 @@
from raspyrfm import *
import sys
import time
#import fs20
from argparse import ArgumentParser
import wave, struct
import threading
@ -18,137 +17,29 @@ parser.add_argument("-p", "--protocol", help=u"Protocol for sending")
parser.add_argument("-w", "--write", help=u"write wavefile")
args, remainargs = parser.parse_known_args()
txdata = None
if len(remainargs) > 0:
txdata = rcprotocols.encode(args.protocol, remainargs)
if txdata is None:
print("invalid code!")
exit()
def rxcb(dec, train):
for p in dec:
print(p)
if len(dec) == 0:
print(train)
if not raspyrfm_test(args.module, RFM69):
print("Error! RaspyRFM not found")
exit()
rfm = RaspyRFM(args.module, RFM69)
rfm.set_params(
Freq = args.frequency, #MHz
Datarate = 20.0, #kbit/s
Bandwidth = 300, #kHz
SyncPattern = [],
RssiThresh = -105, #dBm
ModulationType = rfm69.OOK,
OokThreshType = 1, #peak thresh
OokPeakThreshDec = 3,
Preamble = 0,
TxPower = 13
)
rctrx = rcprotocols.RcTransceiver(args.module, args.frequency, rxcb)
if txdata:
rfm.set_params(
SyncPattern = [],
Datarate = 1000.0 / (args.timebase if args.timebase else txdata[1])
)
rep = (args.repeats if args.repeats else txdata[2])
rfm.send(txdata[0] * rep)
print("Code sent!")
if len(remainargs) > 0:
rctrx.send_args(args.protocol, remainargs, args.timebase, args.repeats)
del rctrx
exit()
trainbuf = []
class RxThread(threading.Thread):
def __init__(self):
self.__event = threading.Event()
self.__event.set()
threading.Thread.__init__(self)
def __rxcb(self, rfm):
bit = False
cnt = 1
train = []
#if args.write:
# wf = wave.open(args.write, "wb")
# wf.setnchannels(1)
# wf.setsampwidth(1)
# wf.setframerate(20000)
#else:
wf = None
while self.__event.isSet():
fifo = rfm.read_fifo_wait(64)
ba = bytearray()
for b in fifo:
mask = 0x80
while mask != 0:
if (b & mask) != 0:
ba.append(245)
else:
ba.append(10)
if ((b & mask) != 0) == bit:
cnt += 1
else:
if cnt < 3: #<150 us
train *= 0 #clear
elif cnt > 50:
if not bit:
train.append(cnt)
if len(train) > 20:
trainbuf.append(list(train))
train *= 0 #clear
elif len(train) > 0 or bit:
train.append(cnt)
cnt = 1
bit = not bit
mask >>= 1
if wf:
wf.writeframesraw(ba)
wf.writeframes('')
def run(self):
while True:
self.__event.wait()
rfm.set_params(
Datarate = 20.0 #kbit/s
)
rfm.start_receive(self.__rxcb)
def suspend(self):
self.__event.clear()
def resume(self):
self.__event.set()
rxthread = RxThread()
rxthread.daemon = True
rxthread.start()
lasttime = time.time() + 3
state = 1
while True:
time.sleep(0.1)
if len(trainbuf) > 0:
train = trainbuf.pop()
for i, v in enumerate(train):
train[i] = v * 50
dec = rcprotocols.decode(train)
if (dec):
print(dec)
else:
print(train)
if time.time() > lasttime:
lasttime = time.time() + 3
rxthread.suspend()
try:
txdata = rcprotocols.encode_dict({"protocol": "emylo", "id": 970046, "key": "A"})
rfm.set_params(
Datarate = 1000.0 / txdata[1]
)
rep = txdata[2]
rfm.send(txdata[0] * rep)
except:
pass
rxthread.resume()
time.sleep(10)
rctrx.send_dict({"protocol": "ittristate", "house": "A", "unit": 1, "state": state})
if (state == 1):
state = 0
else:
state = 1

View file

@ -9,27 +9,22 @@ from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-m", "--module", type=int, metavar="1-4", help=u"RaspyRFM module 1-4", default=1)
parser.add_argument("-o", "--mode", help=u"Mode (rcpulse, lacrosse)", default="rcpulse")
args = parser.parse_known_args()
parser.add_argument("-f", "--frequency", type=float, help=u"frequency in MHz", default=433.92)
args, remargs = parser.parse_known_args()
srvsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srvsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srvsock.bind(('', 1989))
srvsock.listen(5)
rfm = RaspyRFM(1, RFM69)
rfm.set_params(
Freq = 433.92, #MHz
Datarate = 20.0, #kbit/s
Bandwidth = 300, #kHz
SyncPattern = [],
RssiThresh = -105, #dBm
ModulationType = rfm69.OOK,
OokThreshType = 1, #peak thresh
OokPeakThreshDec = 3,
Preamble = 0,
TxPower = 13
)
def rxcb(dec, train):
pass
if not raspyrfm_test(args.module, RFM69):
print("Error! RaspyRFM not found")
exit()
rctrx = rcprotocols.RcTransceiver(args.module, args.frequency, rxcb)
lock = threading.Lock()
@ -47,12 +42,8 @@ class clientthread(threading.Thread):
print(chunk)
try:
lock.acquire()
rctrx.send_dict(json.loads(chunk))
txdata = rcprotocols.encode_dict(json.loads(chunk))
rfm.set_params(
SyncPattern = [],
Datarate = 1000.0 / txdata[1]
)
rfm.send(txdata[0] * txdata[2])
except:
pass
lock.release()

View file

@ -338,7 +338,6 @@ class Rfm69(threading.Thread):
elif key == "Datarate":
rate = int(round(FXOSC / (value * 1000)))
self.__write_reg_word(RegBitrateMsb, rate)
print("written datarate")
elif key == "Deviation":
dev = int(round(value * 1000 / FSTEP))
@ -530,9 +529,9 @@ class Rfm69(threading.Thread):
b = self.read_fifo_wait()
if b != 0:
thresh += 1
break;
break
if i == 149:
break;
break
#restore registers
self.__write_reg(RegRssiThresh, rssithresh)
@ -556,7 +555,7 @@ class Rfm69(threading.Thread):
self.__wait_int()
self.__mutex.acquire()
if self.__mode == MODE_RX:
break;
break
def start_receive(self, cb):
self.__start_rx(0)

View file

@ -1,121 +0,0 @@
#!/usr/bin/env python2.7
from raspyrfm import *
import sys
import time
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()
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(1, RFM69)
rfm.set_params(
Freq = 868.35, #MHz
Datarate = 20.0, #kbit/s
Bandwidth = 200, #kHz
SyncPattern = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07],
RssiThresh = -105, #dBm
ModulationType = rfm69.OOK,
OokThreshType = 1, #peak thresh
OokPeakThreshDec = 3,
)
if txdata:
rfm.SetParams(
SyncPattern = [],
Datarate = 5.0
)
rep = (args.repeats if args.repeats else txdata[1])
rfm.SendPacket(txdata[0] * rep)
print("Code sent!")
exit()
def Decode(pulses):
if len(pulses) != 118:
return;
b = ""
for p in pulses:
if (p>=6) and (p<=10):
b += "s"
elif (p > 10) and (p<15):
b += "l"
else:
b += '?'
s = ""
while len(b) > 0:
if b[:2] == 'ss':
s += '0'
elif b[:2] == 'll':
s += '1'
else:
s += '?'
b = b[2:]
print(s)
print("SYNC " + s[1:13] + " HC1 " + s[13:22] + " HC2 " + s[22:31] +
" ADR " + s[31:40])
return
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.receive_packet(400)
s = ""
pulsecount = 3
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