OpenTherm <-> UART

Источник: Alex_Jet » 30 мар 2022, 17:00

Мой пример опроса газового котла через шлюз OpenTherm ↔ UART (ModBus RTU) и, соответственно, через MegaD-2561 как шлюз UART (ModBus RTU) ↔ Ethernet (HTTP) с помощью сценария в intraHouse v4:

/** 
* @name Отопление - OT мониторинг газового котла  
* @desc Мониторинг котла по Opentherm  
* @version 4  
*/

const boiler_gaz = Device("ACTOR_HEATING_SYS_01", [
  {"name":"data_request", "note":"Запрос данных", "type":"string", "val":""},
  {"name":"firmware", "note":"Версия прошивки", "type":"string", "val":""},
  {"name":"boiler_status", "note":"Master и Slave статус", "type":"string", "val":""},
  {"name":"temp_outside", "note":"Температура внешняя", "type":"string", "val":""},
  {"name":"temp_heating", "note":"Температура отопления", "type":"string", "val":""},
  {"name":"temp_return", "note":"Температура обратки", "type":"string", "val":""},
  {"name":"temp_hot_water", "note":"Температура ГВС", "type":"string", "val":""},
  {"name":"pressure", "note":"Давление в системе", "type":"string", "val":""},
  {"name":"modulation", "note":"Модуляция горелки", "type":"string", "val":""},
  {"name":"error_number", "note":"Ошибка котла", "type":"string", "val":""},
  {"name":"Max_Hot_Water", "note":"Максимальная температура ГВС", "type":"string", "val":""},
  {"name":"Min_Hot_Water", "note":"Минимальная температура ГВС", "type":"string", "val":""},
  {"name":"Max_Heating", "note":"Максимальная температура отопления", "type":"string", "val":""},
  {"name":"Min_Heating", "note":"Минимальная температура отопления", "type":"string", "val":""}
]);

const temp_outside = Device("BOILER_GAS_TEMP_OUTSIDE");
const temp_heating = Device("BOILER_GAS_TEMP_HEATING");
const temp_return = Device("BOILER_GAS_TEMP_RETURN");
const temp_hot_water = Device("BOILER_GAS_TEMP_HOT_WATER");
const pressure = Device("BOILER_GAS_PRESSURE_SYSTEM");
const modulation = Device("BOILER_GAS_MODULATION_BURNER");
const error_number = Device("BOILER_GAS_ERROR_NUMBER");

const state_fault = Device("BOILER_GAS_STATE_FAULT");
const state_CH_mode = Device("BOILER_GAS_STATE_CH_MODE");
const state_DHW_mode = Device("BOILER_GAS_STATE_DHW_MODE");
const state_flame = Device("BOILER_GAS_STATE_FLAME");

const boiler_status = Device("ST_HEATING_SYS_04");

