Эмулятор электросчётчиков

image-loader.svg

Ряд наших устройств имеет встроенный порт RS-485 для прямого подключения электросчётчиков, имеющих данный интерфейс. Для облегчения тестирования устройств мы разработали небольшую программу-эмулятор. Именно о ней дальше и пойдёт речь.

Данная статья появилась после написания предыдущей про эмулятор ИБП.

Идея эмулятора оказалась очень полезной. С его помощью были отловлены некоторые баги в «прошивках» микроконтроллеров.

В итоге возникла идея реализовать то же самое и с электросчётчиками. Ранее отладку мы вели очень просто, вот на таком вот стенде:

c5cc965e8596df9a0dfb432f9640fa88.jpg

Подключая по очереди каждый из электросчётчиков, разработчик «прошивки» убеждался в корректности считывания данных.

Надо сказать, что современные электросчётчики достаточно навороченные, но мы снимаем с них не всю информацию, а только следующие параметры:

  • Серийный номер

  • Напряжение сети (В)

  • Частота сети (Гц)

  • Ток нагрузки (А)

  • Мощность нагрузки (Вт)

  • Текущее значение тарифов (кВт x ч)

Начинали мы с модели «Меркурий 206», но постепенно список стал расширяться. На текущий момент у нас поддерживаются четыре модели:

  • Инкотекс-СК «Меркурий 206»

  • Энергомера «СЕ102»

  • Энергомера «СЕ102М»

  • IEK «STAR 104/1»

В будущем наверняка добавим поддержку и других приборов учёта.

Каждый электросчётчик имеет свой протокол работы и свою скорость обмена данными по RS‑485 интерфейсу. Ниже я хочу кратко рассказать об особенностях каждого из них.

Инкотекс-СК «Меркурий 206»

image-loader.svg

Данный счётчик весьма популярен, по нему несложно найти примеры подключения и примеры кода для считывания информации.

Счётчик подключается к считывающему устройству четырьмя проводами: линии A, B интерфейса RS-485, питание и «земля».

Скорость обмена данными 9600 бит/сек без контроля чётности.

Напряжение питания может быть в диапазоне от 6 до 12В.

Имеются модификации со встроенным источником питания: Меркурий 206 PRSN и Меркурий 206 PRSNO.

С этим счётчиком была связана одна проблема. Изначально мы питали его от напряжения 12В, от которого запитывался и наш контроллер. Но в процессе эксплуатации несколько счётчиков вышло из строя. Точнее они работали, но обмен данными по RS-485 пропал. Мы вскрыли несколько из них и обнаружили что там вышел из строя сам драйвер. Он питался через стабилизатор, выполненный на резисторе и стабилитроне. Так вот стабилитрон просто вышел из строя и всё напряжение шло непосредственно на драйвер.

Для исключения подобных случаев мы сделали у себя в контроллере отдельный выход на напряжение 10В для запитывания электросчётчиков. После этого было выпущено более 10 тыс. таких изделий и проблем с ними в этой части больше не было.

Теперь надо немного остановиться на протоколе обмена данного электросчётчика.
Он довольно простой и в чём-то похож на Modbus RTU.
Описание протокола можно найти здесь.

Форматы запросов и ответов совпадают.

Название поля

Условное обозначение

Длина поля (байт)

Примечание

Сетевой адрес

ADDR

4

Серийный номер счётчика (передаётся старшим байтом вперёд)

Команды

CMD

1

Двоичный код команды

Данные

­-

0…17

Может отсутствовать (в зависимости от типа и назначения пакета)

Контрольная сумма

CRC16

2

2-х байтовый̆ циклический̆ избыточный̆ код, вычисляемый̆ по всем предшествующим байтам данного пакета (передаётся младшим байтом вперёд)

CRC16 рассчитывается при помощи полинома 0xA001 и стартового значения 0xFFFF, точно так же, как в протоколе Modbus RTU.

Приведу пример функций расчёта контрольной суммы, которые используются у нас в проектах:

typedef struct
{
    uint16_t CRC;
    uint16_t Polynom;
} TCRC16;

void InitCRC16(TCRC16 *pCRC, uint16_t pInitValue, uint16_t pPolynom)
{
  pCRC->CRC = pInitValue;
  pCRC->Polynom = pPolynom;
}

