Дружим WirenBoard с Arduino (slave) по ModBus

Текущий скетч:

/*
 * 
  WB some standard registers add
  v1.1
  Тут добавлены coil 0..5 включающие gpio. Перечислены ниже как "Выходы для Coil 00-05"  
*/

//Для работы с EEPROM
#include <EEPROM.h>

//https://github.com/emelianov/modbus-esp8266
#include <ModbusRTU.h>

//Объект md
ModbusRTU mb;

#define RXTX_PIN 12

#define LED_BUILTIN 13

//Адреса в EEPROM для хранения регистров
#define h80eeprom 0 //Modbus address
#define h6Eeeprom 1 //110 speed port
#define h6Feeprom 2 //111 parity bit
#define h70eeprom 3 //112 stop bit



//Выходы для Coil 00-05
#define Coil00  A4
#define Coil01  A3
#define Coil02  A2
#define Coil03  A1
#define Coil04  A0
#define Coil05  LED_BUILTIN

//Массив для хранения выходов, связанных с coil
static uint8_t coilOutList[] = {Coil00, Coil01, Coil02, Coil03, Coil04, Coil05};

//тут описываем прототипы функций. Чтобы при создании структуры уже были.
void h80setup(void);
uint16_t h80set(TRegister* reg, uint16_t val);

void h6Esetup(void); //скорость.
uint16_t h6Eset(TRegister* reg, uint16_t val);

void h6Fsetup(void); //четность
uint16_t h6Fset(TRegister* reg, uint16_t val);

void h70setup(void); // стопбиты
uint16_t h70set(TRegister* reg, uint16_t val);

void i68setup(void); // время работы
uint16_t i68set(TRegister* reg, uint16_t val);

//User function
void c00setup(void); // coil relay output
uint16_t c00set(TRegister* reg, uint16_t val);
uint16_t c00get(TRegister* reg, uint16_t val);

void hc8setup(void); // просто регистр
//void h111setup(void);
//void h112setup(void);

/*
void ftest80(int);
void ftest104(int);
*/

typedef void (* funcPtr) (); 
// Теперь создадим массив длиной три 
// и сложем в него указатели на функции.
// Массив имеет тип, который мы только что создали
//FuncPtr funcArray[2] = {h80setup, pf104};

//Все эти функции будут вызваны при запуске
//Сюда дописываем и свои тоже
const funcPtr funcArr[] = {
  h80setup,
  h6Esetup,
  h6Fsetup,
  h70setup,
  i68setup,
  //User function
  c00setup,
  hc8setup
  };

  //User function
  //c00setup,
  //hc8setup

/*
//Структура
struct structRegistersModbus {
  const byte rtype;             //Тип регистра. 1:coil 2:input 3:holding
  const int address;           //Адрес регистра
  const byte param2;           //
  const byte param3;           //
  const void (* FuncPtr) (int);//Указатель на функцию, обрабатывающую регистр
};

const structRegistersModbus  allRegs[] = {
  {3, 80,  33,  0, &f80 },
  {1, 104, 34,  0, &f104 },
  {2, 1,   35,  35, &f80 },
  {3, 2,   36,  36, &f80 },
  {4, 3,   37,  37, &f80 },
  {5, 4,   38,  38, &f80 },
  {1, 104, 34,  0, &f104 },
  {2, 1,   35,  35, &f80 },
  {3, 2,   36,  36, &f80 },
  {4, 3,   37,  37, &f80 },
  {5, 4,   38,  38, &f80 },
  {1, 104, 34,  0, &f104 },
  {2, 1,   35,  35, &f80 },
  {3, 2,   36,  36, &f80 },
  {4, 3,   37,  37, &f80 },
  {5, 4,   38,  38, &f80 },
  {1, 104, 34,  0, &f104 },
  {2, 1,   35,  35, &f80 },
  {3, 2,   36,  36, &f80 },
  {4, 3,   37,  37, &f80 },
  {5, 4,   38,  38, &f80 },

};
*/

/*
//Структура
struct structTest {
  const byte type; 
  const int address;
  const byte param2;
  const byte param3;
  const void (* FuncPtr) (int);
};

const structTest Test[] PROGMEM = {
  {0, 80, 33,  0, &ftest80 },
  {1, 104, 34,  0, &ftest104 },
  {2, 0, 0,  35, 0 },
  {0, 0, 0,  35, 0 },
  {1, 0, 0,  37, 0 },
  {2, 0, 0,  38, 0 },
};
*/

