С момента установки стабилизаторов на сеть всего домашнего хозяйства очередной раз переделал главный щиток. Было решено отказаться от реле напряжения “Зубр” – в них больше не было смысла: пока сеть в рамках работы стабилизатора – она вытягивается стабилизатором до более-менее нормального уровня; если же стабилизатор не может этого сделать – он сам и отключит нагрузку. Единственным минусом было то, что таймаут включения нагрузки на стабилизаторе Элекс Герц порядка 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А, она обходится без токового трансформатора: нагрузка подключается через плату.
2 мысли о “Использование PZEM-004T в хозяйстве”