Эмулятор электросчётчиков
Ряд наших устройств имеет встроенный порт RS-485 для прямого подключения электросчётчиков, имеющих данный интерфейс. Для облегчения тестирования устройств мы разработали небольшую программу-эмулятор. Именно о ней дальше и пойдёт речь.
Данная статья появилась после написания предыдущей про эмулятор ИБП.
Идея эмулятора оказалась очень полезной. С его помощью были отловлены некоторые баги в «прошивках» микроконтроллеров.
В итоге возникла идея реализовать то же самое и с электросчётчиками. Ранее отладку мы вели очень просто, вот на таком вот стенде:
Подключая по очереди каждый из электросчётчиков, разработчик «прошивки» убеждался в корректности считывания данных.
Надо сказать, что современные электросчётчики достаточно навороченные, но мы снимаем с них не всю информацию, а только следующие параметры:
Серийный номер
Напряжение сети (В)
Частота сети (Гц)
Ток нагрузки (А)
Мощность нагрузки (Вт)
Текущее значение тарифов (кВт x ч)
Начинали мы с модели «Меркурий 206», но постепенно список стал расширяться. На текущий момент у нас поддерживаются четыре модели:
Инкотекс-СК «Меркурий 206»
Энергомера «СЕ102»
Энергомера «СЕ102М»
IEK «STAR 104/1»
В будущем наверняка добавим поддержку и других приборов учёта.
Каждый электросчётчик имеет свой протокол работы и свою скорость обмена данными по RS‑485 интерфейсу. Ниже я хочу кратко рассказать об особенностях каждого из них.
Инкотекс-СК «Меркурий 206»
Данный счётчик весьма популярен, по нему несложно найти примеры подключения и примеры кода для считывания информации.
Счётчик подключается к считывающему устройству четырьмя проводами: линии 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»
Этот электросчётчик недорогой, но имеет меньше возможностей. В частности, он не позволяет выдавать значения напряжения сети, частоты, тока и мощности потребления нагрузкой.
Поэтому с данного счётчика мы считываем только значения тарифов и полное значение серийного номера. Полное потому, что адресация счётчика осуществляется по пяти последним цифрам серийника, а сам он имеет длину до 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М»
Данный электросчётчик интересен двумя моментами.
Во-первых, он имеет встроенный источник питания для порта 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»
Данный электросчётчик имеет много схожего с вышеописанными моделями. Он подключается к считывающему устройству четырьмя проводами: линии 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‑порта, через который будет осуществляться обмен. Поддерживаются все указанные выше электросчётчики.
Менять любые параметры можно прямо в процессе работы, и тут же видеть результат:
Скачать эмулятор можно по ссылке.