/*
//const structRegistersModbus menu[] PROGMEM = {
//  {3, 0, 0,  0, pf80 },
//  {3, 0, 0,  0, pf80 },
//};
*/

byte modbusNeedStart = 0; //флаг необходимости перезапуска Modbus

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  
  Serial.begin(9600);
  Serial.println("Start!");

  //переключаем GPIO в выходы 2do - в цикле надо
  //pinMode(LED_BUILTIN, OUTPUT);
  //for (byte i=0; i<(sizeof(coilOutList)/sizeof(coilOutList[0]); i++){
    for (byte i=0; i < (sizeof(coilOutList)/sizeof(coilOutList[0])); i++){
    pinMode(coilOutList[i], OUTPUT);
  }
  
  //Считаем количество элементов массива.
  //const int funcArrLenght = sizeof(funcArr)/sizeof(funcArr[0]);
  Serial.print("funcArrLenght=");
  Serial.println(sizeof(funcArr)/sizeof(funcArr[0]));
  //тут в цикле пройдем по всем элементам funcArr и запустим функцию.

  for (byte i=0; i < (sizeof(funcArr)/sizeof(funcArr[0])); i++){
    Serial.print(i);
    Serial.println("start");
    delay(40);
    funcArr[i]();
    Serial.println("startED");
    //Serial.print((byte)allRegs[i].rtype);
    //Serial.print("   ");
    //Serial.println((int)allRegs[i].address);

  }


//Настройка канала A таймера0
OCR0A = 128; //Устанавливаем регистр совпадения
TIMSK0 |= (1 << OCIE0A);  // включение прерываний по совпадению для 0 таймера, канал A
  
//Serial.println("STARTED!");delay (100);
}



//uint32_t i = 65280;
//byte *ptri = (byte*)&i;
//  Serial.print("byte0=: ");
//  Serial.println(*ptri);
//  Serial.print("byte1=: ");
//  Serial.println(*(ptri+1));

// the loop function runs over and over again forever
void loop() {
  if (modbusNeedStart){
    //Serial.println("modbusNeedStart!!!");
    modbusStart();//Запускаем Modbus
    modbusNeedStart = 0;
    //Serial.print("mb.Hreg(128) "); Serial.println(mb.Hreg(128));
  }
  mb.task();


  
  //digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(50);                       // wait
  //digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(50);     // wait
//  Serial.print("mb.Hreg(128) "); Serial.println(mb.Hreg(128));
  
/*
  if (Serial.available()){
    if (Serial.read() == '1'){
      // Обращаемся к текущему элементу массива,
      // тем самым делая вызов функции по очереди 
        //funcArray[i]();
      // Это тернарный оператор. Он проверяет на истинность
      // выражение после знака = и перед знаком ?.
      // если оно истинно, то переменной присваивается значение
      // после знака ?, если ложно - то значение после знака :.
      // здесь он нужен, чтобы листать индекс массива от 0 до 
      // длины массива минус один.  
      i = (i < 1) ? i+1 : 0;
    }
  }
  */

  //Serial.println(pgm_read_byte(allRegs[0].type));
//  Serial.print("type: ");
//  Serial.println(allRegs[0].type);
//  Serial.print("FuncPtr: ");
//  allRegs[0].FuncPtr(1);
//  allRegs[1].FuncPtr(1);
  //allRegs[0].FuncPtr();
  //Test[0].FuncPtr(15);
  //Test[1].FuncPtr(22);
}





// А вот и описания функций.
//
//Адрес 128 **************
void h80setup(){
  //Serial.println("enter h80setup");
  //Надо прочитать из EEPROM байт.
  byte readByte = eeRead(h80eeprom, 1);
  if (0 == readByte){//Если прочитанное равно нулю
    eeWrite(h80eeprom, 1, 1); //Записываем в EEprom 1
    readByte = 1;
  };
  //Serial.print("address=");Serial.println(readByte);
  mb.addHreg(128); //Создаем holding регистр с адресом 128
  mb.Hreg(128, readByte);//Устанавливаем значение
  mb.onSetHreg(128, h80set); // Add callback on Hreg 128 value set
  modbusNeedStart=1;//нужно перезапустить Modbus
}
uint16_t h80set(TRegister* reg, uint16_t val){
  //Serial.println("enter h80set");
  //Надо прочитать из EEPROM байт.
  byte readByte = eeRead(h80eeprom, 1);
  if (0!=val || 248>val){//Если прочитанное НЕ равно новому, не ноль и в диапазоне адресов
    eeWrite(h80eeprom, 1, val); //Записываем в EEprom 1
    modbusNeedStart=1;//нужно перезапустить Modbus
    return val;
  }
  else{
    return readByte; //Если значение неверно - просто оставляем старое
  }
}

