Esp 12e как передавать данные между модулями

Для универсального пульта, над проектом которым я работал, не хватало ретранслятора IR кодов. Сигналы WiFI пробивают стены, а вот с инфракрасными лучами сложнее — только прямая видимость или, в крайнем случае, отраженный сигнал. Поскольку такая задача может иметь и самостоятельное значение, то я буду описывать ее как отдельный, самостоятельный проект.

Hardware/железо Software/программы Описание
ESP8266 12E
ведущий
#include ESP8266WiFi.h
#include ESP8266mDNS.h
#include pgmspace.h //PROGMEM
/>
IR приемник #include IRremoteESP8266.h GPIO2 (pin D4)
второй комплект
ESP8266 12E
ведомый
#include ESP8266WiFi.h
#include ESP8266mDNS.h
#include pgmspace.h //PROGMEM
/>
IR излучатель #include IRremoteESP8266.h GPIO15 (pin D8)

В программе ведущего модуля (ESP_reciver_trans.ino) измените подключение излучателя, например, также на GPIO15 (pin D8), как и у ведомого модуля. Эта инициализация у меня осталась от универсального пульта, где был дефицит со свободными ножками. Когда излучатель подключен к GPIO0, то это мешает запуску модуля после его прошивки — приходится все время переключаться.

Сам проект, по сути, простой и понятный, но я столкнулся с некоторыми проблемами, о решении которых и пойдет речь.

Первая и непонятная проблема связана с raw массивами. При повторном декодировании IR сигнала программа сбрасывалась и перезапускалась. Пришлось ввести промежуточную временную переменную temp_int_array[76]

Формирование IR массива

String temp_str = "";
unsigned int temp_int_array[76];

