Это небольшой набросок рассказа о " Комфортный дом Просто иллюстрация того, что даже имея не слишком большие знания и опыт, можно чего-то добиться.
Другими словами, достаточно настойчивый дятел убьет любое дерево.
Все началось с простого желания управлять светом в доме с помощью Arduino. Включая переключатели Ливоло , купил еще до этой безумной идеи с домашней автоматикой.
Но, в отличие от радиорозеток, «щелкнуть» их с помощью моей любимой библиотеки RC-Switch не получилось, а поиск других готовых решений показал их полное отсутствие.
А китайские производители и продавцы на вопрос о протоколе ответили, что эта штука работает на частоте 433 МГц.
Не очень полезная информация.
Однако я не буду претендовать на святую невинность.
Ведь вместе с Ардуино я купил еще пару блоков по четыре реле, чтобы в случае чего тривиально замкнуть выбранные кнопки пульта.
И это, кстати, достаточно популярное решение, потому что быстрое, относительно дешевое и очень сердито.
Но в душе я стремился к красоте.
Как ни странно, помогла обычная зубочистка, два резистора и один конденсатор.
Эпические неудачи
Однако сначала я не подумал о зубочистках.Но в процессе чтения чего-либо более-менее простого о декодировании радиопротоколов я наткнулся на замечательный ресурс NetHome. И там автор публикует, во-первых, схема делителя , позволяющая записывать демодулированный сигнал с ресивера на компьютер через обычный микрофонный вход и одновременно простую утилиту Анализатор протоколов для записи и анализа сигнала.
.
виновники торжества
Итак, я собрал делитель, подключил его к ноуту, нажал кнопку пульта и начал смотреть результаты – благо модуляция (амплитудная) пульта совпала с модуляцией приёмника.
Анализатор протоколов на самом деле довольно крутая штука.
Программа определяет наиболее популярные протоколы, и если встретит неизвестный, можно просмотреть «осциллограмму» с разбивкой по импульсам.
К сожалению, она не знала протокола Ливоло.
И это меня даже немного смутило, так как не показывало четко истинную форму сигнала с пульта Livolo. Это оказалось случайностью, когда мне пришло в голову посмотреть сигнал еще и в Мужество .
Здесь импульсы стали отчетливо видны и, мне кажется, причина бед «Анализатора протоколов» очевидна: крайне малая длительность этих самых импульсов — от 100 до 500 микросекунд. В том же редакторе я решил пойти по простому пути — записать полученный сигнал в WAV, а затем воспроизвести его с помощью Arduino на пине, к которому подключен передатчик.
Ведь у меня был Ethernet шилд со слотом microSD, вполне подходящий для «плеера».
Немного поискав и я нашел «музыкальную» библиотеку ТМРпкм .
.
вот что показал анализатор протоколов
.
сравни с Аудасити
Идея основывалась на том, что схожие по сути ИК-протоколы можно успешно и довольно просто смоделировать, подключив ИК-светодиод к аудиовыходу компьютера или другого гаджета, способного воспроизводить звук с подходящими параметрами.
Сказано - сделано.
И отложим: переключатели не поняли шутки, а просмотр излучаемого таким образом сигнала в Audacity показал, что форма импульсов слишком искажена.
Повторение формы
Тогда я решил пойти на крайние меры.А именно, глупо повторять форму сигнала, не выходя на логический уровень.
Для этого необходимо и довольно сложно закодировать сообщения кода в скетче Arduino. И здесь мне очень повезло.
Если посмотреть кодовое сообщение пульта Livolo, то можно заметить, что оно состоит из множества (около 100) многократно повторяющихся пакетов импульсов.
Итак, все пакеты в кодовом пакете совершенно одинаковы – это своего рода защита от помех: чрезмерное количество пакетов гарантирует, во-первых, надежный захват сигнала АРУ приемника, а, во-вторых, прием самой команды.
.
.
Вот такая картина, если нажать кнопки подряд
.
Понять, где сигнал, а где шум, довольно просто.
Здесь вы можете оценить масштаб катастрофы: сигнал — всего одна кнопка
Но не только пакеты импульсов внутри одного пакета кода оказались идентичными.
Livolo также использует систему фиксированных кодов, то есть одной кнопке пульта всегда соответствует один и тот же пакет импульсов.
Проверить это легко: достаточно нажать одну и ту же кнопку несколько раз и сравнить результаты.
В моем случае все они оказались совершенно идентичными.
.
повторение – не только мать учения, но и залог уверенного приема сигнала
Это то, что я называю удачей: фиксированный код без каких-либо ухищрений.
Таким образом, нужно было последовательно нажать и записать сигнал всех необходимых кнопок на пульте в Audacity, а затем посчитать количество импульсов в пакете каждой кнопки, узнать их длительность и передать все это в код Arduino. .
Для этого требовался инструмент, достаточно тонкий, чтобы не блокировать обзор сигнала Audacity, и достаточно нейтральный, чтобы не поцарапать дисплей ноутбука в процессе.
И вот настал звездный час зубочистки.
На самом деле искусство подсчета импульсов одними глазами мне недоступно, но если подвигать указатель, то это очень даже неплохо.
В одной руке он держал зубочистку, а другой тут же записывал результаты.
При достаточном увеличении видно, что пакет состоит из пяти типов импульсов (условно: длинный вниз, короткий вверх, короткий вниз, средний вверх, средний вниз).
.
Если увеличить масштаб еще больше, то можно на глазок оценить длину импульсов по линии Audacity, что я и сделал для всех пяти.
Кроме того, каждому импульсу был присвоен порядковый номер — это основано на использовании переменных байтового типа в целях экономии памяти Arduino. Я только сейчас подумал, что можно будет разделить на 10 и не заморачиваться насчет «сокращений».
.
Теоретические границы импульсов выделены синим и красным цветом, так как в идеале фронты должны быть вертикальными, но это если нет радиоканала
Работа оказалась не столько интеллектуальной, сколько муторной.
Количество отдельных импульсов «переплывало» от кнопки к кнопке.
И хотя я предполагал, что с разумной точки зрения этого быть не должно, до логического уровня анализа я не дошел.
Я просто закодировал результат и опробовал его.
Ничего не получалось с первого раза.
Однако это было ожидаемо.
Чего я не ожидал, так это того, что со второго раза все получится.
Но дело оказалось в том, что при прямом кодировании (т.е.
если импульс направлен вверх, кодируем OUTPUT/HIGH) сигнал получился перевернутым - очевидно, это особенность передатчика.
Решить это было проще простого: инвертируем уровни в коде (т.е.
кодируем восходящий импульс OUTPUT/LOW).
Сравнение симуляции и оригинального сигнала (в Audactiy, на глазок) также показало небольшое несоответствие длины импульсов — это я тоже исправил.
Первая версия, великая и ужасная
Последним штрихом этого этапа стал ввод кодовых последовательностей во флэш-память Arduino (PROGMEM), чтобы не занимать драгоценную оперативную память.int txPin = 9; // pin connected to RF transmitter int i; // counter to send command pulses int pulse; // count pulse repetitions int incomingByte = 0; // for incoming serial data // hard coded commands (see txButton): 1 - pulse start, 2 - zero, 3 - one, 4 - pause, 5 - low int button1[45]={44, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2}; int button2[43]={43, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2}; int button3[41]={40, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 5, 3, 4, 2, 4, 2, 4, 2}; int button4[43]={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 4, 2, 4, 2}; int button5[43]={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2}; int button6[43]={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 4, 2, 4, 2, 4, 2}; int button7[41]={40, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 5, 3, 4, 2, 4, 2}; int button8[43]={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 4, 2}; int button9[43]={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2}; int button10[43]={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 2, 4, 3, 4, 2, 4, 2, 4, 2}; int button11[41]={40, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 5, 2, 4, 3, 4, 2}; void setup () { pinMode(txPin, OUTPUT); Serial.begin(9600); Serial.println("Number = button; a to press 0; b to shut off all"); } void loop(){ if (Serial.available() > 0) { // read the incoming byte: incomingByte = Serial.read(); switch(incomingByte) { case 49: txButton(button1); Serial.println("Switching on 1"); break; case 50: txButton(button2); Serial.println("Switching on 2"); break; case 51: txButton(button3); Serial.println("Switching on 3"); break; case 52: txButton(button4); Serial.println("Switching on 4"); break; case 53: txButton(button5); Serial.println("Switching on 5"); break; case 54: txButton(button6); Serial.println("Switching on 6"); break; case 55: txButton(button7); Serial.println("Switching on 7"); break; case 56: txButton(button8); Serial.println("Switching on 8"); break; case 57: txButton(button9); Serial.println("Switching on 9"); break; case 97: txButton(button10); Serial.println("Switching on 0"); break; case 98: txButton(button11); Serial.println("Switching All off"); break; } } // end if serial available }// end void loop // transmit command. Due to transmitter (or something, I don't know) transmission code should be INVERTED. Ex: one is coded as LOW-delay->HIGH instead of HIGH-delay-LOW void txButton(int cmd[]) { Serial.print("Processing. Array size is "); Serial.println(cmd[0]); digitalWrite(txPin, HIGH); // not sure if its required, just an attempt to start transmission to enable AGC of the receiver delay(1000); for (pulse= 0; pulse <= 100; pulse=pulse+1) { // repeat command 100 times for (i = 1; i < cmd[0]+1; i = i + 1) { // transmit command switch(cmd[i]) { case 1: // start digitalWrite(txPin, HIGH); delayMicroseconds(550); digitalWrite(txPin, LOW); // Serial.print("s"); break; case 2: // "zero", that is short high spike digitalWrite(txPin, LOW); delayMicroseconds(110); digitalWrite(txPin, HIGH); // Serial.print("0"); break; case 3: // "one", that is long high spike digitalWrite(txPin, LOW); delayMicroseconds(303); digitalWrite(txPin, HIGH); // Serial.print("1"); break; case 4: // pause, that is short low spike digitalWrite(txPin, HIGH); delayMicroseconds(110); digitalWrite(txPin, LOW); // Serial.print("p"); break; case 5: // low, that is long low spike digitalWrite(txPin, HIGH); delayMicroseconds(290); digitalWrite(txPin, LOW); // Serial.print("l"); break; } } } }
В таком виде код продержался, по-моему, больше полугода, а потом мне надоело, и вообще мне снова захотелось чего-то красивого.
Вторая версия с PROGMEM #include <avr/pgmspace.h> // needed to use PROGMEM
#define txPin 8 // pin connected to RF transmitter (pin 8)
byte i; // command pulses counter for Livolo (0 - 100)
byte pulse; // counter for command repeat
// commands stored in PROGMEM arrays (see on PROGMEM use here: http://arduino.cc/forum/index.phpЭtopic=53240.0 )
// first array element is length of command
const prog_uchar button1[45] PROGMEM ={44, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2};
const prog_uchar button2[43] PROGMEM ={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2};
const prog_uchar button3[41] PROGMEM ={40, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 5, 3, 4, 2, 4, 2, 4, 2};
const prog_uchar button4[43] PROGMEM ={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 4, 2, 4, 2};
const prog_uchar button5[43] PROGMEM ={42, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2};
const prog_uchar button7[41] PROGMEM ={40, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 5, 3, 4, 2, 4, 2};
const prog_uchar button11[41] PROGMEM ={40, 1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5, 3, 4, 2, 5, 2, 4, 3, 4, 2};
// pointers to command arrays
PROGMEM const prog_uchar *buttonPointer[] = {button1, button2, button3, button4, button5, button7, button11};
void setup() {
// sipmle example: send button "button2" once. Note that array elements numbered starting from "0" (so button1 is 0, button2 is 1 and so on)
txButton(1);
}
void loop() {
}
// transmitting part
// zeroes and ones here are not actual 0 and 1. I just called these pulses for my own convenience.
// also note that I had to invert pulses to get everything working
// that said in actual command "start pulse" is long low; "zero" = short high; "one" = long high; "pause" is short low; "low" is long low.
void txButton(byte cmd) {
prog_uchar *currentPointer = (prog_uchar *)pgm_read_word(&buttonPointer[cmd]); // current pointer to command array passed as txButton(cmd) argument
byte cmdCounter = pgm_read_byte(¤tPointer[0]); // read array length
for (pulse= 0; pulse <= 180; pulse = pulse+1) { // how many times to transmit a command
for (i = 1; i < cmdCounter+1; i = i + 1) { // counter for reading command array
byte currentCmd = pgm_read_byte(¤tPointer[i]); // readpulse type from array
switch(currentCmd) { // transmit pulse
case 1: // start pulse
digitalWrite(txPin, HIGH);
delayMicroseconds(550);
digitalWrite(txPin, LOW);
break;
case 2: // "zero"
digitalWrite(txPin, LOW);
delayMicroseconds(110);
digitalWrite(txPin, HIGH);
break;
case 3: // "one"
digitalWrite(txPin, LOW);
delayMicroseconds(303);
digitalWrite(txPin, HIGH);
break;
case 4: // "pause"
digitalWrite(txPin, HIGH);
delayMicroseconds(110);
digitalWrite(txPin, LOW);
break;
case 5: // "low"
digitalWrite(txPin, HIGH);
delayMicroseconds(290);
digitalWrite(txPin, LOW);
break;
}
}
}
digitalWrite(txPin, LOW);
}
Выделение общего
Еще раз просматривая WAV, я особо ни на что не надеялся.Однако более тщательное, чем раньше, сравнение кодовых сообщений преподнесло приятный сюрприз.
Часть последовательности в начале каждого пакета импульсов оказалась общей для всех пакетов одного кодового сообщения, и для всех кнопок пульта в целом.
.
вырезаем разные пуговицы - и сразу видно, что часть упаковки не меняется
До понимания протокола было еще далеко, но это небольшое открытие позволило еще больше сэкономить память контроллера.
Я просто помещал отдельную часть посылки в отдельный массив, который автоматически «воспроизводился» перед каждой уникальной частью.
Основная часть теперь обосновалась на отдельной территории.
#include <avr/pgmspace.h> // needed to use PROGMEM
#define txPin 8 // pin connected to RF transmitter (pin 8)
byte i; // command pulses counter for Livolo (0 - 100)
byte pulse; // counter for command repeat
// commands stored in PROGMEM arrays (see on PROGMEM use here: http://arduino.cc/forum/index.phpЭtopic=53240.0 )
// first array element is length of command
const prog_uchar start[30] PROGMEM = {1, 2, 4, 2, 4, 2, 4, 3, 5, 2, 4, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2}; // remote ID - no need to store it with each command
const prog_uchar button1[15] PROGMEM ={14, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2}; // only command bits
const prog_uchar button2[13] PROGMEM ={12, 5, 3, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2};
const prog_uchar button3[11] PROGMEM ={10, 5, 3, 5, 3, 4, 2, 4, 2, 4, 2};
const prog_uchar button4[13] PROGMEM ={12, 4, 2, 4, 2, 5, 3, 4, 2, 4, 2, 4, 2};
const prog_uchar button5[13] PROGMEM ={12, 5, 2, 4, 3, 4, 2, 4, 2, 4, 2, 4, 2};
const prog_uchar button7[11] PROGMEM ={10, 5, 3, 4, 2, 5, 3, 4, 2, 4, 2};
const prog_uchar button11[11] PROGMEM ={10, 5, 3, 4, 2, 5, 2, 4, 3, 4, 2};
// pointers to command arrays
PROGMEM const prog_uchar *buttonPointer[] = {start, button1, button2, button3, button4, button5, button7, button11};
void setup() {
// sipmle example: send button "button2" once. Note that array elements numbered starting from "0" (so button1 is 0, button2 is 1 and so on)
// Serial.begin(9600);
}
void loop() {
txButton(3);
delay(1000);
}
// transmitting part
// zeroes and ones here are not actual 0 and 1. I just called these pulses for my own convenience.
// also note that I had to invert pulses to get everything working
// that said in actual command "start pulse" is long low; "zero" = short high; "one" = long high; "pause" is short low; "low" is long low.
void txButton(byte cmd) {
prog_uchar *currentPointer = (prog_uchar *)pgm_read_word(&buttonPointer[cmd]); // current pointer to command array passed as txButton(cmd) argument
byte cmdCounter = pgm_read_byte(¤tPointer[0]); // read array length
prog_uchar *currentPointerStart = (prog_uchar *)pgm_read_word(&buttonPointer[0]); // current pointer to start command array
for (pulse= 0; pulse <= 180; pulse = pulse+1) { // how many times to transmit a command
for (i = 0; i<30; i=i+1) {
byte currentCmd = pgm_read_byte(¤tPointerStart[i]);
sendPulse(currentCmd);
// Serial.print(currentCmd);
// Serial.print(", ");
}
for (i = 1; i < cmdCounter+1; i = i + 1) { // counter for reading command array
byte currentCmd = pgm_read_byte(¤tPointer[i]); // readpulse type from array
sendPulse(currentCmd);
// Serial.print(currentCmd);
// Serial.print(", ");
}
}
}
void sendPulse(byte txPulse) {
switch(txPulse) { // transmit pulse
case 1: // start pulse
digitalWrite(txPin, HIGH);
delayMicroseconds(550);
digitalWrite(txPin, LOW);
break;
case 2: // "zero"
digitalWrite(txPin, LOW);
delayMicroseconds(110);
digitalWrite(txPin, HIGH);
break;
case 3: // "one"
digitalWrite(txPin, LOW);
delayMicroseconds(303);
digitalWrite(txPin, HIGH);
break;
case 4: // "pause"
digitalWrite(txPin, HIGH);
delayMicroseconds(110);
digitalWrite(txPin, LOW);
break;
case 5: // "low"
digitalWrite(txPin, HIGH);
delayMicroseconds(290);
digitalWrite(txPin, LOW);
break;
}
digitalWrite(txPin, LOW);
}
Несмотря на некоторый успех и то, что код работал вполне хорошо и хлеба не просил, меня продолжало смутно беспокоить то, что количество импульсов в посылках было разным.
С одной стороны, ничто не мешало протоколу иметь такую особенность, с другой стороны, под рукой было множество примеров подобных протоколов (от сокетов, например, и метеостанций), где прослеживалась тенденция к 24-битности.
отправка.
Поиск шаблонов
Третий подход к машине заключался в том, что я пытался разгадать логику конструктора протоколов.С самого начала мне было удобнее думать, что короткие импульсы означают «0», а средней длительности — «1».
При этом самый длинный импульс в пакете я взял за стартовый и не нагружал его каким-либо другим смыслом.
Осталось разобраться, почему совмещаются импульсы вверх и вниз (при разной длительности в этом вроде нет необходимости), и как вообще это понять.
Процесс завершился следующими выводами: 1. Существует четкое правило последовательности импульсов: за импульсом «вверх» всегда следует импульс «вниз», независимо от длительности импульса.
2. Два коротких импульса подряд в моей системе координат означают «0».
3. Аналогично, каждый импульс средней длительности означает «1».
4. Самый длинный импульс отправки – это старт или стоп, который не имеет значения и зависит только от точки зрения.
Если применить эти правила к импульсному пакету, то станет ясно, что его общая длина всегда равна 24 битам, включая старт-стоп.
Из них 16 бит — это обнаруженная ранее «фиксированная» часть и 7 бит — уникальная часть для каждой цифровой кнопки пульта дистанционного управления.
Собственно, постоянная длина пакета привела меня к выводу, что идентификация логического уровня прошла успешно.
.
по всем правилам
Из «формата» пакета естественным образом следовало, что 16-битный фрагмент, скорее всего, является идентификатором пульта, позволяющим использовать несколько пультов в одной квартире или не беспокоить соседей, если у них одинаковые выключатели.
К счастью, у меня в руках оказалась запись еще одного пульта, из которой следовало, что коды цифровых кнопок на обоих пультах одинаковы.
Все вместе означает, что есть прекрасная возможность имитировать практически неограниченное количество пультов Livolo, в зависимости от собственных фантазий и потребностей.
Главное соблюдать правило: 16 бит — это идентификатор пульта, и использовать либо известные кнопки, либо генерировать их по принципу «кнопки» из 7 бит. Однако практика показала, что не все 16-битные идентификаторы пульта подходят. Но это не так уж и страшно: согласно той же практике, найти подходящий идентификатор не составляет особой сложности.
Остается только переписать код и избавиться наконец от этих ужасных, корявых массивов.
И вот результат #define txPin 8 // pin connected to RF transmitter (pin 8)
byte i; // just a counter
byte pulse; // counter for command repeat
boolean high = true; // pulse "sign"
// keycodes #1: 0, #2: 96, #3: 120, #4: 24, #5: 80, #6: 48, #7: 108, #8: 12, #9: 72; #10: 40, #OFF: 106
// real remote IDs: 6400; 19303
// tested "virtual" remote ID: 8500, other IDs could work too, as long as they do not exceed 16 bit
// known issue: not all 16 bit remote ID are valid
// have not tested other buttons, but as there is dimmer control, some keycodes could be strictly system
// use: sendButton(remoteID, keycode);
// see void loop for an example of use
void setup() {
}
void loop() {
sendButton(6400, 120); // blink button #3 every 3 seconds using remote with remoteID #6400
delay(3000);
}
void sendButton(unsigned int remoteID, byte keycode) {
for (pulse= 0; pulse <= 180; pulse = pulse+1) { // how many times to transmit a command
sendPulse(1); // Start
high = true; // first pulse is always high
for (i = 16; i>0; i--) { // transmit remoteID
byte txPulse=bitRead(remoteID, i-1); // read bits from remote ID
selectPulse(txPulse);
}
for (i = 7; i>0; i--) { // transmit keycode
byte txPulse=bitRead(keycode, i-1); // read bits from keycode
selectPulse(txPulse);
}
}
digitalWrite(txPin, LOW);
}
// build transmit sequence so that every high pulse is followed by low and vice versa
void selectPulse(byte inBit) {
switch (inBit) {
case 0:
for (byte ii=1; ii<3; ii++) {
if (high == true) { // if current pulse should be high, send High Zero
sendPulse(2);
} else { // else send Low Zero
sendPulse(4);
}
high=!high; // invert next pulse
}
break;
case 1: // if current pulse should be high, send High One
if (high == true) {
sendPulse(3);
} else { // else send Low One
sendPulse(5);
}
high=!high; // invert next pulse
break;
}
}
// transmit pulses
// slightly corrected pulse length, use old (commented out) values if these not working for you
void sendPulse(byte txPulse) {
switch(txPulse) { // transmit pulse
case 1: // Start
digitalWrite(txPin, HIGH);
delayMicroseconds(500); // 550
digitalWrite(txPin, LOW);
break;
case 2: // "High Zero"
digitalWrite(txPin, LOW);
delayMicroseconds(100); // 110
digitalWrite(txPin, HIGH);
break;
case 3: // "High One"
digitalWrite(txPin, LOW);
delayMicroseconds(300); // 303
digitalWrite(txPin, HIGH);
break;
case 4: // "Low Zero"
digitalWrite(txPin, HIGH);
delayMicroseconds(100); // 110
digitalWrite(txPin, LOW);
break;
case 5: // "Low One"
digitalWrite(txPin, HIGH);
delayMicroseconds(300); // 290
digitalWrite(txPin, LOW);
break;
}
}
Мы жертвуем библиотеке
В принципе, можно было бы остановиться на новом коде, но он все равно загромождал основную программу, и другим желающим его использовать пришлось бы прибегать к ненужному копипасту.Поэтому я решил немного потренироваться «на котах» и превратить это в отдельную библиотеку.
В этом процессе неоценимую помощь оказали инструкции на сайте Arduino.cc .
На русском инструкция опубликована на Arduino.ru .
Получилось точно по рецепту (ни шагов в сторону, ни прыжков на месте).
Файл h, cpp, readme и небольшой пример, показывающий, как всем этим счастьем пользоваться.
Лилволо.
h /*
Livolo.h - Library for Livolo wireless switches.
Created by Sergey Chernov, October 25, 2013.
Released into the public domain.
*/
#ifndef Livolo_h
#define Livolo_h
#include "Arduino.h"
class Livolo
{
public:
Livolo(byte pin);
void sendButton(unsigned int remoteID, byte keycode);
private:
byte txPin;
Теги: #Сделай сам или Сделай сам #сделай сам #программирование #Умный дом #arduino
-
Веб-Индустрия Индии В 21 Веке
19 Oct, 24 -
Идеальный Android-Плеер Для Немузыки
19 Oct, 24 -
Мгтс, Забудь Обо Мне Навсегда!
19 Oct, 24 -
7 Маленьких Хитростей При Работе В Windows
19 Oct, 24 -
Каталог Брендов За 250$ (С Доменами .Com)
19 Oct, 24 -
Функциональный C#: Неизменность
19 Oct, 24