//Скорость 110 **************
void h6Esetup(){
  //Serial.println("enter h6Esetup");
  //Надо прочитать из EEPROM байт.
  byte readByte = eeRead(h6Eeeprom, 1);
//  Serial.print("readByte");Serial.println(readByte);
  mb.addHreg(110); //Создаем holding регистр с адресом 110
  uint16_t speedReg = 0;
  switch(readByte){
    case 1:
      speedReg = 12;
      break;
    case 2:
      speedReg = 24;
      break;
    case 4:
      speedReg = 48;
      break;
    case 9:
      speedReg = 96;
      break;
    case 19:
      speedReg = 192;
      break;
    case 57:
      speedReg = 576;
      break;
    case 115:
      speedReg = 1152;
      break;
  }
  if (0 == speedReg){//Если прочитанное ни с чем не совпало
    eeWrite(h6Eeeprom, 1, 9); //Записываем в EEprom 9 (9600)
    speedReg = 96;
  }
  mb.Hreg(110, speedReg);//Устанавливаем значение
  mb.onSetHreg(110, h6Eset); // Add callback on Hreg 128 value set
  modbusNeedStart=1;//нужно перезапустить Modbus
}
uint16_t h6Eset(TRegister* reg, uint16_t val){
  //Serial.println("enter h6Eset");
  uint8_t toeeprom = 0;
  //Serial.print("val=");Serial.println(val);
  switch(val){
    case 12:
      toeeprom = 1;
      break;
    case 24:
      toeeprom = 2;
      break;
    case 48:
      toeeprom = 4;
      break;
    case 96:
      toeeprom = 9;
      break;
    case 192:
      toeeprom = 19;
      break;
    case 576:
      toeeprom = 57;
      break;
    case 1152:
      toeeprom = 115;
      break;
  }
  if (0 == toeeprom){//Если записанное не совпало со списком скоростей - то ой.
    eeWrite(h6Eeeprom, 1, toeeprom); //Записываем в EEprom 1
    modbusNeedStart=1;//нужно перезапустить Modbus
    return val;
  }
  else{
    return mb.Hreg(110); //Если значение неверно - просто оставляем старое
  }
}

//Четность 111 **************
void h6Fsetup(){
  //Serial.println("enter h6Esetup (111)");
  //Надо прочитать из EEPROM байт.
  byte readByte = eeRead(h6Feeprom, 1);
  //Serial.print("readByte h6Feeprom");Serial.println(readByte);
  //Serial.print("h6Feeprom=");Serial.println(h6Feeprom);
  //Serial.print("readByte 0 ");Serial.println(eeRead(0, 1));
  //Serial.print("readByte 1 ");Serial.println(eeRead(1, 1));
  //Serial.print("readByte 2 ");Serial.println(eeRead(2, 1));
  //Serial.print("readByte 3 ");Serial.println(eeRead(3, 1));
  
  mb.addHreg(111); //Создаем holding регистр с адресом 111
  uint8_t temp = 4;
  switch(readByte){
    case 1:
      temp = 0;
      break;
    case 2:
      temp = 1;
      break;
    case 3:
      temp = 2;
      break;
  }
  if (4 == temp){//Если прочитанное ни с чем не совпало
    eeWrite(h6Feeprom, 1, 1); //Записываем в EEprom 3 (2 стопбита)
    temp = 0;
  }
   /* 0 — нет бита чётности (none),
    1 — нечетный (odd),
    2 — четный (even) */
  //Serial.print("mb.Hreg(111, temp) ");Serial.println(temp);
  mb.Hreg(111, temp);//Устанавливаем значение четности
  mb.onSetHreg(111, h6Fset); // Add callback on Hreg 111 value set
  modbusNeedStart=1;//нужно перезапустить Modbus
}

