Анализатор пультов (IR сигналов)

LEOWRS
Offline
Зарегистрирован: 08.08.2018

Как было сказано на одном сайте «вместо дорогого устройства управления можно купить недорогой контроллер, который будет управлять кондиционером, достаточно знать систему команд…»

Самое простое решение, записать нужные сигналы и транслировать их контроллером. Обычно это работает, но может глючить, так пульт выдает не идеальный сигнал, приемник так же имеет погрешность. В результате «паспортный» сигнал в 560мкс на деле колеблется от 500 до 600, а будучи ретранслирован, погрешность увеличится и не факт что устройство распознает команду.

Это можно решить, записав сигнал раз 10, вычислить по среднему, и полученный «выровненный» отправлять контроллером не один, а три раза подряд.

Однако если требуется полностью эмулировать пульт простейшего кондиционера, то такой подход неприемлем. 4 режима работы, 4 режима мощности вентилятора, три варианта мощности, плюс 16 вариантов температуры, и получаем под тысячу уникальных «команд».

Скачать IR-decoder.ino

Для работы с пультами сделал декодер. Работает под EPS NodeMCU транслируя результаты в браузер, где их намного удобнее изучать.

Аппаратная часть элементарна, голая EPS с подключенным на цифровой вход IR приемником. Примерно так:

https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e070f971494b-30... 300w" />

Настройка скетча элементарная:

const char *ssid = "CCCP";
const char *password = "";
IPAddress ip(192, 168, 88, 55);
IPAddress gateway(192, 168, 88, 1);
IPAddress subnet(255, 255, 255, 0);
#define IRpin 14 //Номер входа (D5)

Меняете сетевые настройки, указываете пин куда подключен приемник, запускаете, открываете в браузере тот IP который указали в скетче, и ничего не меняя, отправляете пультом сигнал на подключенный к контроллеру ИК приемник.

https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e0712997f93a-30... 300w, https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e0712997f93a-76... 768w" />

Скрипт сам распознает формат передачи данных, отличая распространенный NECовский от старого RC5, автоматом определяет значения соответствующие единицам, нулям, и пропускам-разделителям, при этом с каждым нажатием на пульт «обучается» выравнивая средний этих показателей.

Переключаете опцию Show: с RAW DATA на BIN CODE, и снова нажав кнопку на пульте, получаете вот такую картинку, при этом если дважды на пульте нажали «+», а данные не изменились, значит пульт «тупой» и никаких настроек внутри себя не хранит.

https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e07140b07950-30... 300w" />

Такой пульт нет смысла анализировать, переключаете Show в режим «C++ array» и можно смело копипастить в свой скетч.

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

https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e0714fbe06ab-30... 300w, https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e0714fbe06ab-76... 768w" />

Если попался пульт работающий на CR5 то картинка будет немного иная. И все вышеописанные 3 шага будут выглядеть вот так:

https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e0716942a476-30... 300w, https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e0716942a476-76... 768w" />

Единственный в наличие у меня пульт, работающий по CR5 — это пульт от старого кассетного магнитофона Sony. Сигнал он отправляет повторяя от 4 до 10 раз, поэтому в скетче придется либо в цикл поставить отправку, либо раскопипастить значения массива. Проверить не могу, так как магнитофон давно выброшен.

Если автоматический режим распознавания протокола и сигналов не справился с задачей, можно MODE переключить в режиме MANUAL и вручную выставлять параметры для анализа. С протоколом понятно. DigitalSignal задает где «живут данные» UP в сигналах, DOWN в затуханиях, Separator это время разделителя сигнала, One это время единицы, Zero соответственно нуля. Время понятное дело в микросекундах.

Важный момент: если пробуете анализировать разные пульты, то при каждой смене пульта в автоматическом режиме анализа нужно нажать кнопочку «RESET» чтобы в нули слетели все ранее проанализированные автоматом параметры.

Умные пульты

С умными пультами, вроде пульта от кондиционера, которые хранят параметры в себе и на их основе формируют сигнал, ради который проект и затевался, алгоритм работы следующий.

Нажав 4 раза подряд кнопку «вкл-выкл» в бинарном режиме видим изменения данных:

https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e0719a7bfd12-30... 300w, https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e0719a7bfd12-76... 768w" />