void UpdateCRC16(TCRC16 *pCRC, uint8_t pValue)
{
  pCRC->CRC ^= pValue;

  for (uint8_t i = 8; i > 0; i--)
    if (pCRC->CRC & 1)
      pCRC->CRC = (pCRC->CRC >> 1) ^ pCRC->Polynom;
    else
      pCRC->CRC >>= 1;
}

К сожалению, в данном протоколе обмена отсутствует какой-либо префикс начала пакета данных, поэтому определить его можно только по времени отсутствия обмена данными на линии, необходимого для передачи 5–6 байт.

Мы используем следующие команды для этого счётчика:

0×27 (чтение тарифов) — в ответ получаем пакет из 16 байт данных по 4 байта на каждый тариф. При этом данные передаются в двоично-десятичном формате с точностью до 10 Вт.

Запрос:
00 00 04 D2 27 79 7B 00 00 04 D2 – адрес счётчика 27 – команда 79 7B – CRC16

Ответ:
00 00 04 D2 27 00 02 27 50 00 02 27 50 00 02 27 50 00 02 27 50 A5 FB
00 00 04 D2 – адрес счётчика
27 – команда
00 02 27 50 – тариф №1 (227,5 кВт × ч)
00 02 27 50 – тариф №2 (227,5 кВт × ч)
00 02 27 50 – тариф №3 (227,5 кВт × ч)
00 02 27 50 – тариф №4 (227,5 кВт × ч)
A5 FB – CRC16

0×63 (чтение U, I, P) — получаем значения сетевого напряжения, тока и мощности потребления. Данные также передаются в двоично-десятичном формате.

Величина напряжения передаётся с коэффициентом 10, тока — 100, мощности — без коэффициента.

Запрос:
00 00 04 D2 63 79 48
00 00 04 D2 – адрес счётчика
63 – команда
79 48 – CRC16

Ответ:
00 00 04 D2 63 23 00 01 50 00 01 00 A5 FB
00 00 04 D2 – адрес счётчика
63 – команда
23 00 – напряжение (230,0 В)
01 50 – ток нагрузки (1,5 А)
00 01 00 – потребляемая мощность (100 Вт)
A5 FB – CRC16

0×81 (F, текущего тарифа и битовых флагов) — из ответа данной команды нас интересуют только первые два байта, представляющие частоту в двоично-десятичном формате. Частота передаётся с коэффициентом 100.

Запрос:
00 00 04 D2 81 F9 01
00 00 04 D2 – адрес счётчика
81 – команда
09 01 – CRC16

Ответ:
00 00 04 D2 81 50 50 3A 00 00 00 00 00 00 CC A4
00 00 04 D2 – адрес счётчика
81 – команда
50 50 – частота (50,5 Гц)
3A – битовые флаги (0 бит – неравенство токов, 1 бит – обратная энергия, остальные биты не используются).
00 00 00 00 00 00 – резерв
CC A4 – CRC16

Энергомера «СЕ102»

image-loader.svg

Этот электросчётчик недорогой, но имеет меньше возможностей. В частности, он не позволяет выдавать значения напряжения сети, частоты, тока и мощности потребления нагрузкой.

Поэтому с данного счётчика мы считываем только значения тарифов и полное значение серийного номера. Полное потому, что адресация счётчика осуществляется по пяти последним цифрам серийника, а сам он имеет длину до 16 десятичных цифр.

Счётчик подключается к считывающему устройству также четырьмя проводами: линии A, B интерфейса RS-485, питание и «земля».

Скорость обмена данными 9600 бит/сек без контроля чётности.

Напряжение питания, указанное в документации: 12В.

Внутри для питания драйвера RS-485 используется классический линейный стабилизатор 78L05, проблем с выходом из строя модуля связи на данных счётчиках зафиксировано не было. Реально питать его можно напряжением от 7 до 18В.

Теперь рассмотрим протокол обмена. Тут он более навороченный.

Описание его находится здесь.

Формат запроса и ответа следующий:

Условное обозначение

Длина поля (байт)

Примечание

END

1

Байт начала пакета

OPT

1

Тип адресации и контрольной суммы

AddrD

2

Адрес электросчётчика (последние 5 цифр серийного номера)

