Интеграция в iH OLED-дисплея через MegaD
-
В задаче Интеграция в iH входов типа Click Mode MegaD я описывал вариант использования "мультикнопки" для вывода различной информации на OLED-дисплей с контроллером SSD1306. Поскольку лишь недавно "допилил" сценарий работы с дисплеем, то сейчас хочу рассказать что же у меня вышло.
Итак, "мультикнопка", висящая на входе MegaD, имеет три варианта нажатия:
а. Однократное нажатие (click=1). Если дисплей был выключен (или был перезапуск iH, или перезапуск плагина megad, или сценарий был сохранен), то он включится и отобразит температуру внутри помещения. При втором нажатии кнопки отобразится значение влажности помещения, при третьем нажатии отобразиться значение СО2. Следующее нажатие снова отобразит температуру в помещении и так по кругу.
б. Двукратное нажатие (click=2). На дисплее отобразится значение температуры на улице.
в. Удержание кнопки (m=2). Дисплей выключится. Можно конечно было "навесить" что-то еще на этот вариант нажатия кнопки, но в нашей семье дисплей иногда мешает спать. Поэтому и решил его таким образом принудительно выключать.Кроме всего этого, сценарий запускается при изменении значения (value) датчиков и если в текущий момент времени на дисплее отображается соответствующий датчику параметр, то его значение обновляется на дисплее.
/** * @name Сервис - обновление данных на OLED * @desc При однократном нажатии кнопки 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({ plugin: "megad2", //Переменная названия плагина channel: 33, //Переменная номера канала плагина param: 1, //Переменная для выбора параметров button_state: 0, //Переменная для подсчета одинарных нажатий кнопки oled_state: 0, //Переменная состояния OLED (0 - выключен, 1 - включен) col: 0, //Переменная для расчета № столбца для вывода текста time: 1, //Переменная для вывода времени (0 - нет, 1 - есть) text: "", //Переменная для отображения текста на OLED saved: "", //Переменная для "запоминания" текста 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; //Вариант нажатия кнопки //Одинарное нажатие кнопки всегда возвращает 1! if(mode == 1) { //Если OLED был выключен то отображаем первый параметр if(!this.oled_state) { this.param = mode; this.button_state = this.param; } //Перебираем параметры при каждом одинарном нажатии кнопки else { this.param = this.button_state + 1; if(this.param > 2) this.button_state = 0; else this.button_state = this.param; } } //Двойное нажатие кнопки возвращает 4, удержание - 5. 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.2, "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"; 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"); //Центрирование заголовка 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"; //Вывод команд на дисплей if(this.saved != this.text) { //Если новый заголовок this.saved = this.text; //то запоминаем его this.ClearOLED(); //очищаем первую строку //делаем паузу, выводим новый заголовок this.startTimer("T2", 0.05, "SendTextToOLED"); //делаем паузу, выводим значение this.startTimer("T3", 0.2, "SendValueToOLED"); } else { //Иначе обновляем значение this.SendValueToOLED(); } if(time) { //Если есть вывод времени //то после отображения значения выводим его this.startTimer("T4", 0.4, "SendTimeToOLED"); } if(!this.oled_state) { //Если дисплей выключен //то включаем его после обновления параметра this.startTimer("T5", 0.4, "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) на некоторое время дисплей вновь заработал правильно, однако потом все строки дисплея сместились на половину строки и с этим я уже ничего не смог сделать.
Сценарий обновлен 19.11.2020
-
Большое спасибо за труд, использовал ваш код вроде все работает, жду продолжения статьи.