uint16_t h6Fset(TRegister* reg, uint16_t val){
  //Serial.println("enter h6Fset (111)");
  uint8_t toeeprom = 0;
  switch(val){
    case 0:
      toeeprom = 1;
      break;
    case 1:
      toeeprom = 2;
      break;
    case 2:
      toeeprom = 3;
      break;
  }
  if (0!=toeeprom){//Если записанное не совпало со списком четностей
    eeWrite(h6Feeprom, 1, toeeprom); //Записываем в EEprom 1
    modbusNeedStart=1;//нужно перезапустить Modbus
    return val;
  }
  else{
    return mb.Hreg(111); //Если значение неверно - просто оставляем старое
  }
}

void h70setup(){
  //Serial.println("enter h70setup (112)");
  //Надо прочитать из EEPROM байт.
  byte readByte = eeRead(h70eeprom, 1);
  //Serial.print("readByte");Serial.println(readByte);
  mb.addHreg(112); //Создаем holding регистр с адресом 112
  uint8_t temp = 0;
  switch(readByte){
    case 1:
      temp = 1;
      break;
    case 2:
      temp = 2;
      break;
  }
  if (0 == temp){//Если прочитанное ни с чем не совпало
    eeWrite(h70eeprom, 1, 2); //Записываем в EEprom 9 (9600)
    temp = 2;
  }
  /*1 — 1 стопбит
    2 — 2 стопбит*/
  mb.Hreg(112, temp);//Устанавливаем значение четности
  mb.onSetHreg(112, h70set); // Add callback on Hreg 112 value set
  modbusNeedStart=1;//нужно перезапустить Modbus
}

uint16_t h70set(TRegister* reg, uint16_t val){
  //Serial.println("enter h70set (112)");
  uint8_t toeeprom = 0;
  //Serial.print("val=");Serial.println(val);
  switch(val){
    case 1:
      toeeprom = 1;
      break;
    case 2:
      toeeprom = 2;
      break;
  }
  if (0 != toeeprom){//Если записанное не совпало со списком четностей
    eeWrite(h70eeprom, 1, toeeprom); //Записываем в EEprom 1
    modbusNeedStart=1;//нужно перезапустить Modbus
    return val;
  }
  else{
    return mb.Hreg(112); //Если значение неверно - просто оставляем старое
  }
}


void i68setup(){
  Serial.println("enter h68setup (104)");
  //
  mb.addIreg(104, 0, 2); //Создаем input регистрЫ с адресом 104-105 записывая в них "0"
  //mb.Ireg(104, 0);//Устанавливаем значение счетчика. Только надо делать это не тут.
  //mb.Ireg(105, 0);//Устанавливаем значение счетчика. Только надо делать это не тут.
  mb.onGetIreg(104, i68get, 2);  // Add single callback for multiple Inputs. It will be called for each of these inputs value get
  //modbusNeedStart=1;//нужно перезапустить Modbus
}

uint16_t i68get(TRegister* reg, uint16_t val){
  //Serial.println("enter h68get (104)");
  //Serial.print(reg->address.address);
  //
  //mb.Ireg(104, TCCR0A);//Устанавливаем значение счетчика. Только надо делать это не тут.
  //так, надо взять ulong значение millis(), разделить его на 1000.
  uint32_t tempSecunds = millis()/1000;
  //Получим ulong секунд. Старшую часть записываем в 104 а младшую в 105
  //просто берем указатель16-разрядный на старшую часть, для начала.
  uint16_t* ptrTemp = (uint16_t*)&tempSecunds;
  if(reg->address.address == 104)
    //return OCR0A;
    return *(ptrTemp+1);
  if(reg->address.address == 105)
    return *(ptrTemp);
  return 256;
}


ISR(TIMER0_COMPA_vect)
{
     //digitalWrite(13, !digitalRead(13));
}



/*
void ftest80(int i){
  Serial.print("PACANI");
  Serial.println(i*2);
}

void ftest104(int i){
  Serial.print("VASCHE");
  Serial.println(i*2);
}
*/


