В предыдущих статьях мы познакомились с замечательным внешним RS485/ModbusRTU блоком расширения EBYTE MA01-AACX2240 и научились управлять работой его реле. Сегодня мы разберём программирование цифровых входов MA01-AACX2240 и научимся получать с них данные в контроллере на ESP32.
Поскольку это RS485/ModbusRTU блок, то при помощи него можно не только расширить функционал вашего контроллера, но и вынести реле, цифровые и аналоговые входы на значительное расстояние от самого контроллера (десятки и сотни метров), что может быть полезно в различных проектах.
MA01-AACX2240 это довольно функциональное устройство и позволяет не только получать данные с цифровых входов, но и тонко настраивать работу самих этих входов — можно использовать счётчики, устанавливать работу по фронтам сигнала, выбирать метод обнуления счётчиков и т. д. и т. п., обо всём этом мы и поговорим далее…
❯ Регистры, ассоциированные с цифровыми входами
Снова привожу таблицу Modbus RTU регистров MA01-AACX2240, в которой я отметил цветом регистры, ассоциированные с цифровыми входами. Всего таких групп регистров пять:
- Текущее состояние цифровых входов
- Счётчики цифровых входов
- Метод срабатывания для счётчиков
- Тип обнуления (автоматическое или ручное)
- Значение для обнуления
Во второй таблице тоже присутствует один регистр, относящийся к цифровым входам. Это т. н. «фильтр», то есть количество импульсов для усреднения показаний счётчиков (по умолчанию — 6). В таблице (видимо ошибочно) этот параметр ассоциирован с аналоговыми входами (AI), а в документации — с цифровыми (что верно).
Итого, суммарно в блоке MA01-AACX2240 присутствуют 6 групп Modbus RTU регистров, относящихся к цифровым входам. Далее мы попробуем подробнее разобраться с их работой.
❯ Тестовое оборудование
В качестве управляющего, как и в предыдущих статьях, мы будем использовать контроллер Lavritech V7.1 Lite на ESP32 с внутренним модулем Lavritech RS485 V1. Перечень оборудования для проведения тестов:
- Клеммы на DIN-рейку ABB MA2,5 для подключения сетевых проводов
- Автоматический выключатель ABB S201 C16 для подачи сетевого питания
- Блок питания на DIN-рейку MEAN WELL 12В 2А для запитки MA01-AACX2240
- Контроллер Lavritech V7.1 Lite с модулем Lavritech RS485 V1
- Блок EBYTE MA01-AACX2240
Устанавливаем всё это на стенд и соединяем соответствующим образом. Контроллер Lavritech V7.1 Lite запитывается от USB разъёма компьютера, а соединение по интерфейсу RS485 между контроллером и блоком MA01-AACX2240 производится при помощи отрезка витой пары на контакты A-A и B-B клеммных колодок обоих устройств.
❯ Цифровые входы
В серии блоков EBYTE MA0x-xxCXxxx0 предусмотрено наличие до 4-х цифровых входов. В блоке MA01-AACX2240 присутствуют 2 из них. То или иное количество цифровых и аналоговых входов в различных моделях определяется распайкой соответствующих компонентов на типовой печатной плате серии MA0x-xxCXxxx0.
Производитель в официальной документации декларирует следующие параметры цифровых входов MA01-AACX2240:
Как вы видите, цифровые входы MA01-AACX2240 являются довольно функциональными и имеют множество параметров и настроек, в этой статье мы разберём программирование двух основных из них: собственно текущего состояния цифровых входов и значения их счётчиков. Ниже приведена выдержка из таблиц с этими параметрами.
Из этой таблицы видно, что данные, ассоциированные с цифровыми входами, распределены по двум диапазонам Modbus RTU регистров.
Discrete Inputs. 8-битные регистры, доступные только для чтения (код функции 0x02). Используются в MA01-AACX2240 для представления данных о состоянии DI входов.
Holding Registers. 16-битные регистры, доступные для чтения и записи (коды функций 0x03, 0x06, 0x10). Используются в MA01-AACX2240 для хранения настроек, текущих значений и т. п.
Программирование получения данных из этих регистров блока MA01-AACX2240 мы разберём далее в этой статье.
❯ Получение данных о текущем состоянии цифровых входов
Данные о текущем состоянии цифровых входов хранятся в 8-битных регистрах Discrete Inputs, доступных только для чтения. В таблице указаны 4 регистра (это для всей серии MA0x-xxCXxxx0), в нашем случае это 2 регистра 0х0000 и 0х0001.
0х0000
0х0001
Для управления блоком MA01-AACX2240, выступающего в качестве Modbus RTU слейва, мы будем использовать Arduino библиотеку ModbusMaster.
Примечание. Это статья из цикла о внешнем Modbus RTU блоке расширения MA01-AACX2240 и тут предполагается, что вы знакомы с предыдущими статьями этого цикла. Поэтому некоторые подробности и пояснения в этой статье я буду опускать.
В скетче определяем адреса регистров двух цифровых входов MA01-AACX2240.
#define ADR_DI_DI0 0
#define ADR_DI_DI1 1
И переменные для хранения текущего состояния цифровых входов.
byte stateDi0 = 0;
byte stateDi1 = 0;
Полный код скетча, который реализует чтения регистров состояния цифровых входов на блоке MA01-AACX2240:
/*
Lavritech V7.1 Lite (RS485 V1)
EBYTE MA01-AACX2240
Modbus RTU DI example
*/
#include <Wire.h>
#include <ModbusMaster.h>
#define RS485_SerialNum 1
#define RS485_RX 16
#define RS485_TX 17
//#define RS485_DE -1
#define MODBUS_BAUD 9600
#define MODBUS_SLAVE_ID 32
#define REQ_DELAY 100
int cntOk = 0;
int cntEr = 0;
uint8_t result;
HardwareSerial RsSerial(RS485_SerialNum);
ModbusMaster RS;
#define ADR_DI_DI0 0
#define ADR_DI_DI1 1
byte stateDi0 = 0;
byte stateDi1 = 0;
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println(F("Start MA01-AACX2240 DI example..."));
RsSerial.begin(MODBUS_BAUD, SERIAL_8N1, RS485_RX, RS485_TX);
RS.begin(MODBUS_SLAVE_ID, RsSerial);
RS.preTransmission(preTransmission);
RS.postTransmission(postTransmission);
} // setup
void preTransmission() {;}
void postTransmission() {;}
void displayInfo() {
Serial.print(F(" ok:")); Serial.print(cntOk);
Serial.print(F("/")); Serial.print(cntEr);
Serial.print(F(" DI:"));
Serial.print(stateDi0);
Serial.print(stateDi1);
Serial.println();
}
word readDi1(word shift) {
word res = 0;
result = RS.readDiscreteInputs(shift, 1);
if (result == RS.ku8MBSuccess) {
res = RS.getResponseBuffer(0);
cntOk++;
} else {
cntEr++;
}
delay(REQ_DELAY);
return res;
}
void loop() {
stateDi0 = readDi1(ADR_DI_DI0);
stateDi1 = readDi1(ADR_DI_DI1);
displayInfo();
delay(1000);
} // loop
Получением данных о состоянии цифровых входов занимается функция readDi1().
word readDi1(word shift) {
word res = 0;
result = RS.readDiscreteInputs(shift, 1);
if (result == RS.ku8MBSuccess) {
res = RS.getResponseBuffer(0);
cntOk++;
} else {
cntEr++;
}
delay(REQ_DELAY);
return res;
}
Результат работы нашего скетча (рамкой выделены запросы с замкнутым нулевым (первым) цифровым входом):
Видно, что все запросы проходят успешно и нет ни одной ошибки проведения операций чтения DI регистров с блока MA01-AACX2240. Обратите внимание, что количество запросов возрастает на 2 за один цикл. Это не очень эффективно и далее мы попробуем решить эту проблему.
❯ Чтение состояния DI за 1 запрос
Функция readDiscreteInputs() позволяет читать сразу несколько последовательно расположенных регистров и при помощи неё можно получить 2 значения за один запрос.
Я не буду приводить здесь полный код скетча чтения состояния 2-х входов за 1 запрос, он практически полностью повторяет предыдущий, приведу только изменения в коде.
Вызываем функцию чтения состояния 2-х входов за 1 запрос, передавая ей в качестве параметра адрес цифрового входа #0 (#1 идёт последовательно за ним).
readDi2(ADR_DI_DI0);
Код самой функции чтения состояния 2-х входов за 1 запрос. Здесь последовательно читаются 2 байта из приёмного буфера.
void readDi2(word shift) {
result = RS.readDiscreteInputs(shift, 2);
if (result == RS.ku8MBSuccess) {
stateDi0 = bitRead(RS.getResponseBuffer(0), 0);
stateDi1 = bitRead(RS.getResponseBuffer(0), 1);
cntOk++;
} else {
cntEr++;
}
delay(REQ_DELAY);
}
Результат работы модернизированного скетча — как вы видите, результат тот же, только для его получения используется всего один запрос, вместо двух, что гораздо более эффективно.
❯ Получение значений счётчиков цифровых входов
Теперь попробуем получить значения счётчиков цифровых входов. Здесь нам уже придётся иметь дело с 16-битными Holding Registers (см. таблицу выше).
Определяем адреса регистров, хранящих текущие значения счётчиков цифровых входов MA01-AACX2240.
#define ADR_HR_COUNT0 2527
#define ADR_HR_COUNT1 2528
И переменные для их хранения.
word count0 = 0;
word count1 = 0;
Далее создаём скетч, реализующий получение значений счётчиков цифровых входов MA01-AACX2240:
/*
Lavritech V7.1 Lite (RS485 V1)
EBYTE MA01-AACX2240
Modbus RTU DI count example
*/
#include <Wire.h>
#include <ModbusMaster.h>
#define RS485_SerialNum 1
#define RS485_RX 16
#define RS485_TX 17
//#define RS485_DE -1
#define MODBUS_BAUD 9600
#define MODBUS_SLAVE_ID 32
#define REQ_DELAY 100
int cntOk = 0;
int cntEr = 0;
uint8_t result;
HardwareSerial RsSerial(RS485_SerialNum);
ModbusMaster RS;
#define ADR_HR_COUNT0 2527
#define ADR_HR_COUNT1 2528
word count0 = 0;
word count1 = 0;
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println(F("Start MA01-AACX2240 DI count example..."));
RsSerial.begin(MODBUS_BAUD, SERIAL_8N1, RS485_RX, RS485_TX);
RS.begin(MODBUS_SLAVE_ID, RsSerial);
RS.preTransmission(preTransmission);
RS.postTransmission(postTransmission);
} // setup
void preTransmission() {;}
void postTransmission() {;}
word readHr1(word shift) {
word res = 0;
result = RS.readHoldingRegisters(shift, 1);
if (result == RS.ku8MBSuccess) {
res = RS.getResponseBuffer(0);
cntOk++;
} else {
cntEr++;
}
delay(REQ_DELAY);
return res;
}
void displayInfo() {
Serial.print(F(" ok:")); Serial.print(cntOk);
Serial.print(F("/")); Serial.print(cntEr);
Serial.print(F(" c0:")); Serial.print(count0);
Serial.print(F(" c1:")); Serial.print(count1);
Serial.println();
}
void loop() {
count0 = readHr1(ADR_HR_COUNT0);
count1 = readHr1(ADR_HR_COUNT1);
displayInfo();
delay(1000);
} // loop
Непосредственно получением значений счётчиков занимается функция readHr1().
word readHr1(word shift) {
word res = 0;
result = RS.readHoldingRegisters(shift, 1);
if (result == RS.ku8MBSuccess) {
res = RS.getResponseBuffer(0);
cntOk++;
} else {
cntEr++;
}
delay(REQ_DELAY);
return res;
}
Вот результат работы нашего скетча:
Здесь хорошо виден принцип работы счётчиков цифровых входов MA01-AACX2240: при поступлении импульсов значение счётчиков возрастает через усредняющее «скользящее окно» в 6 значений, за которое отвечает регистр «All channel filter» (от 1 до 16, по умолчанию 6), который тоже можно изменять и подстраивать под ваши задачи.
❯ Улучшения и прочие DI регистры
Чтобы не загромождать статью, создание скетча, получающего 2 значения счётчиков цифровых входов за один запрос, я оставляю вам в качестве домашнего задания. Если вы внимательно прочитали эту статью, то для вас не составит труда создать скетчи для работы с остальными четырьмя регистрами (параметрами), ассоциированными DI входами блока MA01-AACX2240.
❯ Заключение
Мы уже научились управлять реле и получать данные с цифровых входов блока MA01-AACX2240, далее нас ожидает ещё более захватывающее исследование — работа с аналоговыми входами, в которой нам поможет «волшебная коробочка» — генератор тока и напряжения QH-VISG2-ED.