EBYTE MA01-AACX2240: программирование цифровых входов

wopn1kj8ybsa-qos0kv50e6d40c.jpeg

В предыдущих статьях мы познакомились с замечательным внешним RS485/ModbusRTU блоком расширения EBYTE MA01-AACX2240 и научились управлять работой его реле. Сегодня мы разберём программирование цифровых входов MA01-AACX2240 и научимся получать с них данные в контроллере на ESP32.

Поскольку это RS485/ModbusRTU блок, то при помощи него можно не только расширить функционал вашего контроллера, но и вынести реле, цифровые и аналоговые входы на значительное расстояние от самого контроллера (десятки и сотни метров), что может быть полезно в различных проектах.

MA01-AACX2240 это довольно функциональное устройство и позволяет не только получать данные с цифровых входов, но и тонко настраивать работу самих этих входов — можно использовать счётчики, устанавливать работу по фронтам сигнала, выбирать метод обнуления счётчиков и т. д. и т. п., обо всём этом мы и поговорим далее…

Регистры, ассоциированные с цифровыми входами


Снова привожу таблицу Modbus RTU регистров MA01-AACX2240, в которой я отметил цветом регистры, ассоциированные с цифровыми входами. Всего таких групп регистров пять:

  • Текущее состояние цифровых входов
  • Счётчики цифровых входов
  • Метод срабатывания для счётчиков
  • Тип обнуления (автоматическое или ручное)
  • Значение для обнуления


3tiu7vtbcjtyld4xbg8edo8grjy.png

Во второй таблице тоже присутствует один регистр, относящийся к цифровым входам. Это т. н. «фильтр», то есть количество импульсов для усреднения показаний счётчиков (по умолчанию — 6). В таблице (видимо ошибочно) этот параметр ассоциирован с аналоговыми входами (AI), а в документации — с цифровыми (что верно).

1t7c5kcce0k74gh1u__tjfxhh1s.png

Итого, суммарно в блоке 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 клеммных колодок обоих устройств.

e7vsiappqdcmzoel4lqwlkhb1yq.jpeg

Цифровые входы

xx46kuii1j5u3jj_yn2nof54vfc.jpeg

В серии блоков EBYTE MA0x-xxCXxxx0 предусмотрено наличие до 4-х цифровых входов. В блоке MA01-AACX2240 присутствуют 2 из них. То или иное количество цифровых и аналоговых входов в различных моделях определяется распайкой соответствующих компонентов на типовой печатной плате серии MA0x-xxCXxxx0.

ms8axem4hsejtitdtwubozjavhi.jpeg


Производитель в официальной документации декларирует следующие параметры цифровых входов MA01-AACX2240:

8zgx1p-4lpmibrmdbgzq6vsvhsg.png


Как вы видите, цифровые входы MA01-AACX2240 являются довольно функциональными и имеют множество параметров и настроек, в этой статье мы разберём программирование двух основных из них: собственно текущего состояния цифровых входов и значения их счётчиков. Ниже приведена выдержка из таблиц с этими параметрами.

okowwns7ybevlaniapfkjga7gi4.png


Из этой таблицы видно, что данные, ассоциированные с цифровыми входами, распределены по двум диапазонам Modbus RTU регистров.

Discrete Inputs. 8-битные регистры, доступные только для чтения (код функции 0×02). Используются в MA01-AACX2240 для представления данных о состоянии DI входов.

Holding Registers. 16-битные регистры, доступные для чтения и записи (коды функций 0×03, 0×06, 0×10). Используются в 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 
#include 

#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;
}


Результат работы нашего скетча (рамкой выделены запросы с замкнутым нулевым (первым) цифровым входом):

4qiowl5bs8yaccofx-dugzoeysi.png

Видно, что все запросы проходят успешно и нет ни одной ошибки проведения операций чтения 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);
}


Результат работы модернизированного скетча — как вы видите, результат тот же, только для его получения используется всего один запрос, вместо двух, что гораздо более эффективно.

apjmi07tqh9-cragcasba_iwaiy.png

Получение значений счётчиков цифровых входов


Теперь попробуем получить значения счётчиков цифровых входов. Здесь нам уже придётся иметь дело с 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 
#include 

#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;
}


Вот результат работы нашего скетча:

00x02tmocuiglavcmmgdlibkpei.png

Здесь хорошо виден принцип работы счётчиков цифровых входов MA01-AACX2240: при поступлении импульсов значение счётчиков возрастает через усредняющее «скользящее окно» в 6 значений, за которое отвечает регистр «All channel filter» (от 1 до 16, по умолчанию 6), который тоже можно изменять и подстраивать под ваши задачи.

Улучшения и прочие DI регистры


Чтобы не загромождать статью, создание скетча, получающего 2 значения счётчиков цифровых входов за один запрос, я оставляю вам в качестве домашнего задания. Если вы внимательно прочитали эту статью, то для вас не составит труда создать скетчи для работы с остальными четырьмя регистрами (параметрами), ассоциированными DI входами блока MA01-AACX2240.

Заключение


Мы уже научились управлять реле и получать данные с цифровых входов блока MA01-AACX2240, далее нас ожидает ещё более захватывающее исследование — работа с аналоговыми входами, в которой нам поможет «волшебная коробочка» — генератор тока и напряжения QH-VISG2-ED.

jbjkagdb_p28lduy-jmke--hcoa.jpeg


p-u9l27ynelxi92bcmdxhu76ma8.png

© Habrahabr.ru