AddrS

2

Адрес устройства, отправившего запрос. В нашем случае адрес контроллера.

PAL

N

Структура данных

CRC8

1

Контрольная сумма

END

1

Байт окончания пакета

END (0xС0) — флаг, обозначающий начало и конец пакета. Если в пакете встречается байт с кодом, идентичным END, то такой байт замещается на последовательность из 2 байтов: 0xDB и 0xDC. Специальный символ 0xDB называется ESC-символом. Если в пакете встречается байт с кодом ESC-символа, то он замещается двухбайтовой последовательностью 0xDB, 0xDD.

OPT (0×48): включена 16-битная адресация и 8-битный циклический код.

AddrD — адрес назначения (счётчика), передаётся младшим байтом вперёд.

AddrS (253) — адрес источника, передаётся младшим байтом вперёд.

CRC8 — контрольная сумма пакета, рассчитывается до применения ESC-символов для всех байтов, исключая первый и последний END. Вычисление производится с использованием полинома 0xB5 и стартового значения 0×00.

Внутренняя структура сообщения PAL при запросе следующая:

Условное обозначение

Длина поля (байт)

Примечание

Passw

4

Пароль доступа

Serv

1

Сервисное поле

CmdH

1

Старший байт кода команды

CmdL

1

Младший байт кода команды

Data

N

Данные

Passw (777777) — пароль доступа к информации.

Формат поля Serv:

Условное обозначение

Бит

Примечание

Direct

7

Тип обмена (1 — запрос, 0 — ответ)

ClassAccess

6…4

Класс доступа к счётчику (0×05)

MessageLen

3…0

Количество байт, которые помещены в данные

CmdH, CmdL — старший и младший байты кода команды счётчику.

Data — данные.

Внутренняя структура сообщения PAL при ответе повторяет структуру запроса, но только адреса AddrD и AddrS меняются местами, а поле пароля отсутствует:

Условное обозначение

Длина поля (байт)

Примечание

Serv

1

Сервисное поле

CmdH

1

Старший байт кода команды

CmdL

1

Младший байт кода команды

Data

N

Данные

Расчёт контрольной суммы осуществляется следующими функциями:

uint8_t emCRC8;
uint8_t emPolynom8;

void emUpdateCRC8(uint8_t pValue)
{
  emCRC8 ^= pValue;

  for (uint8_t i = 8; i > 0; i--)
  {
    if ((emCRC8 & 128) > 0)
      emCRC8 = (uint8_t) (emCRC8 << 1) ^ emPolynom8;
    else
      emCRC8 <<= 1;
  }
}

Мы используем следующие команды для данного счётчика:

0×0130 (чтение тарифа). В запросе передаём два значения: индекс глубины опроса (0 — текущие значения) и номер тарифа (1…5).

Запрос:
C0 48 D2 04 FD 00 31 DE 0B 00 D2 01 30 00 02 33 C0
C0 – END
48 – OPT
D2 04 – адрес счётчика 1234
FD 00 – адрес контроллера 253
31 DE 0B 00 – Passw 777777
D2 – запрос с двумя байтами в пакете
01 – AddrH
30 – AddrL
00 – текущие значения
02 – тариф №2
33 – CRC8
C0 – END

Ответ:
C0 48 FD 00 D2 04 57 01 30 10 08 21 DE 58 00 00 94 C0
C0 – END
48 – OPT
FD 00 – адрес контроллера 253
D2 04 – адрес счётчика 1234
57 – ответ с семью байтами в пакете
01 – AddrH
30 – AddrL
10 08 21 – текущая дата в двоично-десятичном формате
DE 58 00 00 – значение тарифа №2 (227,5 кВт × ч)
94 – CRC8
C0 – END

0×011A (чтение серийного номера). Серийник считывается за два запроса с передачей одного байта, который указывает какую часть серийного номера считать, младшую — 0 или старшую — 1. Каждая часть состоит из восьми символов.

Запрос:
C0 48 D2 04 FD 00 31 DE 0B 00 D1 01 1A 01 CB C0
C0 – END
48 – OPT
D2 04 – адрес счётчика 1234
FD 00 – адрес контроллера 253
31 DE 0B 00 – Passw 777777
D1 – запрос с одним байтом в пакете
01 – AddrH
1A – AddrL
01 – старшая часть
CB – CRC8
C0 – END

Ответ:
C0 48 FD 00 D2 04 58 01 1A 30 30 30 30 30 30 30 00 E0 C0
C0 – END
48 – OPT
FD 00 – адрес контроллера 253
D2 04 – адрес счётчика 1234
58 – ответ с восемью байтами в пакете
01 – AddrH
1A – AddrL
30 30 30 30 30 30 30 00 – старшая часть серийного номера (0000000)
E0 – CRC8
C0 – END

Запрос:
C0 48 D2 04 FD 00 31 DE 0B 00 D1 01 1A 00 7E C0
C0 – END
48 – OPT
D2 04 – адрес счётчика 1234
FD 00 – адрес контроллера 253
31 DE 0B 00 – Passw 777777
D1 – запрос с одним байтом в пакете
01 – AddrH
1A – AddrL
00 – младшая часть
7E – CRC8
C0 – END

Ответ:
C0 48 FD 00 D2 04 58 01 1A 34 33 32 31 30 30 30 30 DB DD C0
C0 – END
48 – OPT
FD 00 – адрес контроллера 253
D2 04 – адрес счётчика 1234
58 – ответ с восемью байтами в пакете
01 – AddrH
1A – AddrL
30 30 30 30 30 30 30 00 – младшая часть серийного номера (0000000)
DB DD – CRC8 (0xDB с учётом ESC-последовательности)
C0 – END

Энергомера «СЕ102М»

image-loader.svg

Данный электросчётчик интересен двумя моментами.

Во-первых, он имеет встроенный источник питания для порта RS-485, вследствие чего, подключение к контроллеру осуществляется всего двумя проводами — A и B.

Скорость обмена данными 9600 бит/сек, 7 бит данных, контроль чётности.

Во-вторых, он может работать без адресации, поэтому не нужно указывать никаких цифр серийного номера, чтобы снимать с него данные.

Протокол обмена этого электросчётчика соответствует ГОСТ Р МЭК 61107–2001 «Обмен данными при считывании показаний счетчиков, тарификации и управлении нагрузкой». Он достаточно навороченный, но в стандарте описан весьма тщательно, поэтому полностью приводить здесь я его не буду, а сосредоточусь лишь на основных моментах и примерах запросов.

Описание команд применительно к данному счётчику приведено здесь.

В первую очередь надо отметить, что протоколом предусмотрены следующие управляющие символы:

ACK (0×06) — подтверждение
SOH (0×01) — начало заголовка
STX (0×02) — начало текста
ETX (0×03) — конец текста

Хотя протокол и основан на системе «запрос-ответ», но единого формата пакетов тут нет. Более того, одни запросы и ответы могут содержать контрольную сумму, а другие передаются без неё!

Контрольную сумму далее будем обозначать как BCC. Она рассчитывается как сумма всех байтов за исключением начального SOH или STX.

Чтобы что-то считать со счётчика в первую очередь нужно установить связь специальной командой, затем отправить нужные команды на считывание параметров, после чего другой командой завершить обмен.

Разберём эти команды подробнее. В примерах далее пробелы будут разделять символы команд для наглядности, реально они не передаются. Специальным символы будут указываться своими аббревиатурами, а различные числовые значения в 16-ричном формате с префиксом 0x:

Начало передачи данных без адреса. С это команды начинается обмен:
Запрос:
/?! 0x0D 0x0A

Ответ:
/EKT5CE102Mv01 0x0D 0x0A

Подтверждение начала обмена:
Запрос:
ACK 051 0x0D 0x0A
0 – нормальная процедура протокола
5 – скорость 9600 бод
1 – режим программирования.

Ответ:
SOH P0 STX (1234) ETX BCC
P – команда пароля
0 – без пароля
(1234) – серийный номер в скобках

Чтение величины напряжения:
Запрос:
SOH R1 STX VOLTA() ETX BCC
R – команда чтения
1 – чтение в формате ASCII
VOLTA() – чтение напряжения

Ответ:
STX VOLTA(230.1) 0x0D 0x0A ETX BCC
VOLTA(230.1) – величина напряжения в скобках

