Skip to content

Instantly share code, notes, and snippets.

@raplin
Last active August 17, 2022 17:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raplin/76da6392f34934738ff865891a7b672f to your computer and use it in GitHub Desktop.
Save raplin/76da6392f34934738ff865891a7b672f to your computer and use it in GitHub Desktop.
Dependency-free reading of the Hopi HP-9800 power meter (USB version) in python. Windows or Linux etc.
#
# By Richard Aplin, released into the public domain for any purpose, no warranties implied, 8/17/2022
#
#
import struct
import serial
#Super lightweight code to read Hopi HP-9800 power meter
class Hopi(object):
REGS=[
("Active Power","W"),
("RMS Current","A"),
("Voltage RMS","V"),
("Frequency","Hz"),
("Power Factor","pf"),
("Annual Power","KWH"),
("Active Power","KWH"),
("Reactive Power","KWH"),
("Load Time","mins"),
]
def __init__(self,port="COM4"):
baud=9600
self.debug=False #True
self.ser=serial.Serial(port,baudrate=baud,timeout=1)
def crc(self,data):
poly=0xa001
crc = 0xFFFF
for b in data:
cur_byte = 0xFF & ord(b)
for _ in range(0, 8):
if (crc & 0x0001) ^ (cur_byte & 0x0001):
crc = (crc >> 1) ^ poly
else:
crc >>= 1
cur_byte >>= 1
return struct.pack("<H",crc&0xffff)
def read(self):
#self.readRegs(4,2) #1,10)
self.regs=self.readRegs(0,len(self.REGS)*2) #1,10)
def display(self):
dmax=99 #can limit how many regs displayed
for idx,value in enumerate(self.regs):
name,unit=self.REGS[idx]
print "%s\t%.3f%s" % (name,value,unit)
dmax-=1
if dmax==0:
break
def phex(self,data):
s=" ".join(["%02x" % ord(d) for d in data])
return s
def readRegs(self,first,count,addr=1):
cmd=0x03
fout=[]
m=struct.pack(">BBHH",addr,cmd,first,count)
m+=self.crc(m)
if self.debug:
print "\t>",self.phex(m)
self.ser.write(m)
replyLen=3+(count*2)+2
r=self.ser.read(replyLen)
if self.debug:
print "\t<",self.phex(r)
if len(r)!=replyLen:
print "Bad reply len",len(r)
return
ccrc=self.crc( r[:-2] )
if ccrc!=r[-2:]:
print "bad crc"
return
addr,f,bcount=struct.unpack(">BBB",r[:3])
if addr==addr and f==cmd:
#unpack floats from hopi
d=r[3:-2]
fpos=0
while fpos<len(d):
fpval=d[fpos:fpos+4]
v=struct.unpack("<f",fpval)[0]
fout.append(v)
fpos+=4
#print fpos/2,v
return fout
else:
print "Bad reply"
h=Hopi(port="COM4")
while True:
h.read()
h.display()
@OParczyk
Copy link

Hi! Could you please consider adding a license? I'm currently porting to python 3 and making some other changes and would like to redistribute them later, but without a license it's "all rights reserved" and I can't legally do so.
Thanks!

@raplin
Copy link
Author

raplin commented Aug 17, 2022

sure thing

@OParczyk
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment