Использование PZEM-004T в хозяйстве

С момента установки стабилизаторов на сеть всего домашнего хозяйства очередной раз переделал главный щиток. Было решено отказаться от реле напряжения “Зубр” – в них больше не было смысла: пока сеть в рамках работы стабилизатора – она вытягивается стабилизатором до более-менее нормального уровня; если же стабилизатор не может этого сделать – он сам и отключит нагрузку. Единственным минусом было то, что таймаут включения нагрузки на стабилизаторе Элекс Герц порядка 3 секунд и не регулируется.

В итоге были собраны минутные таймеры на NE555 с выходом на управление пускателем, который и отключал сеть на заданный интервал.

Но речь сейчас пойдет немного о другом элементе щитка.

Для мониторинга потребления было решено оставить измерители под DIN-рейку, они были включены до стабилизаторов и показывали то, какая сеть приходит на них. К сожалению, у них нет никакого интерфейса для работы с компьютером, а хотелось еще и собирать долговременную статистику. Для этого были найдены, куплены и установлены рядом с DIN-измерителями еще и PZEM-004T – небольшие платы с подключением к сети и к токовому трансформатору с одной стороны и интерфейсом RS232 с другой.

Плата позволяла измерять напряжение, ток, текущее потребление и работать в качестве счетчика – показывать накопленное значение потребления. Для сброса последнего значения на плате есть кнопка (надо зажать на 5 секунд, отпустить и еще раз кратковременно нажать).

Для сбора статистики решил использовать одну из лежащих без дела Cubieboard2. Платы подключил через переходники на pl2303.

Для общения с платой в сети был найден скрипт на Python, который (с небольшими модификациями) и использовал для наполнения RRD-базы.

Продублирую здесь
#!/usr/bin/env python3
#coding=utf-8
 
# Code by Massi from  https://www.raspberrypi.org/forums/viewtopic.php?t=124958#p923274
 
 
# PDAControl
# Documentation PDAControl English:
# http://pdacontrolen.com/meter-pzem-004t-with-arduino-esp32-esp8266-python-raspberry-pi/
# Documentacion PDAControl Español:
# http://pdacontroles.com/medidor-pzem-004t-con-arduino-esp32-esp8266-python-raspberry-pi/
# Video Tutorial : https://youtu.be/qt32YT_1oH8
 
 
import serial
import time
import struct
import sys
 
class PZEM:
 
	setAddrBytes 		=	[0xB4,0xC0,0xA8,0x01,0x01,0x00,0x1E]
	readVoltageBytes 	= 	[0xB0,0xC0,0xA8,0x01,0x01,0x00,0x1A]
	readCurrentBytes 	= 	[0XB1,0xC0,0xA8,0x01,0x01,0x00,0x1B]
	readPowerBytes 		= 	[0XB2,0xC0,0xA8,0x01,0x01,0x00,0x1C]
	readRegPowerBytes 	= 	[0XB3,0xC0,0xA8,0x01,0x01,0x00,0x1D]
 
 
	# dmesg | grep tty       list Serial linux command
 
	def __init__(self, com=sys.argv[1], timeout=10.0):       # Usb serial port
	#def __init__(self, com="/dev/ttyUSB0", timeout=10.0):       # Usb serial port
	#def __init__(self, com="/dev/ttyAMA0", timeout=10.0):  	 # Raspberry Pi port Serial TTL
	#def __init__(self,com="/dev/rfcomm0", timeout=10.0):
 
 
		self.ser = serial.Serial(
			port=com,
			baudrate=9600,
			parity=serial.PARITY_NONE,
			stopbits=serial.STOPBITS_ONE,
			bytesize=serial.EIGHTBITS,
			timeout = timeout
		)
		if self.ser.isOpen():
			self.ser.close()
		self.ser.open()
 
	def checkChecksum(self, _tuple):
		_list = list(_tuple)
		_checksum = _list[-1]
		_list.pop()
		_sum = sum(_list)
		if _checksum == _sum%256:
			return True
		else:
			raise Exception("Wrong checksum")
 
	def isReady(self):
		self.ser.write(serial.to_bytes(self.setAddrBytes))
		rcv = self.ser.read(7)
		if len(rcv) == 7:
			unpacked = struct.unpack("!7B", rcv)
			if(self.checkChecksum(unpacked)):
				return True
		else:
			raise serial.SerialTimeoutException("Timeout setting address")
 
	def readVoltage(self):
		self.ser.write(serial.to_bytes(self.readVoltageBytes))
		rcv = self.ser.read(7)
		if len(rcv) == 7:
			unpacked = struct.unpack("!7B", rcv)
			if(self.checkChecksum(unpacked)):
				tension = unpacked[2]+unpacked[3]/10.0
				return tension
		else:
			raise serial.SerialTimeoutException("Timeout reading tension")
 
	def readCurrent(self):
		self.ser.write(serial.to_bytes(self.readCurrentBytes))
		rcv = self.ser.read(7)
		if len(rcv) == 7:
			unpacked = struct.unpack("!7B", rcv)
			if(self.checkChecksum(unpacked)):
				current = unpacked[2]+unpacked[3]/100.0
				return current
		else:
			raise serial.SerialTimeoutException("Timeout reading current")
 
	def readPower(self):
		self.ser.write(serial.to_bytes(self.readPowerBytes))
		rcv = self.ser.read(7)
		if len(rcv) == 7:
			unpacked = struct.unpack("!7B", rcv)
			if(self.checkChecksum(unpacked)):
				power = unpacked[1]*256+unpacked[2]
				return power
		else:
			raise serial.SerialTimeoutException("Timeout reading power")
 
	def readRegPower(self):
		self.ser.write(serial.to_bytes(self.readRegPowerBytes))
		rcv = self.ser.read(7)
		if len(rcv) == 7:
			unpacked = struct.unpack("!7B", rcv)
			if(self.checkChecksum(unpacked)):
				regPower = unpacked[1]*256*256+unpacked[2]*256+unpacked[3]
				return regPower
		else:
			raise serial.SerialTimeoutException("Timeout reading registered power")
 
	def readAll(self):
		if(self.isReady()):
			return(self.readVoltage(),self.readCurrent(),self.readPower(),self.readRegPower())
 
	def close(self):
		self.ser.close()
 
 
 
if __name__ == "__main__":
	sensor = PZEM()
	try:
 
		#print("Checking readiness")
		#print(sensor.isReady())
		#print("Reading voltage")
		print("V:", sensor.readVoltage(), "I:", sensor.readCurrent(), "W:", sensor.readPower(), "P:", sensor.readRegPower())
		#print("Reading current")
		#print(sensor.readCurrent())
		#print("Reading power")
		#print(sensor.readPower())
		#print("reading registered power")
		#print(sensor.readRegPower())
		#print("reading all")
		#print(sensor.readAll())
	finally:
		sensor.close()

[свернуть]

Данные выводились в таком формате:
V: 204.5 I: 20.55 W: 4165 P: 9598334

Устройство успешно работает уже несколько месяцев. Провода от токовых трансформаторов удлинены примерно до метра; плюс-минус так же – провод с RS232 до переходников на pl2303.

По мере развития темы солнечных панелей встал вопрос об учете выработки. Были использованы оставшиеся измерители; запущены в работу все найденные одноплатники – еще одна куби и первая малинка.

По мере роста числа точек были дозаказаны еще измерители. Новые – ими оказалась 3-я версия – отличались: отсутствовала кнопка (сбросить счетчик наработки можно было только программными методами); поменялся протокол на Modbus; добавилось измерение Power Factor и частоты; добавлен триггер на превышение мощности и возможность калибровки.

Так или иначе, старый софт был неприменим. Снова поиски, на сей раз скрипт был найден на просторах одного из форумов.

Снова небольшая модификация для приведения вывода скрипта к совместимому с первым вариантом вида, плюс добавление возможности указать устройство для считывания данных в качестве аргумента:

Спойлер

#!/usr/bin/python3
 
import pymodbus
import serial
import math
import sys
 
from pymodbus.pdu import ModbusRequest
from pymodbus.client.sync import ModbusSerialClient as ModbusClient
from pymodbus.transaction import ModbusRtuFramer
 
def calc (registers, factor):
    format = '%%0.%df' % int (math.ceil (math.log10 (factor)))
    if len(registers) == 1:
        return format % ((1.0 * registers[0]) / factor)
    elif len(registers) == 2:
        return format % (((1.0 * registers[1] * 65535) + (1.0 * registers[0])) / factor)
    #endif
#end calc
 
client = ModbusClient (method = "rtu", port=sys.argv[1], stopbits = 1, bytesize = 8, parity = 'N', baudrate = 9600)
 
#Connect to the serial modbus server
connection = client.connect()
if client.connect ():
    try:
        result = client.read_input_registers (0x0000, 10, unit = 0x01)
        print ('V: ' + calc (result.registers[0:1], 10), 'I: ' + calc (result.registers[1:3], 1000), 'W: ' + calc (result.registers[3:5], 10), 'P: ' + calc (result.registers[5:7], 1), 'F: ' + calc (result.registers[7:8], 10), 'f: ' + calc (result.registers[8:9], 100), 'a: ' + calc (result.registers[9:10], 1))
    finally:
        client.close()
    #end try
#end if

[свернуть]

Пример данных:

V: 225.6 I: 0.849 W: 182.8 P: 2047 F: 49.9 f: 0.95 a: 0

Пока в тестировании один день. Интерфейсный кабель (RS232) сделал из витой пары; длина около 8 метров.

Существует также версия под меньшие токи – до 10А, она обходится без токового трансформатора: нагрузка подключается через плату.

Добавить комментарий