Интеграция в iH OLED-дисплея через MegaD



  • В задаче Интеграция в iH входов типа Click Mode MegaD я описывал вариант использования "мультикнопки" для вывода различной информации на OLED-дисплей с контроллером SSD1306. Поскольку лишь недавно "допилил" сценарий работы с дисплеем, то сейчас хочу рассказать что же у меня вышло.
    Итак, "мультикнопка", висящая на входе MegaD, имеет три варианта нажатия:
    а. Однократное нажатие (click=1). Если дисплей был выключен (или был перезапуск iH, или перезапуск плагина megad, или сценарий был сохранен), то он включится и отобразит температуру внутри помещения. При втором нажатии кнопки отобразится значение влажности помещения, при третьем нажатии отобразиться значение СО2. Следующее нажатие снова отобразит температуру в помещении и так по кругу.
    б. Двукратное нажатие (click=2). На дисплее отобразится значение температуры на улице.
    в. Удержание кнопки (m=2). Дисплей выключится. Можно конечно было "навесить" что-то еще на этот вариант нажатия кнопки, но в нашей семье дисплей иногда мешает спать. Поэтому и решил его таким образом принудительно выключать.

    Кроме всего этого, сценарий запускается при изменении значения (value) датчиков и если в текущий момент времени на дисплее отображается соответствующий датчику параметр, то его значение обновляется на дисплее.

    /** 
    * @name Обновление данных на OLED гостевой 
    * @desc При однократном нажатии кнопки OLED отображает параметры микроклимата данного помещения. При двойном нажатии кнопки
    *       OLED отображает уличную температуру. При удержании кнопки дисплей выключается. 
    * @version 4 
    */
    
    const button  = Device("BUTTON1_01");
    const dt      = Device("STEMP1_01");
    const dh      = Device("SHUMIDITY1_01");
    const dco2    = Device("SENSORA1_01");
    const dt_s    = Device("STEMP4_01");
    
    startOnChange([button,dt,dh,dco2,dt_s]);
    
    script({
      param: 0,         //Переменная для выбора параметров
      button_state: 0,  //Переменная для подсчета одинарных нажатий кнопки
      oled_state: 0,    //Переменная состояния OLED (выключен/включен)
      plugin: "megad2", //Переменная названия плагина
      channel: 33,      //Переменная номера канала плагина
      col: 0,           //Переменная для расчета № столбца для вывода текста
      time: 0,          //0 - нет вывода времени, 1 - есть
      text: "",         //Переменная для отображения текста на OLED
      value: "",        //Переменная для отображения значения на OLED
      unit: "",         //Переменная для отображения ед.изм. на OLED
      
      start() {
        //Обновление данных по датчикам темературы
        if(this.isChanged(dt, "value") && this.param == 1) {
          this.SendData1();
        }
        else if(this.isChanged(dh, "value") && this.param == 2) {
          this.SendData2();
        }
        else if(this.isChanged(dco2, "value") && this.param == 3) {
          this.SendData3();
        }
        else if(this.isChanged(dt_s, "value") && this.param == 4) {
          this.SendData4();
        }
        //Переключение режимов отображения кнопкой
        else if(this.isChanged(button, "value")) {
          let mode = button.value;  //Вариант нажатия кнопки
          
          if(mode == 1) {
            //Если OLED был выключен то отображаем первый параметр
            if(!this.oled_state) {
              this.param = mode;
              this.button_state = param;
            }
            //Перебираем параметры при каждом одинарном нажатии кнопки
            else {
              this.param = this.button_state + 1;
              if(this.param > 2) this.button_state = 0;
              else this.button_state = this.param;
            }
          }
          else {
            this.param = mode;
            this.button_state = 0;
          }
          
          //Выводим информацию на OLED в соответствии с выбранным параметром
          switch(this.param) {
            case 1: this.SendData1();
                    break;
            case 2: this.SendData2();
                    break;
            case 3: this.SendData3();
                    break;
            case 4: this.SendData4();
                    break;
            case 5: this.SendData5();
                    break;
          }
          
          if(mode > 0) {
            this.startTimer("ButRst", 0.5, "ClearState");
          }
        }
      },
        
      SendData1() {
        let text = "Температура"
        let value = dt.value;
        let unit = "grad";
        this.DataToOLED(text, value, unit, this.time);
      },
      
      SendData2() {
        let text = "Влажность";
        let value = dh.value;
        let unit = "percent";
        this.DataToOLED(text, value, unit, this.time);
      },
      
      SendData3() {
        let text = "Уровень_СО2,_ppm";
        let value = dco2.value;
        let unit = "ppm";
        let time = 1;
        this.DataToOLED(text, value, unit, this.time);
      },
      
      SendData4() {
        let text = "На_улице";
        let value = dt_s.value;
        let unit = "grad";
        this.DataToOLED(text, value, unit, this.time);
      },
      
      SendData5() {
        this.TurnOffOLED();
      },
      
      //Функция форматирования данных и их отображения на OLED
      DataToOLED(text, value, unit, time) {
        let iconv = require("/opt/intrahouse-c/backend/node_modules/iconv-lite");
        let buf = iconv.encode(text, "cp866");
        this.text = buf.toString("latin1");
        
        //Центрирование параметра на OLED
        let length = this.text.length;
        let col = (128 - length*6)/2;
        this.col = col.toFixed(0);
        
        //Форматирование цифровых значений
        let str = ""; //Вспомогательная переменная
        if(value < 100) value = value.toFixed(1);
        str = value.toString();
        if(str.length < 4 && value > 0) this.value = 's+' +str;
        else if(str.length < 4) this.value = 'ss' +str;
        else if(str.length < 5) this.value = 's' +str;
        else this.value = str;
        
        //Выбор единицы измерения
        if(unit == "grad") this.unit = ":";
        else if(unit == "percent") this.unit = "%";
        else if(unit == "ppm") this.unit = "s";
        
        //Вывод команд на дисплей
        this.ClearOLED();       //Очищаем первую строчку
        this.startTimer("T2", 0.2, "SendTextToOLED");
        this.startTimer("T3", 0.4, "SendValueToOLED");
        if(time) {              //Если есть вывод времени
          //то после отображения значения выводим его
          this.startTimer("T4", 0.6, "SendTimeToOLED");
        }
        if(!this.oled_state) {  //Если дисплей выключен
          //то включаем его после обновления параметра
          this.startTimer("T5", 0.6, "TurnOnOLED");
        }
      },
      
      ClearState() {
        this.assign(button, "value", 0);
      },
      
      ClearOLED() {
        this.pluginCommand({unit: this.plugin, command: '/sec/?pt=' +this.channel+ '&disp_cmd=1&row=0'});
      },
      
      ControlOLED(cmd) {
        this.pluginCommand({unit: this.plugin, command: '/sec/?cmd=' +this.channel+ ':' +cmd});
      },
      
      TurnOnOLED() {
        this.oled_state = 1;
        this.ControlOLED(this.oled_state);
      },
      
      TurnOffOLED() {
        this.oled_state = 0;
        this.ControlOLED(this.oled_state);
      },
      
      SendTextToOLED() {
        this.pluginCommand({unit: this.plugin, command: '/sec/?pt=' +this.channel+ '&text=' +this.text+ '&col=' +this.col+ '&row=0'});
      },
      
      SendValueToOLED() {
        this.pluginCommand({unit: this.plugin, command: '/sec/?pt=' +this.channel+ '&text=' +this.value+this.unit});
      },
      
      SendTimeToOLED() {
        //Запрос системного времени и форматирование единиц (для отображения 00...09)
        let time = new Date();
        let hours = time.getHours() < 10 ? "0" +time.getHours() : time.getHours();
        let minutes = time.getMinutes() < 10 ? "0" +time.getMinutes() : time.getMinutes();
        let seconds = time.getSeconds() < 10 ? "0" +time.getSeconds() : time.getSeconds();
        //Группировка единиц времени для передачи плагину
        let text = hours+ ':' +minutes+ ':' +seconds;
        this.pluginCommand({unit: this.plugin, command: '/sec/?pt=' +this.channel+ '&text=' +text+ '&col=45&row=6'});
      }
    });
    

    Код сценария получился довольно многострочным. Однако читатель, обладающий небольшим навыком JS-программирования сможет понять и разобраться со сценарием благодаря наличию комментариев и понятному названию функций. Непосредственно с дисплеем работают 5 функций - TurnOnOLED(), TurnOffOLED(), SendTextToOLED(), SendValueToOLED(), SendTimeToOLED(). Все они используют переменные уровня сценария (к которым нужно обращаться через "this"), поскольку обмен данными с дисплеем медленный и необходимо выдерживать паузы, запуская таймеры Т2-Т5, по истечению которых должны вызываться соответствующие функции (однако какой-либо аргумент в таком случае в функцию передать нет возможности):

    • для очистки первой строки, на которой по умолчанию выводится текст с наименованием параметра;
    • для вывода текста с наименованием параметра;
    • для вывода значения параметра крупным шрифтом ("нативный" шрифт MegaD);
    • для вывода времени обновления параметра.

    Кстати, при "игре" с выводом последнего возникли трудности отображения данных на дисплее. Итерационным методом понял, что надпись времени нужно выводить в 6-й строке (row=6), но когда попробовал вывести время в седьмой, то подопытный дисплей у меня нещадно заглючил! Вначале значение параметра (крупный шрифт) отображалось только на левой части дисплея (хотя все остальные строки - в полном объеме) и никакие переинициализации ("save" MegaD-2561 или php-скрипт) и даже долговременное снятие питания с дисплея не помогали! Каким-то чудом (с начало вывел время в row=1...6, потом снова в row=7) на некоторое время дисплей вновь заработал правильно, однако потом все строки дисплея сместились на половину строки и с этим я уже ничего не смог сделать.

    В стадии написания...



  • Большое спасибо за труд, использовал ваш код вроде все работает, жду продолжения статьи.


Log in to reply