From 613f46b4791b0a9373ec2d0942d102282f724024 Mon Sep 17 00:00:00 2001 From: "S. Seegel" Date: Sun, 5 Mar 2017 22:42:34 +0100 Subject: [PATCH] corrected links --- connair.py | 73 ++++++++++++- hamarx.py | 78 +++++++++++++- intertechno.py | 40 ++++++- lacrosse.py | 33 +++++- logilightrx.py | 54 +++++++++- logilighttx.py | 38 ++++++- rfm69.py | 287 ++++++++++++++++++++++++++++++++++++++++++++++++- sensors.py | 84 ++++++++++++++- 8 files changed, 679 insertions(+), 8 deletions(-) mode change 120000 => 100755 connair.py mode change 120000 => 100755 hamarx.py mode change 120000 => 100755 intertechno.py mode change 120000 => 100755 lacrosse.py mode change 120000 => 100755 logilightrx.py mode change 120000 => 100755 logilighttx.py mode change 120000 => 100644 rfm69.py mode change 120000 => 100644 sensors.py diff --git a/connair.py b/connair.py deleted file mode 120000 index 96fdcc8..0000000 --- a/connair.py +++ /dev/null @@ -1 +0,0 @@ -../my-pi-projects/RaspyRFM/connair.py \ No newline at end of file diff --git a/connair.py b/connair.py new file mode 100755 index 0000000..fe53a24 --- /dev/null +++ b/connair.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python2.7 + +import socket +import rfm69 +import sys + +UDP_IP = "0.0.0.0" +UDP_PORT = 49880 +HELLO_MESSAGE = "HCGW:VC:Seegel Systeme;MC:RaspyRFM;FW:1.00;IP:192.168.2.124;;" + +sock = socket.socket(socket.AF_INET, # Internet + socket.SOCK_DGRAM) # UDP +sock.bind((UDP_IP, UDP_PORT)) + +#sock.sendto("TXP:0,0,10,20000,350,25,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,17;", ("192.168.178.51", 49880)) +#sys.exit(0) + +rfm = rfm69.Rfm69() +rfm.SetParams( + Freq = 433.92, + TXPower = 13, + ModulationType = rfm69.OOK, + SyncPattern = [], + RssiThresh = -72 + ) + +while True: + data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes + #data = "TXP:0,0,6,5950,350,25,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,3,1,1,3,1,3,1,3,3,1,1,17;" + #addr = ("123", 5) + print "received message:", data, "from ", addr + + msg = str(data).split(":") + + if msg[0] == "SEARCH HCGW": + sock.sendto(HELLO_MESSAGE, addr) + print "Hello message" + + 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 + print "bitleft: ", bitleft + + print "reps: ", rep + print "Pulse data", pulsedata + print "bin:", bindata + rfm.SetParams(Datarate = 1000.0 / steplen) + rfm.SendPacket(bindata * rep) diff --git a/hamarx.py b/hamarx.py deleted file mode 120000 index 68fd9a5..0000000 --- a/hamarx.py +++ /dev/null @@ -1 +0,0 @@ -../my-pi-projects/RaspyRFM/hamarx.py \ No newline at end of file diff --git a/hamarx.py b/hamarx.py new file mode 100755 index 0000000..ff14477 --- /dev/null +++ b/hamarx.py @@ -0,0 +1,77 @@ +#!/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: + 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; + diff --git a/intertechno.py b/intertechno.py deleted file mode 120000 index b09f2b6..0000000 --- a/intertechno.py +++ /dev/null @@ -1 +0,0 @@ -../my-pi-projects/RaspyRFM/intertechno.py \ No newline at end of file diff --git a/intertechno.py b/intertechno.py new file mode 100755 index 0000000..624fce0 --- /dev/null +++ b/intertechno.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python2.7 + +from rfm69 import Rfm69 +import rfm69 +import sys +import time + +if len(sys.argv) != 2: + print "usage: intertechno " #12-digit code 12 * ['0' | '1' | 'f'] + print "Example: intertechno 0FF0F0F00FF0" + sys.exit(1) + +rfm = Rfm69() +rfm.SetParams( + Freq = 433.92, + Datarate = 2.666666, + TXPower = 13, + ModulationType = rfm69.OOK, + SyncPattern = [] + ) + +#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 + +data = MakeFrame(sys.argv[1], 8) +rfm.SendPacket(data) diff --git a/lacrosse.py b/lacrosse.py deleted file mode 120000 index 0901b9f..0000000 --- a/lacrosse.py +++ /dev/null @@ -1 +0,0 @@ -../my-pi-projects/RaspyRFM/lacrosse.py \ No newline at end of file diff --git a/lacrosse.py b/lacrosse.py new file mode 100755 index 0000000..3fc4352 --- /dev/null +++ b/lacrosse.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python2.7 + +from rfm69 import Rfm69 +import rfm69 +import sensors +from sensors import rawsensor +import sys +import time + +rfm = Rfm69() + +rfm.SetParams( + Freq = 868.300, #MHz center frequency + Datarate = 9.579, #17.241, #kbit/s baudrate + ModulationType = rfm69.FSK, #modulation + SyncPattern = [0x2d, 0xd4], #syncword + Bandwidth = 200, #kHz bandwidth + LnaGain = 0x88 + ) + +print hex(rfm.ReadReg(0x07)) +print hex(rfm.ReadReg(0x08)) +print hex(rfm.ReadReg(0x09)) + + +data = [] + +while 1: + data = rfm.ReceivePacket(7) + obj = rawsensor.CreateSensor(data) + print(str(obj)) + \ No newline at end of file diff --git a/logilightrx.py b/logilightrx.py deleted file mode 120000 index 7e71363..0000000 --- a/logilightrx.py +++ /dev/null @@ -1 +0,0 @@ -../my-pi-projects/RaspyRFM/logilightrx.py \ No newline at end of file diff --git a/logilightrx.py b/logilightrx.py new file mode 100755 index 0000000..357f253 --- /dev/null +++ b/logilightrx.py @@ -0,0 +1,53 @@ +#!/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] diff --git a/logilighttx.py b/logilighttx.py deleted file mode 120000 index 68bd8f7..0000000 --- a/logilighttx.py +++ /dev/null @@ -1 +0,0 @@ -../my-pi-projects/RaspyRFM/logilighttx.py \ No newline at end of file diff --git a/logilighttx.py b/logilighttx.py new file mode 100755 index 0000000..45fb7ea --- /dev/null +++ b/logilighttx.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python2.7 + +from rfm69 import Rfm69 +import rfm69 +import sys + +if len(sys.argv) != 5: + print "usage: logiloghttx.py " + 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) diff --git a/rfm69.py b/rfm69.py deleted file mode 120000 index e503920..0000000 --- a/rfm69.py +++ /dev/null @@ -1 +0,0 @@ -../my-pi-projects/RaspyRFM/rfm69.py \ No newline at end of file diff --git a/rfm69.py b/rfm69.py new file mode 100644 index 0000000..18ccd2a --- /dev/null +++ b/rfm69.py @@ -0,0 +1,286 @@ +import RPi.GPIO as GPIO +import spidev +import threading +import time + +FXOSC = 32E6 +FSTEP = FXOSC / (1<<19) + + +#------ Raspberry RFM Module connection ----- +# Connect RaspyRFM module 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, DIO0/nIRQ on 2nd modul! +# 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, NSS/nFFS on 2nd modul! +#-------------------------------------------------# + +#RFM69 registers +RegFifo = 0x00 +RegOpMode = 0x01 +RegDataModul = 0x02 +RegBitrateMsb = 0x03 +RegBitrateLsb = 0x04 +RegFdevMsb = 0x05 +RegFdevLsb = 0x06 +RegFrMsb = 0x07 +RegFrMid = 0x08 +RegFrLsb = 0x09 +RegPaLevel = 0x11 +RegLna = 0x18 +RegRxBw = 0x19 +RegAfcBw = 0x1A +RegAfcFei = 0x1E +RegAfcMsb = 0x1F +RegAfcLsb = 0x20 +RegFeiMsb = 0x21 +RegFeiLsb = 0x22 +RegRssiConfig = 0x23 +RegRssiValue = 0x24 +RegDioMapping1 = 0x25 +RegDioMapping2 = 0x26 +RegIrqFlags1 = 0x27 +RegIrqFlags2 = 0x28 +RegRssiThresh = 0x29 +RegPreambleMsb = 0x2C +RegPreambleLsb = 0x2D +RegSyncConfig = 0x2E +RegSyncValue1 = 0x2F +RegPacketConfig1 = 0x37 +RegPayloadLength = 0x38 +RegFifoThresh = 0x3C +RegPacketConfig2 = 0x3D +RegTestDagc = 0x6F + +InterPacketRxDelay = 4 #Bitposition +RestartRx = 2 +AutoRxRestartOn = 1 +AesOn = 0 + + +#Modulation type +OOK = 1 +FSK = 0 + +#RFM69 modes +MODE_SLEEP = 0 +MODE_STDBY = 1 +MODE_FS = 2 +MODE_TX = 3 +MODE_RX = 4 + +class Rfm69: + def __init__(self, cs = 0, gpio_int = 25): + 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 + + #Testing presence of module + err = False + for i in range(0, 8): + self.__WriteReg(RegSyncValue1 + i, 0x55) + if self.ReadReg(RegSyncValue1 + i) != 0x55: + err = True + break + self.__WriteReg(RegSyncValue1 + i, 0xAA) + if self.ReadReg(RegSyncValue1 + i) != 0xAA: + err = True + break + if err == True: + print "ERROR! RFM69 not found!" + return + + print "RFM69 found!" + GPIO.setmode(GPIO.BCM) + GPIO.setup(gpio_int, GPIO.IN) + GPIO.add_event_detect(gpio_int, GPIO.RISING, callback=self.__RfmIrq) + + self.__WriteReg(RegOpMode, MODE_STDBY << 2) + self.__WaitMode() + + config = {} + config[RegDataModul] = 0 #packet mode, modulation shaping, modulation + config[RegPayloadLength] = 0 + config[RegPreambleMsb] = 0 + config[RegPreambleLsb] = 0 + config[RegSyncConfig] = 0 #sync off + config[RegPacketConfig1] = 0x00 #Fixed length, CRC off, no adr + config[RegPacketConfig2] = 0 #1<> 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 ReadReg(self, reg): + temp = self.__spi.xfer2([reg & 0x7F, 0x00]) + return temp[1] + + def ReadRegWord(self, reg): + temp = self.__spi.xfer2([reg & 0x7F, 0x00, 0x00]) + return (temp[1] << 8) | (temp[2]) + + def ReadRssiValue(self): + self.__WriteReg(RegRssiConfig, 1) + while ((self.ReadReg(RegRssiConfig) & (1<<1)) == 0): + pass + return self.ReadReg(RegRssiValue) + + def SetParams(self, **params): + for key in params: + value = params[key] + if key == "Freq": + fword = int(round(value * 1E6 / FSTEP)) + self.__WriteReg(RegFrLsb, fword) + self.__WriteReg(RegFrMid, fword >> 8) + self.__WriteReg(RegFrMsb, fword >> 16) + + 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 + 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) + self.__SetReg(RegAfcBw, 0x1F, m<<3 | e) + + elif key == "Preamble": + self.__WriteRegWord(RegPreambleMsb, value) + + elif key == "LnaGain": + self.__SetReg(RegLna, 0x03, value) + + elif key == "RssiThresh": + th = -(value * 2) + self.__WriteReg(RegRssiThresh, th) + + else: + print("Unrecognized option >>" + key + "<<") + + def __WaitMode(self): + while ((self.ReadReg(RegIrqFlags1) & (1<<7)) == 0): + pass + + def SendPacket(self, data): + self.__WriteReg(RegOpMode, MODE_STDBY << 2) + self.__WaitMode() + + #flush FIFO + status = self.ReadReg(RegIrqFlags2) + while (status & 0x40 == 0x40): + self.ReadReg(RegFifo) + status = self.ReadReg(RegIrqFlags2) + + self.__WriteReg(RegPayloadLength, 0) + self.__SetDioMapping(0, 0) # DIO0 -> PacketSent + self.__WriteReg(RegOpMode, MODE_TX << 2) #TX Mode + + for i, d in enumerate(data): + self.__WriteReg(RegFifo, d) + if i>60: + status = self.ReadReg(RegIrqFlags2) + #check FifoFull + while (status & 0x80) == 0x80: + status = self.ReadReg(RegIrqFlags2) + + #wait packet sent + self.__event.wait() + self.__event.clear() + self.__WriteReg(RegOpMode, MODE_STDBY << 2) + self.__WaitMode() + + def ReceivePacket(self, length): + self.__WriteReg(RegPayloadLength, length) + + self.__SetDioMapping(0, 2) #DIO0 -> SyncAddress + self.__SetDioMapping(1, 3) + self.__SetReg(RegOpMode, 7<<2, 4<<2) #RX mode + + self.__event.wait() + self.__event.clear() + self.__SetDioMapping(0, 1) #DIO0 -> PayloaReady + + rssi = -self.ReadReg(RegRssiValue) / 2 + self.__event.wait() + self.__event.clear() + + result = [] + for x in range(length): + result.append(self.ReadReg(RegFifo)) + + self.__WriteReg(RegOpMode, 0) #idle mode + + return (result, rssi) diff --git a/sensors.py b/sensors.py deleted file mode 120000 index 13baf97..0000000 --- a/sensors.py +++ /dev/null @@ -1 +0,0 @@ -../my-pi-projects/RaspyRFM/sensors.py \ No newline at end of file diff --git a/sensors.py b/sensors.py new file mode 100644 index 0000000..c346cc2 --- /dev/null +++ b/sensors.py @@ -0,0 +1,83 @@ +def crc8(buf): + crc = 0 + for j in range(5): + crc = crc ^ buf[j] + for i in range(8): + if (crc & 0x80 == 0x80): + crc = (crc << 1) ^ 0x31 + else: + crc <<= 1 + return crc & 0xFF + +def csum(data): + s = 0 + for i in range(12): + s += data[i] + return s & 0xFF + +class rawsensor(object): + def __init__(self, data): + self._data = {} + self.__raw = data + + def __str__(self): + res = 'RAW RSSI ' + str(self.__raw[1]) + " dBm: " + for data in self.__raw[0]: + res = res + ' ' + hex(data)[2:]; + return res; + + def GetData(self): + return self._data + + @staticmethod + def CreateSensor(data): + obj = lacross.Create(data) + if (obj): + return obj + + obj = emt7110.Create(data) + if (obj): + return obj + + return rawsensor(data) + +class lacross(rawsensor): + def __init__(self, data): + rawsensor.__init__(self, data) + id = data[0][0] << 4 | data[0][1] >> 4 + print "A", hex(data[0][0])[2:], hex(data[0][1])[2:] + self._data['ID'] = hex(id & 0xFC)[2:] + self._data['init'] = bool(id & 1<<1) + self._data['T'] = (10 * ((data[0][1] & 0xF) - 4) + (data[0][2] >> 4) + (data[0][2] & 0xF) / 10.0, 'C') + rh = data[0][3] & 0x7F + if rh <= 100: + self._data['RH'] = (rh, '%') + self._data['batlo'] = bool(rh & 1<<7) + self._data['RSSI'] = data[1] + + def __str__(self): + res = 'La crosse ' + str(self._data) # + ' ' + rawsensor.__str__(self); + return res; + + @staticmethod + def Create(data): + if len(data[0]) >= 5 and len(data[0]) <= 8 and crc8(data[0]) == 0: + return lacross(data) + +class emt7110(rawsensor): + def __init__(self, data): + rawsensor.__init__(self, data) + id = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3] + self._data['ID'] = hex(id)[2:] + self._data['P'] = (((data[4] << 8 | data[5]) & 0x3FFF) / 2.0, 'W') + self._data['U'] = (data[8] / 2.0 + 128, 'V') + self._data['I'] = (data[6] << 8 | data[7], 'mA') + self._data['W'] = (((data[9] << 8 | data[10]) & 0x3FFF) / 100, 'kWh') + + def __str__(self): + return 'emt7110 ' + str(self._data) + + @staticmethod + def Create(data): + if len(data) >= 12 and len(data) <= 20 and csum(data) == 0: + return emt7110(data)