То есть в моем случае включение — это единица на 20-ом бите, и её зеркальное значение в виде нуля на 28ом, при выключении, картина обратная, 20-ый зануляется, 28ой единица.

Если присмотреться, то в приведенном примере 48 бит информации на самом деле 3 пакета по 8 бит, после каждого из которых идет инверсия его самого. Это нормальная практика и это менее ресурсоёмко, нежели сначала пульту формировать контрольную сумму, а потом устройству её проверять, хотя теоретически могут попасться пульты с контрольной суммой, и там придется «взламывать» её алгоритм.

С «вкл-выкл» всё просто, переходим к варианту температуры, подняв пультом последовательно температуру с 18 до 30 градусов, получаем следующую картинку:

https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e071ca7d8aab-30... 300w, https://www.kovalenko.su/wp-content/uploads/2019/12/img_5e071ca7d8aab-76... 768w" />

Видно что температура задается 16-19 и 24-27 битами. При этом на 16-19ом очевидно указаны значения от 1 до 13 в двоичной системе.

Чтобы выставить 25С, нужно в десятичном из 25 вычесть базовые 17, и полученные 1000 биты записать в 16-19 как «0001», не забыв их инверсию записать в 24-27ой как «1110».

Надеюсь кому-нибудь будет полезно

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Прости, что-то не въеду. Она у тебя каким-то боком у фондовому рынку?

EPS - это ж "Earnings per share" (прибыль на акцию). Или я чего-то не понял?

ЕвгенийП
ЕвгенийП аватар
Онлайн
Зарегистрирован: 25.05.2015

Спасибо, сохраню ссылку, вдруг пригодится.

"Вовочка", это не про прибыль на акцию, это про инвалидность по "плоскошутию" :)

LEOWRS
Offline
Зарегистрирован: 08.08.2018

Ворота, за очепятку спасибо, на код она к счастью не влияет.

Задача проекта - чтобы зацепив 3 провода к контроллеру, залив скетч, можно было посмотреть любой пульт, хоть древний от магнитофона, хоть современный от кондционера. Чтобы в простом варианте можно было быстро получить массив для отправки в rawsend, и в тоже время для сложных, чтобы это был наглядный инструмент для анализа параметров пульта.

LEOWRS
Offline
Зарегистрирован: 08.08.2018

Сделал на основе анализатора небольшой код для управления ZH/LW-03, реализовав все режимы и функции пульта, при этом специально несколькими методами записи значений. Единственное чего не делал - это работу с таймером, так как по правде никогда не использовал эту функцию у кондиционера и даже не знаю как там что выставляется :)

Первоначальные данные записываются из анализатора и не важно в каком режиме, при этом гарантируют что начальный, конечный, а так же не измененные скриптом сигналы будут восприняты корректно кондиционером.

#include <IRremote.h>
IRsend irsend;

//Объявляем массивы глобальными, чтобы иметь к ним доступ из функций, желающие могут загнав в класс

//Берем из IR Decoder полученный C++ array, создав константу IRLength
#define IRLength 101
unsigned int IRsignal[IRLength] = {6598, 7572, 650, 3400, 650, 3400, 650, 3400, 650, 3400, 650, 1400, 650, 3400, 650, 3400, 650, 3400, 650, 1400, 650, 1400, 650, 1400, 650, 1400, 650, 3400, 650, 1400, 650, 1400, 650, 1400, 650, 3400, 650, 3400, 650, 1400, 650, 1400, 650, 1400, 650, 1400, 545, 1400, 650, 3400, 555, 1400, 556, 1400, 533, 3400, 555, 3400, 650, 3400, 650, 3400, 650, 3400, 555, 1400, 499, 1400, 499, 3400, 555, 1400, 510, 3400, 650, 1400, 476, 3400, 650, 1400, 476, 1400, 499, 3400, 650, 1400, 499, 3400, 650, 1400, 522, 3400, 650, 1400, 556, 3400, 650, 3400, 650, 7307, 650};
//Так же берем строчку BIN MODE и загоняем в массив char, не забывая +1 к размерности
boolean bits[47] = {1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1};
#define IRkhz 38