script({
  unit: "megad4",                   //Контроллер с UART
  uart_tx: "",                      //Переменная для передачи данных
  uart_rx: "uart_rx=1&mode=rs485",  //Переменная для получения данных
  timeout: 1,                       //Пауза перед получением данных
  time_wd: 6,                       //Пауза если данные записываются в шлюз
  command: 1,                       //Переменная для формирования передачи
  error_rx: 0,                      //Ошибка приема данных (MegaD возвращает CRC Error)
  debug: 0,                         //Отладка: 0 - выключена, 1-включена
  
  //Биты состояния котла в соответствии с OpenTherm
  bit_state_fault: 1,               //Бит наличия ошибки
  bit_state_CH_mode: 2,             //Бит состояния контура отопления 1
  bit_state_DHW_mode: 4,            //Бит состояния контура ГВС
  bit_state_flame: 8,               //Бит состояния горелки
  bit_state_cooling: 16,            //Бит состояния охлаждения
  bit_state_CH2_mode: 32,           //Бит состояния контура отопления 2
  bit_state_diagnostic: 64,         //Бит состояния процедуры диагностики
  
  start() {
    //Если происходит запись данных в шлюз, то добавляем слушателя
    if(global.get("Boiler_Gaz_WriteData")) {
      this.startTimer("PauseWhileWritingData", this.time_wd, "setReadData");
      //this.addListener("globals.Boiler_Gaz_WriteData", "setReadData");
    }
    //Иначе устанавливаем признак чтения данных из шлюза
    else this.setReadData();
  },
  
  setReadData() {
    //Установка признака начала чтения данных из шлюза
    global.set("Boiler_Gaz_ReadData", 1);
    //globals.Boiler_Gaz_ReadData = 1;
    
    //Отладка
    if(this.debug) this.log('Отладка #X: Boiler_Gaz_ReadData = ' +global.get("Boiler_Gaz_ReadData"));
    
    //Передача данных в шлюз
    this.dataTransfer();
  },
  
  dataTransfer() {
    let address = "0b";
    let code = "04";
    let register;
    let number = "0001";
    
    switch(this.command) {
      case 1:   register = "00cc"; //Версия прошивки
                break;
      case 2:   register = "00cd"; //Статус котла
                break;
      case 3:   register = "00ce"; //Ошибка котла
                break;
      case 4:   register = "00cf"; //Температура отопления текущая
                break;
      case 5:   register = "00d0"; //Температура горячей воды текущая
                break;
      case 6:   register = "00d1"; //Уровень модуляции пламени горелки
                break;
      case 7:   register = "00d2"; //Давление  (P*10)
                break;
      case 8:   register = "00d3"; //Температура внешняя ((Т+100)*10)
                break;
      case 9:   register = "00d4"; //Верхняя граница температуры ГВС
                break;
      case 10:  register = "00d5"; //Нижняя граница температуры ГВС
                break;
      case 11:  register = "00d6"; //Верхняя граница температуры котловой воды
                break;
      case 12:  register = "00d7"; //Нижняя граница температуры котловой воды
                break;
      case 13:  register = "00d8"; //Температура обратки текущая
                break;
    }
    
    //Формирование передачи для MegaD
    this.uart_tx = "uart_tx=" +address+code+register+number+ "&mode=rs485";
    
    //Отладка
    if(this.debug) this.log("Отладка #1: request = " +this.uart_tx);
    
    this.sendRequest();
  },
  
  sendRequest() {
    //Передаем запрос на MegaD
    this.pluginCommand({unit:this.unit, command:'/sec/?'+this.uart_tx});
    //Через timeout запрашиваем у контроллера ответ ModBus-устройства
    this.startTimer("getAnswer", this.timeout, "getAnswer");
  },
  
  getAnswer() {
    //Передаем запрос на MegaD, ответ без обработки придет в сценарий (onResponse:'raw')
    this.pluginCommand({unit:this.unit, command:{url:'/sec/?'+this.uart_rx, onResponse:'raw'}}, 'getResponse');
  },
  
  getResponse(body) {
    var data_array;
    var data_status;
    var error;
    var data;
    
    if(body == "CRC Error") {
      data_status = "CRC Error";
      this.error_rx = this.error_rx + 1;
      error = 1;
    }
    else if(body === undefined) {
      data_status = "Not connect";
      error = 1;
    }
    else {
      data_status = "OK";
      error = 0;
      
      //Формируем массив из ответа
      data_array = body.split("|");
      data = parseInt(data_array[3]+data_array[4], 16);
    }
    
    //Вывод статуса запроса данных
    boiler_gaz.setParam("data_request", data_status+ " (CRC Error - " +this.error_rx+ ")");
    
    //Отладка
    if(this.debug) this.log('Отладка #2: body = ' +body+ "; data = " +data);
    
    //Если все нормально, то опрашиваем шлюз
    if(!error) {
      switch(this.command) {
        case 1:   //Версия прошивки
                  data = data*0.01;
                  boiler_gaz.setParam("firmware", data);
                  break;
        case 2:   //Статус котла
                  let bin = this.dec2bin(data);
                  boiler_gaz.setParam("boiler_status", bin+ " ("+data+")");
                  this.assign(boiler_status, "value", bin+ " ("+data+")");
                  
                  let state;
                  state = this.BitState(this.bit_state_fault, data);
                  this.assign(state_fault, "value", state);
                  
                  state = this.BitState(this.bit_state_CH_mode, data);
                  this.assign(state_CH_mode, "value", state);
                  
                  state = this.BitState(this.bit_state_DHW_mode, data);
                  this.assign(state_DHW_mode, "value", state);
                  
                  state = this.BitState(this.bit_state_flame, data);
                  this.assign(state_flame, "value", state);
                  
                  //this.assign(boiler_status, "error", 0);
                  break;
        case 3:   //Ошибки котла
                  if(data === 0) data = "нет";
                  boiler_gaz.setParam("error_number", data);
                  //error_number.setValue(data);
                  this.assign(error_number, "value", data);
                  //this.assign(error_number, "error", 0);
                  break;
        case 4:   //Температура отопления текущая
                  boiler_gaz.setParam("temp_heating", data);
                  //temp_heating.setValue(data);
                  this.assign(temp_heating, "value", data);
                  //this.assign(temp_heating, "error", 0);
                  break;
        case 5:   //Температура горячей воды текущая
                  boiler_gaz.setParam("temp_hot_water", data);
                  //temp_hot_water.setValue(data);
                  this.assign(temp_hot_water, "value", data);
                  //this.assign(temp_hot_water, "error", 0);
                  break;
        case 6:   //Уровень модуляции пламени горелки
                  boiler_gaz.setParam("modulation", data);
                  //modulation.setValue(data);
                  this.assign(modulation, "value", data);
                  //this.assign(modulation, "error", 0);
                  break;
        case 7:   //Давление системы (P*10)
                  data = data*0.1 - 100;
                  boiler_gaz.setParam("pressure", data.toFixed(2));
                  //pressure.setValue(data);
                  this.assign(pressure, "value", data.toFixed(2));
                  //this.assign(pressure, "error", 0);
                  break;
        case 8:   //Температура внешняя ((Т+100)*10)
                  data = data*0.1 - 100;
                  boiler_gaz.setParam("temp_outside", data.toFixed(2));
                  //temp_outside.setValue(data);
                  this.assign(temp_outside, "value", data.toFixed(2));
                  //this.assign(temp_outside, "error", 0);
                  break;
        case 9:   //Верхняя граница температуры ГВС
                  boiler_gaz.setParam("Max_Hot_Water", data);
                  break;
        case 10:  //Нижняя граница температуры ГВС
                  boiler_gaz.setParam("Min_Hot_Water", data);
                  break;
        case 11:  //Верхняя граница температуры отопления
                  boiler_gaz.setParam("Max_Heating", data);
                  break;
        case 12:  //Нижняя граница температуры отопления
                  boiler_gaz.setParam("Min_Heating", data);
                  break;
        case 13:  //Температура обратки текущая
                  boiler_gaz.setParam("temp_return", data);
                  this.assign(temp_return, "value", data);
                  break;
      }
      
      if(++this.command > 13) {
        this.exitScript();
      }
      else this.dataTransfer();
    }
    //Если ошибка, то выходим из сценария
    else this.exitScript();
  },
  
  dec2bin(hex) {
    var str = (parseInt(hex, 10).toString(2)).padStart(16, "0");
    return str.substr(8);
  },
  
  BitState(bit, data) {
    let state = data & bit;
    if(state) state = 1;
    else state = 0;
    return state;
  },
  
  exitScript() {
    this.command = 1;
    
    //Установка признака окончания чтения данных из шлюза
    global.set("Boiler_Gaz_ReadData", 0);
    //globals.Boiler_Gaz_ReadData = 0;
    
    //Отладка
    if(this.debug) this.log('Отладка #X: Boiler_Gaz_ReadData = ' +global.get("Boiler_Gaz_ReadData"));
    
    this.exit();
  }
});