При обслуживании приборов учета Меркурий-230 ART, установленных на удаленных
трансформаторных подстанциях, часто возникает необходимость оперативной проверки работоспособности счетчиков.

Как правило, в местах, где счетчики работают в составе АСКУЭ и соединены в группы по витой
паре 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» и ожидает подключения со стороны смартфона.

image

Схема соединений.


При включении переключателя 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.

image

Программное обеспечение преобразователя.


После подачи питания на устройство микроконтроллер переходит в режим циклического
ожидания получения команды.

Перечень команд:

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, поэтому возможно придется вносить корректировки в эти параметры.

m230.ino
#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» и запустить программу.

В открывшемся приложении нажать на выпадающее меню рядом с надписью «Команды»
после чего появится список команд.

image
Поиск устройств – будет произведен поиск доступных Bluetooth устройств и предложено
подключиться.

Проверить связь – команда проверки связи с преобразователем.

Опросить – однократный опрос счетчика по указанному сетевому адресу.

Монитор – постоянный циклический опрос одного устройства по указанному сетевому адресу (online режим).

Остановить – остановка циклического опроса.

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

image

> Здесь файл проекта для Android Studio

Комментарии (15)


  1. Spoyk
    11.10.2017 20:37

    Я так и не понял «Зачем это нужно?» Если возникли проблемы со связью в АСКУЭ, то идем и списываем на листик все показания. Значит, чтобы не тратить бумагу, нужно снять пломбы с клеммных крышек, сами крышки, подключить эту штуковину и считать показания со счетчика. Так что ли?


  1. jackmas Автор
    11.10.2017 20:43
    +2

    Не совсем так.
    На подстанции установлено бывает по 30 — 40 штук счетчиков, они все соединены по rs 485.
    Нет надобности с каждого снимать крышку.
    Отсоединяем линию 485 от шлюза и начинаем подключаться к счетчикам.
    Бывает, что счетчики отваливаются целыми сегментами. Иногда крысы живущие в ЗРУ перекусывают кабель.
    Разные бывают случаи.


    1. jackmas Автор
      11.10.2017 20:46
      +1

      Да. Забыл добавить.
      «Листиками» мы давно не пользуемся, можно просто снять архив или профиль мощности :)


      1. Stochkas
        12.10.2017 17:37

        =) как листики уже не самый надежный элемент системы?

        хорошая статья. +1


  1. solalex
    11.10.2017 21:11
    -2

    Не понял в чем инновация? в том чтобы опрашивать счетчики через ардуино? или в том что глючит Меркурий 228? Подскажу вам очень простой способ опроса, нужен только openwrt/lede совместимый роутер с usb портом, преобразователь usb-rs485, ну и для связи usb-3g модем. Связка работает довольно стабильно, можно опрашивать хоть раз в 5-10 минут и определить какие счетчики вдруг отвалились.


    1. jackmas Автор
      11.10.2017 21:22
      +2

      У меня достаточно программных и аппаратных средств для дистанционного опроса приборов учета.
      Инновации здесь нет, а вот рабочий процесс упрощается.
      Смартфон у меня всегда с собой в кармане.
      Дополнительно просто беру преобразователь и поехал по объектам.
      Кстати, тем что Вы перечислили мы пользуемся, у нас оптика и промышленные ПК установлены на подстанциях.


      1. jackmas Автор
        11.10.2017 21:33
        +3

        Sorry
        Хотел написать
        тем что Вы перечислили мы НЕ пользуемся, у нас оптика и промышленные ПК установлены на подстанциях.


  1. milast
    12.10.2017 02:59

    LM7805 плохо подходит для этой схемы потому, что при понижении напряжении, разницу между входом и выходом рассеивает в виде тепла.


    1. jackmas Автор
      12.10.2017 09:33

      Согласен с Вами. эту LM конечно же надо заменить.
      Я об этом написал выше.
      Просто под рукой ничего иного не оказалось.


  1. dionic
    12.10.2017 12:02

    Отличная работа. Осталось только протоколы опроса других счетчиков добавить (СЭТ, Альфа А1800, Нева ...). А так думаю да поудобнее ноутбука будет.


  1. mihara1987
    12.10.2017 15:37

    Скажите, использовали вы когда нибудь для опроса счетчика преобразователь Моха 5130?


    1. jackmas Автор
      12.10.2017 15:38

      Моха 5130 не приходилось использовать, только преобразователи.


      1. mihara1987
        12.10.2017 15:47

        Дело в том, что Меркурий 256 сейчас нет в продаже, скорее всего сняли с производства. Вот приходится искать решения.


        1. pseitov
          12.10.2017 16:23

          Пользуюсь Moxa UPort 1150. Ноутбук приходится возить всегда с собой, да.


    1. dionic
      13.10.2017 11:44

      Я использовал moxa 5130. А в чем собственно вопрос?