void setup() {
  Serial.begin(9600);
  bool ONOFF=true;
  int MODE=2; //1-охлаждение, 2-обогрев, 3-осушение, 4-вентилятор
  int TEMP=24; //Температура от 18 до 30, при этом если выставить 17 то датчик отключается и либо охлаждает до Арктики, либо греет до Африки
  bool ECON=false; //Режим экономии
  int AIRFLOW=B10; //B10 - нормальный, B00 - пол силы, B01 - сильный
  int FANSPEED=B00; //B00 - auto, B10 - high, B01 - medium, B11 - low
  bool HIGHPOWER=false; //Мощный режим
  MakeSignal(ONOFF,MODE,TEMP,ECON,AIRFLOW,FANSPEED,HIGHPOWER);
}

void MakeSignal(bool ONOFF,int MODE,int TEMP,int ECON,int AIRFLOW,int FANSPEED, bool HIGHPOWER)
{
  //Если ВКЛ у нас 1 на 20ом и 0 на 28ом, то отправляем функции куда записать и насколько сдвиг инвертированного (обычно он всегда одинаковый)
  SetBit(ONOFF,20,8);
  //С режимами работы сложнее, там меняется 16, 17, 19, 21 и 22 бит, и поскольку режимов всего четыре проще сделать через case-ы
  switch (MODE)
  {
    case 1: //Охлаждение
      SetBit(0,16,8);
      SetBit(1,17,8);
      SetBit(0,19,8);
      SetBit(0,21,8);
      SetBit(0,22,8);
    break;
    case 2: //Отопление
      SetBit(0,16,8);
      SetBit(1,17,8);
      SetBit(0,19,8);
      SetBit(1,21,8);
      SetBit(0,22,8);
    break;    
    case 3: //Осушение
      SetBit(0,16,8);
      SetBit(1,17,8);
      SetBit(0,19,8);
      SetBit(0,21,8);
      SetBit(1,22,8);
    break;    
    case 4: //Вентилятор
      SetBit(1,16,8);
      SetBit(0,17,8);
      SetBit(1,19,8);
      SetBit(0,21,8);
      SetBit(0,22,8);
    break;    
  }
  //С температурой сложнее, можно как и с режимами сделать 18 условий, поскольку у нас 4 бита которые равны t-17 в двоичной, то проще высчитать
  //SetBits передается число, сколько битов изменить, начиная с какого бита, ну и сдвиг для инверсии
  SetBits(TEMP-17,4,16,8);

  //Экономия в отличие от ВКЛ-ВЫКЛ дублируется единицей (активна) на 32 и 34 бите
  SetBit(ECON,32,8);
  SetBit(ECON,34,8);
   
  //Воздушный поток меняет 35 и 36 биты, при этом норма 35=1, 36=0, пол силы 0 и 0, сильный поток 0,1
  //Чтобы не лепить условий, раз биты подряд, записываем, не забывая что функция записи инвертит порядок битов
  //B10 - нормальный, B00 - пол силы, B01 - сильный
  SetBits(AIRFLOW,2,35,8);

  //По той же схеме записываем скорость вентилятора, живущую в 33 и 34 битах
  SetBits(FANSPEED,2,33,8);

  //HIGHPOWER кроме того что живет на 32 бите, в случае включения зануляет 33 и 34, переводя мощность вентилятора в auto
  SetBit(HIGHPOWER,32,8);
  if (HIGHPOWER) {SetBits(0,2,33,8);}

  //Записываем полученные биты, помня, что данные у нас 4го элемента массива с шагом через один записываются как 1400 при нуле и 3400 при единице
  for (int i = 0; i < sizeof(bits) - 1; i++){
    IRsignal[3+i*2]=bits[i] ? 3400 : 1400;
  }
  
  irsend.sendRaw(IRsignal, IRLength, IRkhz); 
}

//Записывает бит в указанный адрес, а так же его инверсию в бит со сдвигом на shift
void SetBit(bool Value, unsigned int Adress, unsigned int Shift)
{
  bits[Adress]=Value;
  bits[Adress+Shift]=!Value;
}

//Записывает несколько битов
void SetBits(unsigned int Value, unsigned int Bits, unsigned int Adress, unsigned int Shift)
{
  for (int x=0; x<Bits; x++)
  {
    SetBit(bitRead(Value,Bits-x-1),Adress+x,Shift); //Инвертируя, если нужно без инверсии, то читаем бит x
  }
}


void loop() {

}