void modbusStart(){
  //Serial.begin(9600, SERIAL_8N2); //четность и стопбиты - менять!
  //mb.begin(&Serial, RXTX_PIN); //запускаем на указанном порту Modbus
  
  uint32_t speedReg = 0;
  switch(mb.Hreg(110)){
    case 12:
      speedReg = 1200;
      break;
    case 24:
      speedReg = 2400;
      break;
    case 48:
      speedReg = 4800;
      break;
    case 96:
      speedReg = 9600;
      break;
    case 192:
      speedReg = 19200;
      break;
    case 576:
      speedReg = 57600;
      break;
    case 1152:
      speedReg = 115200;
      break;
  }

  // SERIAL_8N2 - это дефайн, смотреть можно на 
  // https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/HardwareSerial.h
  // с 68 строки
  // SERIAL_8N1 0x06
  // SERIAL_8N2 0x0E
  // SERIAL_8E1 0x26
  // SERIAL_8E2 0x2E
  // SERIAL_8O1 0x36
  // SERIAL_8O2 0x3E
  // видно что получается суммой четности 
  // N - 0x00
  // E - 0x20
  // O - 0x30
  // и стопбитов
  // 1 - 0x06
  // 2 - 0x0E
  uint8_t serialParam=0;
  switch(mb.Hreg(111)){
    case 0:
      serialParam = 0x0;
      break;
    case 1:
      serialParam = 0x30;
      break;
    case 2:
      serialParam = 0x20;
      break;
  }
    switch(mb.Hreg(112)){
    case 1:
      serialParam += 0x06;
      break;
    case 2:
      serialParam += 0x0E;
      break;
  }
  
  Serial.begin(speedReg, serialParam); //четность и стопбиты - менять!
  //Serial.begin(speedReg, SERIAL_8N2);
  mb.begin(&Serial, RXTX_PIN); //запускаем на указанном порту Modbus
  
  Serial.print("Modbus speed ");Serial.println(mb.Hreg(110));
  mb.setBaudrate(speedReg);// Это не скорость порта, это для задержек, смотри в исходник библиотеки.
  mb.slave(mb.Hreg(128));
  //mb.slave(1); // for debug
  //Serial.print("Modbus speed ");Serial.print(mb.Hreg(110));Serial.print(" speed ");Serial.println(speedReg);
  Serial.print("mb.Hreg(111) parity:");Serial.println(mb.Hreg(111));
  Serial.print("mb.Hreg(112) stopbit");Serial.println(mb.Hreg(112));
  Serial.print("Modbus parameter ");Serial.println(serialParam);
  //Serial.print("Modbus parameter SERIAL_8N2:");Serial.println(SERIAL_8N2);
  Serial.print("Modbus started with ");Serial.println(mb.Hreg(128));
}


uint32_t eeRead (int addr, byte lenght){
  //Возвращает считанное из EEPROM значение
  //Первый (младший) байт читается с addr, количество считывемых байт [1..2] задается в lenght
  uint32_t retVal = 0;
  byte *ptrb = (byte*)&retVal; //Указатель приведен к byte, чтобы обращаться к байтам int отдельно
    for (byte i = 0; i<lenght; i++){
      *(ptrb+i) = EEPROM.read(addr);
    }
  return *ptrb;
}

void eeWrite (uint32_t addr, byte lenght, uint32_t val){
  //Пишет в EEPROM значение
  //Первый (младший) байт пишется в addr, количество записываемых байт [1..2] задается в lenght
  byte *ptrb = (byte*)&val; //Указатель приведен к byte, чтобы обращаться к байтам int отдельно
  for (byte i = 0; i<lenght; i++){
      //Serial.print("write ");
      //Serial.print(*(ptrb+i));
      //Serial.print("write 1");
      //Serial.print(*(ptrb+0));
      EEPROM.update(addr, *(ptrb+i));
    }
}


//User register:

void c00setup(){
  Serial.println("enter c00setup (00)");
  //
  mb.addCoil(0, 0, 6); //Создаем coil регистрЫ с адресом 00-05 записывая в них "0"
  mb.onGetCoil(0, c00get, 6);  // Add single callback for multiple coils. It will be called for each of these coils value get
  mb.onSetCoil(0, c00set, 6);  // Add single callback for multiple coils. It will be called for each of these coils value SET
  //modbusNeedStart=1;//нужно перезапустить Modbus
  Serial.println("eXIT c00setup (00)");
}

uint16_t c00get(TRegister* reg, uint16_t val){
  //Serial.println("enter c00get (00)");
  //Serial.print(reg->address.address);
  //
  //uint32_t tempSecunds = millis()/1000;
  //Получим ulong секунд. Старшую часть записываем в 104 а младшую в 105
  //просто берем указатель16-разрядный на старшую часть, для начала.
  //uint16_t* ptrTemp = (uint16_t*)&tempSecunds;

  //if(reg->address.address == 0)
  //  return COIL_VAL(1);
  //  //return *(ptrTemp+1);
  //if(reg->address.address == 1)
  //  //return *(ptrTemp);
  //  return COIL_VAL(1);
  return val;
}

