Измерване на мрежово напрежение, ток, мощност и енергия с помоща на специализиран модул

Наскоро се сдобих с панел който измерва напрежение, ток, мощност и енергия – http://www.aliexpress.com/item/PEACEFAIR-AC-100A-Digital-LCD-power-meter-power-energy-Volt-Ammeter-with-shell-4-led-panel/32320321777.html?s=p

m1

m2

Производство е на фирмата Pui Ching (Peacefair), модел PZEM-004 (v3.0). Модулът предлага възможност за включване към компютър (или друг контролер) и възможност за четене на данните от панела в реално време. За комуникация, панела разчита на серийна комуникация от типа RS232 (TTL) с интерфейс който е галванично разделен (чрез оптрони) от мрежовото напрежение.

Поглеждаме отвътре:

m3

Следните по-интересни елементи се забелязват от пръв поглед:

Разбира се, целта бе модулът да се свържи към компютър за да могат данните да се записват някъде. След няколко-дневни, безуспешни опити в комуникация с китайците, да науча детайли относно параметрите на връзката (скорост и др.настройки) реших да поровя в Нет-а и открих, че комуникацията се извършва на 9600, 8-N-1. От китайците, намерих линк от къде може да се изтегли софтуера който предлага възможности да показва индикациите (https://mega.nz/#!WoxGgCSS!A9pDXDXrv8nwUgJmhXBl-dhFt-JzOwuBg_JGq5qsD0o), но за съжаление всичко е на йероглифи и нищо не се разбира.

Следвайки указанията в документацията видях, че трябва да се изпращят заявки (команди) след което модула ще отговори подобаващо. Табличката показва възможните команди и примерни отговори от страна на модула:

NO. function Head Data1- Data5 Sum
1 voltage B0 C0 A8 01 01 00 (Computer sends a request to read the voltage value) 1A
A0 00 E6 02 00 00 (Meter reply the voltage value is 230.2V) 88
2 current B1 C0 A8 01 01 00 (Computer sends a request to read the current value) 1B
A1 00 11 20 00 00 (Meter reply the current value is 17.32A) D2
3 Active power B2 C0 A8 01 01 00 (Computer sends a request to read the active power value) 1C
A2 08 98 00 00 00 (Meter reply the active power value is 2200w) 42
4 Read energy B3 C0 A8 01 01 00 (Computer sends a request to read the energy value) 1D
A3 01 86 9f 00 00 (Meter reply the energy value is 99999wh) C9
5 Set the module address B4 C0 A8 01 01 00 (Computer sends a request to set the address, the address is 192.168.1.1) 1E
A4 00 00 00 00 00 (Meter reply the address was successfully set) A4
6 Set the power alarm threshold B5 C0 A8 01 01 14 (computer sends a request to set a power alarm threshold) 33
A5 00 00 00 00 00 (Meter reply the power alarm threshold was successfully set) A5

Или накратко:

B0 C0 A8 01 01 00 1A - sends a request to read the voltage value
B1 C0 A8 01 01 00 1B - sends a request to read the current value
B2 C0 A8 01 01 00 1C - sends a request to read the active power value
B3 C0 A8 01 01 00 1D - sends a request to read the energy value
B4 C0 A8 01 01 00 1E - sends a request to set the address to 192.168.1.1
B5 C0 A8 01 01 14 33 - sends a request to set a power alarm threshold

rs232ttlwires

Остана да се свърже подходящо кабелче към компютъра (USB-RS232/TTL) и да закачите жиците (цветовете може да се различават за различните типове кабели).

За писането на софтуера използвах Borland C++ Builder който е доста стар, но пък удобен за такива малки проекти. След няколко часово експериментиране се получи следната програма с която може да се четат данните:

ACtest1ACtest

Частта от кода която обслужва самата комуникация с модула е следната:

HANDLE hComm;
DWORD dwMask;
OVERLAPPED ovr;
DCB dcb;
unsigned char setvalue1[7] = { 0xB4, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1E }; // Set the address to 192.168.1.1
unsigned char setvalue2[7] = { 0xB0, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1A }; // Read the voltage value
unsigned char setvalue3[7] = { 0xB1, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1B }; // Read the current value
unsigned char setvalue4[7] = { 0xB2, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1C }; // Read the active power value
unsigned char setvalue5[7] = { 0xB3, 0xC0, 0xA8, 0x01, 0x01, 0x00, 0x1D }; // Read the energy value
unsigned char getvalue[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };


// initialize a serial connection
hComm = CreateFile(ком-порта, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hComm == INVALID_HANDLE_VALUE) { обработка на грешки }

// setup comm params: 9600-8-N-1
GetCommState(hComm, &dcb);
dcb.BaudRate = 9600;
dcb.fOutX = false;
dcb.fInX = false;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
SetCommState(hComm, &dcb);
// Read one char at a time.
GetCommMask(hComm, &dwMask);
dwMask |= EV_RXCHAR;
SetCommMask(hComm, dwMask);

...
//-----------------
// sent query about voltage
        for (int i=0; i<7; i++) {
                WriteFile(hComm, &setvalue2[i], 1, &dwMask, NULL);
        }
        Sleep(10);
        // read voltage
        for (int k=0; k<7; k++) {
                ReadFile(hComm, &getvalue[k], 1, &dwMask, NULL);
        }
        // Display Voltage
        Memo1->Lines->Add("Voltage: " + IntToStr(getvalue[2]) + "." + IntToStr(getvalue[3]) + " /V/");
//-----------------
// sent query about Amperage/Current
        for (int i=0; i<7; i++) {
                WriteFile(hComm, &setvalue3[i], 1, &dwMask, NULL);
        }
        Sleep(10);
        memset(getvalue, 0x0, 7);
        // read current
        for (int k=0; k<7; k++) {
                ReadFile(hComm, &getvalue[k], 1, &dwMask, NULL);
        }
        // Display Amperage/Current
        Memo1->Lines->Add("Current: " + IntToStr(getvalue[2]) + "." + IntToStr(getvalue[3]) + " /A/");
//-----------------
// sent query about active power
        for (int i=0; i<7; i++) {
                WriteFile(hComm, &setvalue4[i], 1, &dwMask, NULL);
        }
        Sleep(10);
        memset(getvalue, 0x0, 7);
        // read active power value
        for (int k=0; k<7; k++) {
                ReadFile(hComm, &getvalue[k], 1, &dwMask, NULL);
        }
        // Display Active Power value
        Memo1->Lines->Add("Power: " + IntToHex(getvalue[1],2) + " " + IntToHex(getvalue[2],2) + " " + IntToHex(getvalue[3],2) + " /W/");
//-----------------
// sent query about Energy
        for (int i=0; i<7; i++) {
                WriteFile(hComm, &setvalue5[i], 1, &dwMask, NULL);
        }
        Sleep(10);
        memset(getvalue, 0x0, 7);
        // read energy value
        for (int k=0; k<7; k++) {
                ReadFile(hComm, &getvalue[k], 1, &dwMask, NULL);
        }
        // Display Energy value
        Memo1->Lines->Add("Energy: " + IntToHex(getvalue[1],2) + " " + IntToHex(getvalue[2],2) + " " + IntToHex(getvalue[3],2) + " /Wh/");    
//-----------------
CloseHandle(hComm);

Оригиналния китайски софтуер може да бъде изтеглен от тук или тук, като преди това е необходимо да се инсталира драйвер за USB<->RS232 конвертора. Паролата която изисква програмата е admin.

Налична е библиотека за Arduino: https://github.com/olehs/PZEM004T

Подробно ревю на руски има на следния адрес: https://mysku.ru/blog/china-stores/38717.html

Още примери (с Raspberry Pi, Arduino, ESP8266) има на следните линкове:

https://mysku.ru/blog/china-stores/43331.html

https://wiki.cuvoodoo.info/doku.php?id=spark_counter

http://zftlab.org/pages/2016050700.html

http://www.xpablo.cz/?p=899

И накрая, като малка забележка, искам да добавя, че при всяка заявка към модула той издава звук от малък зумер (вижда се на картинката горе) което е доста дразнещо и е по-добре да се разпой от платката.