В предыдущих статьях мы познакомились с замечательным внешним 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.


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