uint16_t c00set(TRegister* reg, uint16_t val) {
  //Serial.print("enter c00set (00)");
  //Serial.print(reg->address.address);
  //Serial.println(COIL_BOOL(val));
  digitalWrite(coilOutList[reg->address.address], COIL_BOOL(val));
  return val;
}

void hc8setup(){
  Serial.println("enter hс8setup (200)");
  //Надо прочитать из EEPROM байт.
//  byte readByte = eeRead(h70eeprom, 1);
//  Serial.print("readByte");Serial.println(readByte);
  mb.addHreg(200); //Создаем holding регистр с адресом 200
}

Ну и шаблон:

{
    "title": "arduino_title",
    "device_type": "arduino_modbus",
    "group": "g-diy",
    "device": {
        "name": "Arduino",
        "id": "arduino_modbus_slave",
        "min_read_registers": 2,
        "max_read_registers": 10,
        "response_timeout_ms": 200,
        "frame_timeout_ms": 100,
        "device_max_fail_cycles": 5,
        "guard_interval_us": 500,
        "groups": [
            {
                "title": "General",
                "id": "general"
            },
            {
                "title": "HW Info",
                "id": "hw_info"
            },
            {
                "title": "Debug",
                "id": "debug"
            }
        ],

        "parameters": {
            "baud_rate": {
                "title": "Baud rate",
                "description": "baud_rate_description",
                "address": 110,
                "reg_type": "holding",
                "enum": [96, 192, 384, 576, 1152],
                "default": 96,
                "enum_titles": [
                    "9600",
                    "19200",
                    "38400",
                    "57600",
                    "115200"
                ],
                "group": "general",
                "order": 1
            },
            "disable_indication": {
                "title": "Status LED",
                "address": 130,
                "reg_type": "holding",
                "enum": [0, 1],
                "enum_titles": ["Enabled", "Disabled"],
                "default": 0,
                "group": "general",
                "order": 3
            }
        },

        "channels": [
            {
                "name": "testreg",
                "reg_type": "holding",
                "address": 200,
                "scale": 1,
                "max": 255,
                "type": "value",
                "format": "u16",
                "group": "general"
            },

            {
                "name": "testreg0",
                "reg_type": "holding",
                "address": "200:0:1",
                "scale": 1,
                "max": 255,
                "type": "value",
                "format": "u16",
                "group": "general"
            },            

            {
                "name": "testreg1",
                "reg_type": "holding",
                "address": "200:1:1",
                "scale": 1,
                "max": 255,
                "type": "value",
                "format": "u16",
                "group": "general"
            },
            {
                "name": "testreg2",
                "reg_type": "holding",
                "address": "200:2:1",
                "scale": 1,
                "max": 255,
                "type": "value",
                "format": "u16",
                "group": "general"
            },
            {
                "name": "Out0",
                "reg_type": "coil",
                "address": 0,
                "type": "switch",
                "group": "general"
            },
            {
                "name": "Out1",
                "reg_type": "coil",
                "address": 1,
                "type": "switch",
                "group": "general"
            },
            {
                "name": "Out2",
                "reg_type": "coil",
                "address": 2,
                "type": "switch",
                "group": "general"
            },
            {
                "name": "Out3",
                "reg_type": "coil",
                "address": 3,
                "type": "switch",
                "group": "general"
            },
            {
                "name": "Out4",
                "reg_type": "coil",
                "address": 4,
                "type": "switch",
                "group": "general"
            },
            {
                "name": "Out5",
                "reg_type": "coil",
                "address": 5,
                "type": "switch",
                "group": "general"
            },

            {
                "name": "Supply Voltage",
                "reg_type": "holding",
                "address": 8,
                "scale": 0.1,
                "type": "voltage",
                "readonly": true,
                "enabled": false,
                "group": "debug"
            }
        ],
        "translations": {
            "en": {
                "arduino_title": "Arduino DIY modbus slave",
                "Current": "Load current"
        },
            "ru": {
                "General": "Общее",
                "HW Info": "Данные модуля",
                "Debug": "Диагностика",
                "no": "нет",
                "yes": "РґР°",
                "Disabled": "Отключен",
                "testreg": "Тестовый",
                "Supply Voltage": "Напряжение питания",
            }
        }
    }
}