//подпрограмма формирования последовательности импульсов и передачи на излучатель
//cod_len — длина массива, вычисляется при декодировании IR сигнала
void send_ircod() < //max_len — запиши значение, чтобы не было ошибки компиляции
for (int i = 0; i temp_int_array[i] = irSignal[i] * USECPERTICK;//в оригинале: USECPERTICK — MARK_EXCESS;
//формирование строки для wifi
temp_str += String(irSignal[i]);
temp_str += ‘;’;
>
//Serial.println( temp_str); //test
irsend.sendRaw( temp_int_array , cod_len, 38);
>

В конечном итоге получилось так

Формирование IR массива

В программе (смотри скетч) под комментариями есть предложения разработчиков библиотеки по коррекции значений массива, четных и нечетных импульсов. Для информации я их оставил, но сам не использовал.

Следующая проблема вскрылась при опробовании излучателя на основном модуле. Проблема связана, скорее всего, с IR библиотекой. Считывание raw массива необходимо производить с индекса = 1, а на излучатель библиотека считывает массив с нулевого индекса (в предыдущем примере for (int i = 0; i .

Далее, я обратил внимание, что посылая на декодер одну и ту же команду, импульсы на излучателе становились с каждым разом длиннее. Пришлось включить осциллограф и убедиться в этом. Оказалось, что, несмотря на фиксированный, заданный в программе, размер массива, запись в цикле увеличивает этот массив, как бы смещая указатель массива. Пришлось обойти эту проблему, предварительно обнуляя массив irSignal[i]

Получение кодов IR массива

Может я решил эту задачу не самым оптимальным способом, но на данном этапе такой способ мне понятнее — это передача строковой переменной. Для этого значения последовательности импульсов записаны в строку, разделенные точкой с запятой (;). Строка для передачи выглядит примерно так:

Добавив служебные метки, информацию о последовательности (длина кодовой последовательности, частота) пересылаем на IP адрес другого (ведомого) модуля:

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

Получение кодов IR массива

//обнуление массива
for (int i = 0; i 76; i++) temp_int_array[i] = (char)0;
//=================
int count_arr = 0;
ind1 = priem.indexOf(‘;’); //обязательно перед циклом
while (ind1 = 0) <
temp_str = priem.substring(0, ind1); //записали цифровое значение импульса)
priem.remove(0, ind1 + 1); //удалили считанную часть
temp_int = temp_str.toInt();
temp_int_array[count_arr] = (unsigned int)temp_int;
// (unsigned int)temp_str.toInt;
count_arr += 1;
ind1 = priem.indexOf(‘;’);
>
//посылаем массив на излучатель паетом из трех посылок
for (int i = 0; i 2; i++) <
irsend.sendRaw( temp_int_array , cod_len, cod_khz );

delay(47);//47-пауза между кодовыми посылками (samsung)
>

puls

При отладке IR канала я обнаружил, что наименьшее расхождение с несущей частотой (38 КГц) получается при cod_khz=43. На осциллограмме показана разница частот. Кроме того, у Sony импульсы не меандр. Это тоже можно регулировать, но уже в библиотеке: IRremoteESP8266.cpp

В основном модуле я жестко задал значение коэффициента несущей частоты cod_khz=43. Других частот, на других пультах, у меня не было.

puls2

Теперь о формировании самих кодовых импульсов. Здесь тоже пришлось подгонять. На рисунке, сверху вниз: реальный код с пульта SONY, подогнанный код с указанными коэффициентами и красным цветом полученный через библиотеку код без коррекции. Видно, что код без коррекции несколько растянут.

Формирование кодовых импульсов происходит в основном модуле. Есть три варианта, можете выбрать любой:


for (int i = 0; i // irSignal[i] = int_temp * USECPERTICK + MARK_EXCESS; //вариант разработчиков библиотеки
// irSignal[i] = int_temp * USECPERTICK + MARK_EXCESS /2 ; //максимально близкий к оригиналу
irSignal[i] = int_temp * USECPERTICK; //оптимальный
> else < //нечетные, Space
// irSignal[i] = int_temp * USECPERTICK — MARK_EXCESS; //вариант разработчиков библиотеки
// irSignal[i] = int_temp * USECPERTICK — 150; //максимально близкий к оригиналу
irSignal[i] = int_temp * USECPERTICK — 45 ; //оптимальный — подогнал по осциллографу
>
>
temp_str += String(irSignal[i]) + ‘;’;
>

Когда я начал практически использовать пульт, то оказалось, что одной посылки кода недостаточно, необходимо передавать пакет из двух-трех посылок. После того, как я это реализовал, все заработало отлично!

ir_pult

В программе пульту ретранслятора присвоен статический IP адрес 192.168.0.166. На пульте есть общий выключатель ретранслятора и четыре строки с IP адресами WiFi модулей с излучателями IR сигналов.

На каждой панели с IP адресом имеются две кнопки: слева — выбор для изменения в окне редакции, справа — разрешение на ретрансляцию.

Запись каждой панели можно изменить и передать в память модуля: переменные ex_TD[1] — ex_TD[4]. Также в памяти модуля хранятся состояния флажков панелей: переменные cb_TD[1] — cb_TD[4]. Состояние общего выключателя фиксируется в переменной cb_TD[0]. Далее, по желанию можно их запомнить, используя EEPROM или SD card. Главное, что все эти данные доступны и могут быть использованы, в зависимости от конфигурации оборудования.

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

Не увлекайтесь установкой автоматического опроса, чтобы не увеличивать время реакции модуля на ваши действия.

И последнее, чтобы ретранслятор работал для модуля 192.168.0.130 без загрузки страницы в браузер, следует в программе установить: boolean cb_TD[5] = <1, 1, 0, 0, 0>; //[0] — это выключатель

В составе универсального пульта данные для ретранслятора могут иметь два различных источника:
— "живой сигнал", поступающий непосредственно от IR приемника и декодируемый библиотекой;
— сигнал, записанный на SD карту для какого-либо IR пульта.

В каждом из этих случаев сигнал в виде преобразованной raw последовательности может ретранслироваться на другие ESP модули только при разрешенных условиях, которые предварительно выставляются на пульте "transponder".

В режиме сканирования IR команд и записи их на SD карту, ретрансляция IR команд запрещена.

Желая сохранить установки пульта в памяти или на SD карте, я столкнулся с проблемой кодирования строковых переменных при передачи Get запросов от браузера после нажатия на кнопкку "ЗАПИСАТЬ".

Если передать " проба", то в ESP8266 получим вот такую последовательность "%20%D0%BF%D1%80%D0%BE%D0%B1%D0%B0" . Если попытаться ее записать обратно, то она так и будет выглядеть в искаженном виде. Немного подергавшись, в том числе и в Инете, я написал декодер под Win1251. Но это меня не устроило, так как требовалась кодировка utf-8. Чтобы текстовые файлы с SD карты правильно отображались в браузере, файлы должны быть в utf-8 кодировке. Пришлось еще немного повозиться написать функции String decode_(String mm) и int decode_int(char ch2, char ch2).

String decode_(String mm) <
String result_win = "";
String result = "";
char inChar;
mm.replace("%20", " "); //декодировать все пробелы
for (int i = 0; i mm.length(); i++) <
inChar = mm[i];
if (inChar == ‘%’) <
ind2 = decode_int(char(mm[i + 1]), char(mm[i + 2]));
//Serial.print(":ind2:" );
//Serial.println(ind2);
if (ind2 == 208) < //вылавливаем русский код
result += char(ind2); //utf-8 — первый байт
ind2 = decode_int(char(mm[i + 4]), char(mm[i + 5])); //чтение второго байта после %
result += char(ind2); //utf-8 -второй байт
//ind2 = decode_int(char(mm[i + 4]), char(mm[i + 5])) + 48;
ind2 += 48; //win-1251
i += 3; //прошли еще три символа
> else <
if (ind2 == 209) < //вылавливаем русский код, начиная с р
result += char(ind2); //utf-8 — первый байт
ind2 = decode_int(char(mm[i + 4]), char(mm[i + 5])); //чтение второго байта после %
result += char(ind2); //utf-8 -второй байт + 112
ind2 += 112; //win-1251
i += 3; //прошли еще три символа
>
>
result_win += char(ind2); //преобразовали в Win1251
i += 2; //прочли группу с первыми двумя символами
> else < //все остальные, кроме русских, символы
result += inChar;
result_win += inChar; //преобразовали в Win1251
>
>
return result; //строка в кодировке utf-8
//result_win — строка в кодировке Win1251
>

int decode_int(char ch2, char ch2) <
//123%D0%B6%D0%96zZ == 123жЖzZ
//сюда попадает только пара символов после % int result;
if (ch2> 57) < //D from D0
result = (ch2 — 55) * 16;
> else <
result = (ch2 — 48) * 16;
>
if (ch2> 57) <
result = result + (ch2 — 55);
> else < //0 from D0
result = result + (ch2 — 48);
>
return result;
>


Источник: ruben1.narod.ru