Чтение величины тока нагрузки, мощности и частоты сети
Данные команды аналогичный команде чтения напряжения за исключением текстового названия кода команды:
CURRE () — ток нагрузки
POWEP () — мощность нагрузки
FREQU () — частота электрической сети

Чтение значения тарифов:
Запрос:
SOH R1 STX ET0PE(02) ETX BCC
ET0PE – команда нужного тарифа. Нумерация начинается с 02. Индекс 01 предназначен для считывания суммарного значения всех тарифов

Ответ:
STX ET0PE(118.74) 0x0D 0x0A ETX BCC
ET0PE(118.74) – значение тарифа в скобках

Завершение обмена:
Запрос:
SOH B0 ETX BCC
B – команда выхода
0 – признак завершения обмена

Ответа на данную команду нет.

IEK «STAR 104/1»

image-loader.svg

Данный электросчётчик имеет много схожего с вышеописанными моделями. Он подключается к считывающему устройству четырьмя проводами: линии A, B интерфейса RS-485, питание и «земля».

Скорость обмена данными 9600 бит/сек без контроля чётности.

Напряжение питания может быть в диапазоне от 10 до 24В.

Для считывания данных следует обязательно использовать адресацию по пяти последним цифрам серийного номера.

Протокол обмена этого электросчётчика собственный. К сожалению, в открытом доступе его описание мне найти не удалось, поэтому мы обратились к производителю. Всего через неделю после запроса нам на электронную почту прислали текстовый документ с подробным описанием протокола. Никаких NDA подписывать не потребовалось. Даже никто не уточнил зачем он нам нужен.

Итак, разберём необходимые нам команды.

Формат запроса и ответа следующий:

Условное обозначение

Длина поля (байт)

Значение

Примечание

Start1

1

0×73

Start2

1

0×55

Parameters + Len

1

С V0 D L4 L3 L2 L1 L0

Для этих полей вычисляется CRC8

Это подлежит байтстаффингу

Reserved

1

0×00

Address Destination

2

Address Source

2

Command

1

Password / Status

4

Data

0…31

CRC8

1

Stop

1

0×55

Start1, Start2: начало пакета.

Stop: конец пакета.

Parameters:
бит C — кодирование данных (1 — данные закодированы, 0 — нет).
бит V0 — вид структуры пакета (0 — пакет для устройств с малыми вычислительными мощностями (счётчиков), 1 — пакет для устройств с достаточными вычислительными мощностями (УСПД).
бит D — направление движения пакета (1 — запрос к устройству, 0 — ответ устройства).
биты L4…L0 –длина поля Data.

Address Destination: адрес устройства.

Address Source: адрес программы опроса, всегда равен 0xFFFF.

Command: код команды.

Password / Status: пароль для пакета запроса / статус для пакета ответа. Пароль заполняется только для пакетов, которые адресованы устройству. Когда устройство отвечает на запрос, оно заполняет это поле статусными значениями, это могут быть флаги уведомления об авариях в устройстве и другие информационные идентификаторы, к примеру код ошибки выполнения команды.

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

Контрольная сумма CRC8 вычисляется с использованием полинома 0хA9 и стартового значения 0×00.

Пакет перед отправкой подлежит обработке механизмом байтстаффинга, а при приёме — обратного байтстаффинга.

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

При байтстаффинге анализируется каждый байт, и:
0×55 заменяется на 0×73 0×11
0×73 заменяется на 0×73 0×22

При обратном байтстаффинге проводится противоположная замена.

ИТОГИ

Рассмотрев эти четыре модели электросчётчиков можно сделать вывод о большом разнообразии протоколов обмена. Мне больше всего понравились реализации от Инкотекс и IEK. Они простые, бинарные, без лишних заморочек.

Жаль только, что производители не пришли ещё к какой-то единой реализации.

А пока на данном эмуляторе мы тестируем все наши изделия во всём возможном диапазоне напряжений, токов, частоты и мощности.

Программа эмулятора представляет из себя экран с настройками и выбором COM‑порта, через который будет осуществляться обмен. Поддерживаются все указанные выше электросчётчики.

image-loader.svg

Менять любые параметры можно прямо в процессе работы, и тут же видеть результат:

image-loader.svg

Скачать эмулятор можно по ссылке.

© Habrahabr.ru