трансформаторных подстанциях, часто возникает необходимость оперативной проверки работоспособности счетчиков.
Как правило, в местах, где счетчики работают в составе АСКУЭ и соединены в группы по витой
паре RS 485 и подключены к шлюзу Меркурий 228, происходит пропадание связи в момент
установления связи со стороны сервера.
Чтобы выяснить, по какой причине данные со счетчика не поступают на сервер, необходимо с
собой постоянно носить ноутбук и преобразователь Меркурий 221, а в случае проверки
правильности подключения счетчика в ячейке еще необходимо использовать прибор ВАФ.
Учитывая, что на подстанциях в ОРУ или ЗРУ не всегда имеется возможность обеспечения места для работы с компьютерным оборудованием, мною был разработан небольшой прибор на базе модулей Arduino от amperka.ru.
Опрос счетчиков производится из приложения установленного на смартфоне, при этом связь осуществляется через Bluetooth.
Плата преобразователя.
Плата преобразователя представляет собой набор модулей:
— Iskra Mini — полноценная Arduino-платформа с установленным микроконтроллером ATmega328.
— Плата расширения — Bluetooth-модуль HC-05.
— Плата расширения — Приёмопередатчик RS-485.
— Аккумуляторный элемент питания 9В типа «Крона» (сразу оговорюсь неудачный вариант, лучше использовать аккумулятор для подзарядки телефонов)
— Кнопка включения.
— USB разъем для подачи питания 5В.
— Разъем RJ11 для подключения линии RS485.
Все элементы располагаются на отдельной материнской плате, помещенной в пластмассовом корпусе. При включении преобразователя Bluetooth-модуль HC-05 переходит в режим работы
«прозрачного UART» и ожидает подключения со стороны смартфона.
Схема соединений.
При включении переключателя SW1 напряжение питания 9-12 Вольт подается на электронный
регулятор LM7805 (желательно заменить на иную модель).
С выхода регулятора пониженное напряжение 5 Вольт подается на pin «+5V» Iskra Mini, pin «V» Bluetooth модуля и pin «V» модуля RS485. Отрицательный провод аккумулятора подключается к выводам GND всех модулей. При подаче напряжения питания 5 Вольт через разъем microUSB, все модули запитываются аналогично, минуя регулятор LM7805.
J1 и J2 являются перемычками и должны быть замкнуты в рабочем режиме.
J3 – шести контактный разъем, предназначенный для «прошивки» модуля Iskra Mini с помощью
USB-Serial адаптера.
При загрузке ПО в модуль Iskra Mini, перемычки J1 и J2 должны быть разомкнуты. Конденсатор С1 предназначен для осуществления сброса микроконтроллера в момент выполнения загрузки ПО.
Вывод «RX» Iskra Mini соединяется с выводом «TX» Bluetooth модуля, таким образом
микроконтроллер осуществляет прием данных из Bluetooth.
Вывод «ТX» Iskra Mini соединяется с выводом «RX» Bluetooth модуля, таким образом
микроконтроллер производит передачу команд в Bluetooth.
Вывод «8» Iskra Mini соединяется с выводом «DI» модуля RS485, микроконтроллер производит
передачу команд в линию.
Вывод «11» Iskra Mini соединяется с выводом «RO» модуля RS485, микроконтроллер производит
чтение ответов из линии.
Вывод «5» Iskra Mini соединяется с выводом «In/Out» модуля RS485, микроконтроллер
осуществляет переключение режимов работы приемо-передатчика RS485.
RJ11 – разъем для подключения линии RS485.
Программное обеспечение преобразователя.
После подачи питания на устройство микроконтроллер переходит в режим циклического
ожидания получения команды.
Перечень команд:
ping_ -- после получения этой команды МК в ответ отсылает «ping_».
Используется для тестирования канала связи между смартфоном и преобразователем.
scan_ -- команда сканирования сети на наличие приборов учета от адреса 0 до адреса 254
init_XXX – где ХХХ это сетевой адрес опрашиваемого прибора учета. Надо учитывать, что к сетевым адресам меньше 10 должны добавляться два нуля,
а адресам меньше 100 – один ноль.
После получения этой команды МК в ответ отсылает «init_XXX».
Используется для однократного опроса прибора учета.
Пример.
Прибор учета № 13 02 97 02
Команда: init_002
Ответ: init_002
Прибор учета № 16 24 07 32
Команда: init_032
Ответ: init_032
Прибор учета № 17 14 51 52
Команда: init_152
Ответ: init_152
loop_XXX – После получения этой команды МК в ответ отсылает «loop_XXX».
Используется для непрерывного опроса прибора учета.
stop_ – После получения этой команды МК в ответ отсылает «stop_000». Используется для
остановки режима непрерывного опроса прибора учета.
Варианты ответов микроконтроллера после выполнения команд:
Connect_OK – устройство с указанным сетевым адресом обнаружено в сети.
Connect_FAIL – устройства с этим сетевым адресом не отвечает.
Access_OK – пароль доступа передан, ответ получен.
Access_FAIL – доступ закрыт, пароль не принят.
Алгоритм работы ПО микроконтроллера.
После получения команды init или loop отправляется байтовая последовательность
Net Address, 0x00, 0x00, CRC 1, CRC 2.
В случае нахождения устройства в сети будет аналогичный ответ, после чего сравниваются сетевые адреса — отправленный и полученный.
Проверки CRC в ответном массиве на настоящий момент не производится.
При положительном сравнении в модуль Bluetooth отправляется строка Connect_OK, в противном случае Connect_FAIL.
Далее отправляется запрос доступа с передачей пароля Net Address, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, CRC 1, CRC.
При получении ответа, аналогичного в запросе на обнаружение устройства, в модуль Bluetooth отправляется строка Access_OK, в противном случае Access_FAIL.
После установления связи начинается передача запросов для получения данных согласно
протоколу.
Net Address, 0x00, 0x08, 0x00, CRC 1, CRC -- серийный номер
Net Address, 0x00, 0x08, 0x16, 0x40, CRC 1, CRC -- частота
Net Address, 0x00, 0x08, 0x16, 0x21, CRC 1, CRC -- ток
Net Address, 0x00, 0x08, 0x16, 0x11, CRC 1, CRC -- напряжение
Net Address, 0x00, 0x08, 0x16, 0x00, CRC 1, CRC-- мощность
Net Address, 0x00, 0x08, 0x16, 0x51, CRC 1, CRC -- углы
Net Address, 0x00, 0x05, 0x00, 0x00, CRC 1, CRC -- суммарная энергия прямая + обратная + активная + реактивная.
Ссылаясь на рекомендации производителя, особое внимание следует уделять величине timeout между запросом и ответом, поскольку преобразователь всегда является устройством Master, а опрашиваемые устройства -Slave.
Опыт показывает, что величина timeout и количество корректных ответов зависит от качества и длины линии RS485, поэтому возможно придется вносить корректировки в эти параметры.
#include <SoftwareSerial.h>
//-------- порты для rs 485
#define SSerialRx 11 // Serial Receive pin RO
#define SSerialTx 8 // Serial Transmit pin DI
//-------- инициализация
SoftwareSerial RS485Serial(SSerialRx, SSerialTx); // Rx, Tx
//// линия управления передачи приема
#define SerialControl 5 // RS485 Direction control
/////// флаг приема передачи
#define RS485Transmit HIGH
#define RS485Receive LOW
/////// команды
byte testConnect[] = { 0x00, 0x00 };
byte Access[] = { 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
byte Sn[] = { 0x00, 0x08, 0x00 }; // серийный номер
byte Freq[] = { 0x00, 0x08, 0x16, 0x40 }; // частота
byte Current[] = { 0x00, 0x08, 0x16, 0x21 };// ток
byte Suply[] = { 0x00, 0x08, 0x16, 0x11 }; // напряжение
byte Power[] = { 0x00, 0x08, 0x16, 0x00 };// мощность p
byte PowerQ[] = { 0x00, 0x08, 0x16, 0x08 };// мощность Q
byte PowerS[] = { 0x00, 0x08, 0x16, 0x04 };// мощность S
byte CosF[] = { 0x00, 0x08, 0x16, 0x30 };// cosf
byte Angle[] = { 0x00, 0x08, 0x16, 0x51 }; // углы
byte energyT0[] = { 0x00, 0x05, 0x00, 0x00 };/// суммарная энергия прямая + обратная + активная + реактивная
byte energyT1[] = { 0x00, 0x05, 0x00, 0x01 };/// суммарная энергия прямая + обратная + активная + реактивная
byte energyT2[] = { 0x00, 0x05, 0x00, 0x02 };/// суммарная энергия прямая + обратная + активная + реактивная
byte energyT3[] = { 0x00, 0x05, 0x00, 0x03 };/// суммарная энергия прямая + обратная + активная + реактивная
byte energyT4[] = { 0x00, 0x05, 0x00, 0x04 };/// суммарная энергия прямая + обратная + активная + реактивная
byte response[19];
int byteReceived;
int byteSend;
int netAdr;
int SCAN_YES_NO=0;
int TST_YES_NO=0;
int ACCESS_YES_NO=0;
int ALLOW=0;
void setup() {
RS485Serial.begin(9600);
Serial.begin(9600);
// 5 пин в режим выхода
pinMode(SerialControl, OUTPUT);
// ставим на прием
digitalWrite(SerialControl, RS485Receive);
delay(300);
Serial.println("Start_v2.3\r\n");
}
void loop() {
TST_YES_NO=0;
ACCESS_YES_NO=0;
ALLOW=0;
while (Serial.available())
{ char incomingBytes[15];
if(Serial.available()>0)
{
Serial.readBytes(incomingBytes,10);
}
String getS = String(incomingBytes);
if(getS.substring(0,5) == "init_")
{ ALLOW=1;
Serial.print(getS.substring(0,5)+getS.substring(5,8)+"\r\n");
netAdr=getS.substring(5,8).toInt();
}
if(getS.substring(0,5) == "loop_")
{ ALLOW=1;
SCAN_YES_NO=1;
Serial.print(getS.substring(0,5)+getS.substring(5,8)+"\r\n");
netAdr=getS.substring(5,8).toInt();
}
if(getS.substring(0,5) == "scan_")
{ ALLOW=0;
SCAN_YES_NO=0;
Serial.print(getS.substring(0,5)+"\r\n");
netAdr=1;
while(netAdr < 256)
{
testConnect[0] = netAdr;
response[0] = 0;
send(testConnect, sizeof(testConnect), response);
if(response[0] == netAdr)
{
Serial.print("scan;ok;"+String(netAdr)+";0\r\n");
}
else
{
Serial.print("scan;no;"+String(netAdr)+";0\r\n");
}
netAdr++;
delay(5);
}
netAdr=0;
}
if(getS.substring(0,5) == "stop_")
{ ALLOW=0;
SCAN_YES_NO=0;
Serial.print(getS.substring(0,5)+"000\r\n");
//netAdr=getS.substring(5,8).toInt();
}
if(getS.substring(0,5) == "ping_")
{
Serial.print(getS.substring(0,5)+"\r\n");
}
}
if(ALLOW==1 or SCAN_YES_NO==1)
{
//========================================================================
testConnect[0] = netAdr;
response[0] = 0;
send(testConnect, sizeof(testConnect), response);
if(response[0] == netAdr)
{ TST_YES_NO=1;
Serial.print("Connect_OK\r\n");
}
else
{ TST_YES_NO=0;
Serial.print("Connect_FAIL\r\n");
}
//========================================================================
if(TST_YES_NO==1)
{
delay(100);
Access[0] = netAdr;
response[0] = 0;
send(Access, sizeof(Access), response);
if(response[0] == netAdr)
{ ACCESS_YES_NO=1;
Serial.print("Access_OK\r\n");
}
else
{ ACCESS_YES_NO=0;
Serial.print("Access_FAIL\r\n");
}
}
if(ACCESS_YES_NO==1)
{
String serNum = getSerialNumber(netAdr);
Serial.print("s:"+ serNum +"\r\n");
String ARPower = getEnergyT0(netAdr);
Serial.print("p:"+ ARPower+"\r\n");
String valFreq = getFreq(netAdr);
Serial.print("f:"+ valFreq+"\r\n");
String U = getSuply(netAdr);
Serial.print("u:"+ U+"\r\n");
String A = getCurrent(netAdr);
Serial.print("a:"+ A+"\r\n");
String Angle = getAngle(netAdr);
Serial.print("g:"+ Angle+"\r\n");
String PowerNow = getPowerNow(netAdr);
Serial.print("e:"+ PowerNow+"\r\n");
String Tarif1 = getEnergyT1(netAdr);
Serial.print("t1:"+ Tarif1+"\r\n");
String Tarif2 = getEnergyT2(netAdr);
Serial.print("t2:"+ Tarif2+"\r\n");
String Tarif3 = getEnergyT3(netAdr);
Serial.print("t3:"+ Tarif3+"\r\n");
String Tarif4 = getEnergyT4(netAdr);
Serial.print("t4:"+ Tarif4+"\r\n");
String PQ = getPowerQ(netAdr);
Serial.print("q:"+ PQ+"\r\n");
String PS = getPowerS(netAdr);
Serial.print("c:"+ PS+"\r\n");
String CSF = getCosF(netAdr);
Serial.print("k:"+ CSF+"\r\n");
}
delay(1000);
}
}
String getSerialNumber(int netAdr)
{
String s1,s2,s3,s4;
response[0]=0;
Sn[0] = netAdr;
send(Sn, sizeof(Sn),response);
if((int)response[1] < 10) { s1="0" + String((int)response[1]); } else {s1=String((int)response[1]);}
if((int)response[2] < 10) { s2="0" + String((int)response[2]); } else {s2=String((int)response[2]);}
if((int)response[3] < 10) { s3="0" + String((int)response[3]); } else {s3=String((int)response[3]);}
if((int)response[4] < 10) { s4="0" + String((int)response[4]); } else {s4=String((int)response[4]);}
String n = s1+s2+s3+s4;
return String(response[0])+";"+n;
}
String getPowerNow(int netAdr)
{
response[0]=0;
Power[0] = netAdr;
send(Power, sizeof(Power),response);
long r = 0;
int dir_U0=0;
int dir_U1=0;
int dir_U2=0;
int dir_U3=0;
if((long)response[1]<<16 == 0x40) dir_U0=1;
if((long)response[1]<<16 == 0x80) dir_U0=-1;
r |= (long)response[3]<<8;
r |= (long)response[2];
String U0= String(r * dir_U0);
r = 0;
if((long)response[4]<<16 == 0x40) dir_U1=1;
if((long)response[4]<<16 == 0x80) dir_U1=-1;
r |= (long)response[6]<<8;
r |= (long)response[5];
String U1= String(r * dir_U1);
r=0;
if((long)response[7]<<16 == 0x40) dir_U2=1;
if((long)response[7]<<16 == 0x80) dir_U2=-1;
r |= (long)response[9]<<8;
r |= (long)response[8];
String U2= String(r * dir_U2);
r = 0;
if((long)response[10]<<16 == 0x40) dir_U3=1;
if((long)response[10]<<16 == 0x80) dir_U3=-1;
r |= (long)response[12]<<8;
r |= (long)response[11];
String U3= String(r * dir_U3);
if(response[0] == netAdr) return String(String(response[0])+";"+U0+";"+U1+";"+U2+";"+U3);
else return String("Error");
}
String getPowerQ(int netAdr)
{
response[0]=0;
PowerQ[0] = netAdr;
send(PowerQ, sizeof(PowerQ),response);
long r = 0;
int dir_U0=0;
int dir_U1=0;
int dir_U2=0;
int dir_U3=0;
if((long)response[1]<<16 == 0x40) dir_U0=1;
if((long)response[1]<<16 == 0x80) dir_U0=-1;
r |= (long)response[3]<<8;
r |= (long)response[2];
String U0= String(r * dir_U0);
r = 0;
if((long)response[4]<<16 == 0x40) dir_U1=1;
if((long)response[4]<<16 == 0x80) dir_U1=-1;
r |= (long)response[6]<<8;
r |= (long)response[5];
String U1= String(r * dir_U1);
r=0;
if((long)response[7]<<16 == 0x40) dir_U2=1;
if((long)response[7]<<16 == 0x80) dir_U2=-1;
r |= (long)response[9]<<8;
r |= (long)response[8];
String U2= String(r * dir_U2);
r = 0;
if((long)response[10]<<16 == 0x40) dir_U3=1;
if((long)response[10]<<16 == 0x80) dir_U3=-1;
r |= (long)response[12]<<8;
r |= (long)response[11];
String U3= String(r * dir_U3);
if(response[0] == netAdr) return String(String(response[0])+";"+U0+";"+U1+";"+U2+";"+U3);
else return String("Error");
}
String getPowerS(int netAdr)
{
response[0]=0;
PowerS[0] = netAdr;
send(PowerS, sizeof(PowerS),response);
long r = 0;
int dir_U0=0;
int dir_U1=0;
int dir_U2=0;
int dir_U3=0;
if((long)response[1]<<16 == 0x40) dir_U0=1;
if((long)response[1]<<16 == 0x80) dir_U0=-1;
r |= (long)response[3]<<8;
r |= (long)response[2];
String U0= String(r * dir_U0);
r = 0;
if((long)response[4]<<16 == 0x40) dir_U1=1;
if((long)response[4]<<16 == 0x80) dir_U1=-1;
r |= (long)response[6]<<8;
r |= (long)response[5];
String U1= String(r * dir_U1);
r=0;
if((long)response[7]<<16 == 0x40) dir_U2=1;
if((long)response[7]<<16 == 0x80) dir_U2=-1;
r |= (long)response[9]<<8;
r |= (long)response[8];
String U2= String(r * dir_U2);
r = 0;
if((long)response[10]<<16 == 0x40) dir_U3=1;
if((long)response[10]<<16 == 0x80) dir_U3=-1;
r |= (long)response[12]<<8;
r |= (long)response[11];
String U3= String(r * dir_U3);
if(response[0] == netAdr) return String(String(response[0])+";"+U0+";"+U1+";"+U2+";"+U3);
else return String("Error");
}
String getCosF(int netAdr)
{
response[0]=0;
CosF[0] = netAdr;
send(CosF, sizeof(CosF),response);
long r = 0;
int dir_U0=0;
int dir_U1=0;
int dir_U2=0;
int dir_U3=0;
if((long)response[1]<<16 == 0x40) dir_U0=1;
if((long)response[1]<<16 == 0x80) dir_U0=-1;
r |= (long)response[3]<<8;
r |= (long)response[2];
String U0= String(r * dir_U0);
r = 0;
if((long)response[4]<<16 == 0x40) dir_U1=1;
if((long)response[4]<<16 == 0x80) dir_U1=-1;
r |= (long)response[6]<<8;
r |= (long)response[5];
String U1= String(r * dir_U1);
r=0;
if((long)response[7]<<16 == 0x40) dir_U2=1;
if((long)response[7]<<16 == 0x80) dir_U2=-1;
r |= (long)response[9]<<8;
r |= (long)response[8];
String U2= String(r * dir_U2);
r = 0;
if((long)response[10]<<16 == 0x40) dir_U3=1;
if((long)response[10]<<16 == 0x80) dir_U3=-1;
r |= (long)response[12]<<8;
r |= (long)response[11];
String U3= String(r * dir_U3);
if(response[0] == netAdr) return String(String(response[0])+";"+U0+";"+U1+";"+U2+";"+U3);
else return String("Error");
}
String getAngle(int netAdr)
{
response[0]=0;
Angle[0] = netAdr;
send(Angle, sizeof(Angle),response);
long r = 0;
r |= (long)response[1]<<16;
r |= (long)response[3]<<8;
r |= (long)response[2];
String U1= String(r);
r = 0;
r |= (long)response[4]<<16;
r |= (long)response[6]<<8;
r |= (long)response[5];
String U2= String(r);
r=0;
r |= (long)response[7]<<16;
r |= (long)response[9]<<8;
r |= (long)response[8];
String U3= String(r);
if(response[0] == netAdr) return String(String(response[0])+";"+U1+";"+U2+";"+U3);
else return String("Error");
}
String getCurrent(int netAdr)
{
response[0]=0;
Current[0] = netAdr;
send(Current, sizeof(Current),response);
long r = 0;
r |= (long)response[1]<<16;
r |= (long)response[3]<<8;
r |= (long)response[2];
String U1= String(r);
r = 0;
r |= (long)response[4]<<16;
r |= (long)response[6]<<8;
r |= (long)response[5];
String U2= String(r);
r=0;
r |= (long)response[7]<<16;
r |= (long)response[9]<<8;
r |= (long)response[8];
String U3= String(r);
if(response[0] == netAdr) return String(String(response[0])+";"+U1+";"+U2+";"+U3);
else return String("Error");
}
String getSuply(int netAdr)
{
response[0]=0;
Suply[0] = netAdr;
send(Suply, sizeof(Suply),response);
long r = 0;
r |= (long)response[1]<<16;
r |= (long)response[3]<<8;
r |= (long)response[2];
String U1= String(r);
r = 0;
r |= (long)response[4]<<16;
r |= (long)response[6]<<8;
r |= (long)response[5];
String U2= String(r);
r=0;
r |= (long)response[7]<<16;
r |= (long)response[9]<<8;
r |= (long)response[8];
String U3= String(r);
if(response[0] == netAdr) return String(String(response[0])+";"+U1+";"+U2+";"+U3);
else return String("Error");
}
String getFreq(int netAdr)
{
response[0]=0;
Freq[0] = netAdr;
send(Freq, sizeof(Freq),response);
long r = 0;
r |= (long)response[1]<<16;
r |= (long)response[3]<<8;
r |= (long)response[2];
String fr= String(r);
//return fr;
if(response[0] == netAdr) return String(response[0])+";"+fr;
else return String("Error");
}
String getEnergyT0(int netAdr)
{
response[0]=0;
energyT0[0] = netAdr;
send(energyT0, sizeof(energyT0),response);
if(response[0] == netAdr)
{
long r = 0;
r |= (long)response[2]<<24;
r |= (long)response[1]<<16;
r |= (long)response[4]<<8;
r |= (long)response[3];
String A_plus= String(r);
r=0;
r |= (long)response[6]<<24;
r |= (long)response[5]<<16;
r |= (long)response[8]<<8;
r |= (long)response[7];
String A_minus= String(r);
r = 0;
r |= (long)response[10]<<24;
r |= (long)response[9]<<16;
r |= (long)response[12]<<8;
r |= (long)response[11];
String R_plus= String(r);
r = 0;
r |= (long)response[14]<<24;
r |= (long)response[13]<<16;
r |= (long)response[16]<<8;
r |= (long)response[15];
String R_minus= String(r);
return String(String(response[0])+";"+A_plus+";"+A_minus+";"+R_plus+";"+R_minus);
}
else return String("Error");
}
String getEnergyT1(int netAdr)
{
response[0]=0;
energyT1[0] = netAdr;
send(energyT1, sizeof(energyT1),response);
if(response[0] == netAdr)
{
long r = 0;
r |= (long)response[2]<<24;
r |= (long)response[1]<<16;
r |= (long)response[4]<<8;
r |= (long)response[3];
String A_plus= String(r);
r=0;
r |= (long)response[6]<<24;
r |= (long)response[5]<<16;
r |= (long)response[8]<<8;
r |= (long)response[7];
String A_minus= String(r);
r = 0;
r |= (long)response[10]<<24;
r |= (long)response[9]<<16;
r |= (long)response[12]<<8;
r |= (long)response[11];
String R_plus= String(r);
r = 0;
r |= (long)response[14]<<24;
r |= (long)response[13]<<16;
r |= (long)response[16]<<8;
r |= (long)response[15];
String R_minus= String(r);
return String(String(response[0])+";"+A_plus+";"+A_minus+";"+R_plus+";"+R_minus);
}
else return String("Error");
}
String getEnergyT2(int netAdr)
{
response[0]=0;
energyT2[0] = netAdr;
send(energyT2, sizeof(energyT2),response);
if(response[0] == netAdr)
{
long r = 0;
r |= (long)response[2]<<24;
r |= (long)response[1]<<16;
r |= (long)response[4]<<8;
r |= (long)response[3];
String A_plus= String(r);
r=0;
r |= (long)response[6]<<24;
r |= (long)response[5]<<16;
r |= (long)response[8]<<8;
r |= (long)response[7];
String A_minus= String(r);
r = 0;
r |= (long)response[10]<<24;
r |= (long)response[9]<<16;
r |= (long)response[12]<<8;
r |= (long)response[11];
String R_plus= String(r);
r = 0;
r |= (long)response[14]<<24;
r |= (long)response[13]<<16;
r |= (long)response[16]<<8;
r |= (long)response[15];
String R_minus= String(r);
return String(String(response[0])+";"+A_plus+";"+A_minus+";"+R_plus+";"+R_minus);
}
else return String("Error");
}
String getEnergyT3(int netAdr)
{
response[0]=0;
energyT3[0] = netAdr;
send(energyT3, sizeof(energyT3),response);
if(response[0] == netAdr)
{
long r = 0;
r |= (long)response[2]<<24;
r |= (long)response[1]<<16;
r |= (long)response[4]<<8;
r |= (long)response[3];
String A_plus= String(r);
r=0;
r |= (long)response[6]<<24;
r |= (long)response[5]<<16;
r |= (long)response[8]<<8;
r |= (long)response[7];
String A_minus= String(r);
r = 0;
r |= (long)response[10]<<24;
r |= (long)response[9]<<16;
r |= (long)response[12]<<8;
r |= (long)response[11];
String R_plus= String(r);
r = 0;
r |= (long)response[14]<<24;
r |= (long)response[13]<<16;
r |= (long)response[16]<<8;
r |= (long)response[15];
String R_minus= String(r);
return String(String(response[0])+";"+A_plus+";"+A_minus+";"+R_plus+";"+R_minus);
}
else return String("Error");
}
String getEnergyT4(int netAdr)
{
response[0]=0;
energyT4[0] = netAdr;
send(energyT4, sizeof(energyT4),response);
if(response[0] == netAdr)
{
long r = 0;
r |= (long)response[2]<<24;
r |= (long)response[1]<<16;
r |= (long)response[4]<<8;
r |= (long)response[3];
String A_plus= String(r);
r=0;
r |= (long)response[6]<<24;
r |= (long)response[5]<<16;
r |= (long)response[8]<<8;
r |= (long)response[7];
String A_minus= String(r);
r = 0;
r |= (long)response[10]<<24;
r |= (long)response[9]<<16;
r |= (long)response[12]<<8;
r |= (long)response[11];
String R_plus= String(r);
r = 0;
r |= (long)response[14]<<24;
r |= (long)response[13]<<16;
r |= (long)response[16]<<8;
r |= (long)response[15];
String R_minus= String(r);
return String(String(response[0])+";"+A_plus+";"+A_minus+";"+R_plus+";"+R_minus);
}
else return String("Error");
}
//////////////////////////////////////////////////////////////////////////////////
void send(byte *cmd, int s, byte *response) {
// Serial.print("sending...");
unsigned int crc = crc16MODBUS(cmd, s);
unsigned int crc1 = crc & 0xFF;
unsigned int crc2 = (crc>>8) & 0xFF;
delay(10);
digitalWrite(SerialControl, RS485Transmit); // Init Transceiver
for(int i=0; i<s; i++)
{
RS485Serial.write(cmd[i]);
}
RS485Serial.write(crc1);
RS485Serial.write(crc2);
byte i = 0;
digitalWrite(SerialControl, RS485Receive); // Init Transceiver
delay(200);
if (RS485Serial.available())
{
while (RS485Serial.available())
{
byteReceived= RS485Serial.read(); // Read received byte
delay(10);
response[i++] = byteReceived;
}
}
delay(20);
}
unsigned int crc16MODBUS(byte *s, int count) {
unsigned int crcTable[] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};
unsigned int crc = 0xFFFF;
for(int i = 0; i < count; i++) {
crc = ((crc >> 8) ^ crcTable[(crc ^ s[i]) & 0xFF]);
}
return crc;
}
Программное обеспечение смартфона.
После установки Android приложения, в смартфоне в меню приложений необходимо найти
ярлык с названием «М-230» и запустить программу.
В открывшемся приложении нажать на выпадающее меню рядом с надписью «Команды»
после чего появится список команд.
Поиск устройств – будет произведен поиск доступных Bluetooth устройств и предложено
подключиться.
Проверить связь – команда проверки связи с преобразователем.
Опросить – однократный опрос счетчика по указанному сетевому адресу.
Монитор – постоянный циклический опрос одного устройства по указанному сетевому адресу (online режим).
Остановить – остановка циклического опроса.
Журнал – включается или отключается возможность наблюдать поступление данных в журнале обмена, расположенного в нижней части программы.
> Здесь файл проекта для Android Studio
Комментарии (15)
jackmas Автор
11.10.2017 20:43+2Не совсем так.
На подстанции установлено бывает по 30 — 40 штук счетчиков, они все соединены по rs 485.
Нет надобности с каждого снимать крышку.
Отсоединяем линию 485 от шлюза и начинаем подключаться к счетчикам.
Бывает, что счетчики отваливаются целыми сегментами. Иногда крысы живущие в ЗРУ перекусывают кабель.
Разные бывают случаи.
solalex
11.10.2017 21:11-2Не понял в чем инновация? в том чтобы опрашивать счетчики через ардуино? или в том что глючит Меркурий 228? Подскажу вам очень простой способ опроса, нужен только openwrt/lede совместимый роутер с usb портом, преобразователь usb-rs485, ну и для связи usb-3g модем. Связка работает довольно стабильно, можно опрашивать хоть раз в 5-10 минут и определить какие счетчики вдруг отвалились.
jackmas Автор
11.10.2017 21:22+2У меня достаточно программных и аппаратных средств для дистанционного опроса приборов учета.
Инновации здесь нет, а вот рабочий процесс упрощается.
Смартфон у меня всегда с собой в кармане.
Дополнительно просто беру преобразователь и поехал по объектам.
Кстати, тем что Вы перечислили мы пользуемся, у нас оптика и промышленные ПК установлены на подстанциях.jackmas Автор
11.10.2017 21:33+3Sorry
Хотел написать
тем что Вы перечислили мы НЕ пользуемся, у нас оптика и промышленные ПК установлены на подстанциях.
dionic
12.10.2017 12:02Отличная работа. Осталось только протоколы опроса других счетчиков добавить (СЭТ, Альфа А1800, Нева ...). А так думаю да поудобнее ноутбука будет.
mihara1987
12.10.2017 15:37Скажите, использовали вы когда нибудь для опроса счетчика преобразователь Моха 5130?
jackmas Автор
12.10.2017 15:38Моха 5130 не приходилось использовать, только преобразователи.
mihara1987
12.10.2017 15:47Дело в том, что Меркурий 256 сейчас нет в продаже, скорее всего сняли с производства. Вот приходится искать решения.
Spoyk
Я так и не понял «Зачем это нужно?» Если возникли проблемы со связью в АСКУЭ, то идем и списываем на листик все показания. Значит, чтобы не тратить бумагу, нужно снять пломбы с клеммных крышек, сами крышки, подключить эту штуковину и считать показания со счетчика. Так что ли?