/* * Граббер брелков-открывашек всего управляемого по 433,92 МГц * с отображением на LCD_1602 (начат 01.07.2017) */ // Передатчик подключен к Arduino Pin #10 (EN подключен на Pin #4 - см.ниже) #define SendPin 10 // Приемник использует прерывание 0 и подключен к Arduino pin #2 // прерывания : 0 (цифровой порт 2), 1 (цифровой порт 3) #define RcvInt 0 // определение пинов подключения кнопок клавиатуры #define kLeft 9 #define kRight 8 #define kGet 6 #define kBack 7 // сюда подключен вход разрешения работы передатчика (EN) pin #4 #define TransmitterEN 4 // сюда подключена пьезо пищщалка #define Buzzer 5 // сюда подключен транзистор управляющий вибратором #define VibroMotor 5 #include #include #include #include //#include #include // +----- распределение EEPROM (1024 байт) -----+ // | 010 позиция Send main code | // | 015 I2C адрес экрана (int, 0x27 или 0x3F) | // | 020 позиция Send saved code | // | 050-0xx параметры настроек меню Settings | // | (по 4 байта на параметр) | // | 100-399 сохраненные для Main code посылки | // | 400-699 принятые для Saved code посылки | // | 1-2 байт : код протокола | // | 3-4 байт : кол-во разрядов посылки | // | 5-12 байты : код посылки | // ---------------------------------------------+ byte MenuItem=1, SubMenuItem=0, savMenuItem=0, savSubMenuItem=0; // нажатые кнопки и длительность их нажатия // код для маски 0bA000BCDE, где (1/0) // A - длинное/короткое нажатие // B - кнопка 1 (kBack) // C - кнопка 2 (kGet) // D - кнопка 3 (kRight) // E - кнопка 4 (kLeft) word pressed_key=0, savpressed_key; // строка текста подменю (вторая строка экрана) char SubMenuText[16]; // xFlag определяет обработку подменю самостоятельно (true) или основным кодом (false) bool xFlag=false; // переменные позиций EEPROM для Send main/saved code word eMainCodePos, eSavedCodePos; // переменные, передаваемые в протокол, для приема-отправки посылки word Tprot, Tclng; unsigned long long Tdata; // параметр для меню настроек int SettingParm; bool BrutDir=false, // направление брута : 0 вперед, 1 назад BrutLevel=false; // скорость перебора : 0 автомат, 1 шаг char pxchar[16] = " "; // пустая строка в 16 знаков char* MainMenu[] = {"Send main code ", // отправка основных сохраненных кодов "Send saved code ", // отправка принятых кодов "Grabbing radio ", // прием кодов "Bruting code ", // брут на кодах 12 бит (7, 8) "Noice Maker ", // глушилка "Copy saved->main", // копирование из принятых в основные "Send user code ", // отправка введенного вручную кода ??? (пока не сделан) "Settings ", // настройки "Clear memory "}; // очистка памяти и сбос в изначальное состояние // число пунктов (строк) в меню word MainMenuSize = sizeof(MainMenu) / sizeof(*MainMenu); // состояние подсветки дисплея - достаём из памяти int BackLight = EEPROM.read(46+(4*1)); // время запуска автоматической подсветки дисплея unsigned long StartMS = millis(); unsigned long CurrentMS = millis(); // состяние пищалки - 0-255 int BeeperEnable = EEPROM.read(46+(4*2)); // состяние вибратора - 0-255 int VibroEnable = EEPROM.read(46+(4*3)); // проверка и возврат I2C адреса LCD из EEPROM // I2C адрес LCD 0x27 или 0x3F для адаптера в зависимости от маркировки микросхемы: // PCF8574T дает адрес 0x27, PCF8574AT - 0x3F при разомкнутых перемычках A0-A1-A2 int GetAdressLCD() { int LCDadress; EEPROM.get(15, LCDadress); if (!(LCDadress==0x27 || LCDadress==0x3F)) LCDadress = 0x3F; // если в EEPROM не 0x27 или 0x3F, то пусть будет 0x3F return LCDadress; } // инициализация дисплея для 16 символов в 2 строки LiquidCrystal_I2C lcd(GetAdressLCD(), 16, 2); // инициализация объекта для работы с RCSwitch RCSwitch mySwitch = RCSwitch(); void ShowMenu(byte iMenu, byte itype) { // значения itype: // 0 строчка основного меню // 1 меню Main // 2 меню Saved // 3 отправка кода Main // 4 отправка кода Saved // 5 меню Bruting // 6 код скопирован в ячейку Main byte pos, codePos; lcd.clear(); lcd.setCursor(0,0); lcd.print(MainMenu[iMenu-1]); lcd.setCursor(0,1); lcd.print(" "); if (SubMenuItem>0) { lcd.setCursor(0,1); lcd.print(SubMenuText); } if (itype==1 || itype==3) { lcd.setCursor(0,1); lcd.print(eMainCodePos+1); codePos=eMainCodePos+1; } if (itype==2 || itype==4) { lcd.setCursor(0,1); lcd.print(eSavedCodePos+1); codePos=eSavedCodePos+1; } if (itype>0 && itype<6) { if (codePos<10) pos=2; else pos=3; lcd.setCursor(pos,1); lcd.print(Tprot); if (Tprot<10) pos+=2; else pos+=3; lcd.setCursor(pos,1); lcd.print(Tclng); if (Tclng<10) pos+=2; else pos+=3; lcd.setCursor(pos,1); if (itype!=5) { lcd.print("code..."); delay(1200); lcd.setCursor(0,1); } if (Tclng>32) lcd.print(long(Tdata>>32), HEX); lcd.print(long(Tdata<<32>>32), HEX); lcd.print(" "); } if (itype==3 || itype==4) { lcd.setCursor(15,1); lcd.print("*"); } if (itype==6) { lcd.setCursor(0, 1); lcd.print(eSavedCodePos+1); lcd.print(" -> Main "); lcd.print(eMainCodePos+1); lcd.print(" "); } savMenuItem=MenuItem; savSubMenuItem=SubMenuItem; } void SaveCode(word eeAdress) // запись кода в EEPROM : адрес EEPROM { EEPROM.put(eeAdress, Tprot); // 2 байта код протокола EEPROM.put(eeAdress+2, Tclng); // 2 байта кол-во разрядов посылки EEPROM.put(eeAdress+4, Tdata); // 8 байт код посылки } void ReadCode(word eeAdress) // чтение кода из EEPROM : адрес EEPROM { EEPROM.get(eeAdress, Tprot); // 2 байта код протокола EEPROM.get(eeAdress+2, Tclng); // 2 байта кол-во разрядов посылки EEPROM.get(eeAdress+4, Tdata); // 8 байт код посылки } void setup() { // проверка и сохранение I2C адреса LCD в EEPROM: 0x27 или 0x3F // !!! чтобы дисплей определился и начал работать - после первой загрузки этого скетча // !!! может потребоваться 1-2 раза перезагрузить ардуину, пока не появится отображение int LCDadress; EEPROM.get(15, LCDadress); Wire.begin(); Wire.beginTransmission(LCDadress); byte error = Wire.endTransmission(); Wire.end(); if (error != 0) { if (LCDadress==0x3F) LCDadress=0x27; else LCDadress=0x3F; EEPROM.put(15, LCDadress); } // инициализация и настройки lcd.init(); lcd.clear(); lcd.backlight(); // lcd.noBacklight(); lcd.setCursor(4,0); lcd.print(F("Good day")); lcd.setCursor(2,1); lcd.print(F("dear friend!")); delay(1500); // инициализируем клавиатуру pinMode(kLeft, INPUT); digitalWrite(kLeft ,HIGH); pinMode(kRight, INPUT); digitalWrite(kRight ,HIGH); pinMode(kGet, INPUT); digitalWrite(kGet ,HIGH); pinMode(kBack, INPUT); digitalWrite(kBack ,HIGH); eMainCodePos=EEPROM.read(10); eSavedCodePos=EEPROM.read(20); mySwitch.disableReceive(); pinMode(TransmitterEN, OUTPUT); digitalWrite(TransmitterEN, LOW); mySwitch.disableTransmit(); // пин вибромоторчика, ставим низкий уровень на выходе pinMode(VibroMotor, OUTPUT); digitalWrite(VibroMotor, LOW); } // Duration - в милисекундах, Frequency - в герцах. // 4100 - это мой конкретный кварц, свой надо в доке смотреть void Beeper(int Duration, int Frequency = 4100) { // надо Duration помножить на BeeperEnable - это и будет время пищщания if(BeeperEnable > 0) tone(Buzzer, Frequency, Duration * BeeperEnable); } void Vibration(int Duration) { // надо Duration помножить на BeeperEnable - это и будет время пищщания if(VibroEnable > 0){ digitalWrite(VibroMotor, HIGH); delay(Duration * VibroEnable); digitalWrite(VibroMotor, LOW); } } void loop() { // проверка состояния подсветки - для автоматического режима CurrentMS = millis(); // текущее время в миллисекундах // если авторежим и если время старта + время горения подсветки меньше текущего времени - гасим // если подстветка отключена - тоже гасим if((BackLight > 1 && StartMS + BackLight * 1000L < CurrentMS) || (BackLight == 0)) lcd.noBacklight(); // если подстветка всегда включена - включаем её if(BackLight == 1) lcd.backlight(); // обработка нажатий клавиш // MenuItem - номер пункта меню // SubMenuItem - номер пункта подменю // xFlag - где обрабатывать результат нажатия кнопок в подменю: // false - здесь, true - дальше в программе // на выходе: pressed_key - нажатые кнопки и длительность их нажатия (см.описание выше) int ik=0; if (digitalRead(kLeft)==false || digitalRead(kRight)==false || digitalRead(kGet)==false || digitalRead(kBack)==false) do { ik++; savpressed_key = (!digitalRead(kLeft) << 0) | (!digitalRead(kRight) << 1) | (!digitalRead(kGet) << 2) | (!digitalRead(kBack) << 3); if (savpressed_key>0) pressed_key = savpressed_key; delay(100); } while (ik<40 && savpressed_key>0); if (ik<3) // ошибка дребезга контактов { savpressed_key = 0; pressed_key = 0; } if (ik>10) // длинное нажатие { pressed_key = pressed_key | 0b10000000; } // кнопка нажата - включаем подсветку и пикаем // проблема - выше был delay(100) - получается нажали кнопку/задержка/пикнули - плохо if( pressed_key != 0) { // зажигаем подсветку - если надо if(BackLight > 1){ StartMS = millis(); lcd.backlight(); } // коротко пикаем Beeper(1); // коротко бжикаем Vibration(1); } // обработка нажатий клавиш для xFlag==false // !!! при самостоятельной обработке подменю не забывать // !!! при завершении установить xFlag в false if ((pressed_key & 0b0001) && xFlag==false) // нажата kLeft { if (SubMenuItem==0 && MenuItem>0) MenuItem--; if (SubMenuItem==0 && MenuItem==0) MenuItem = MainMenuSize; // переход по кольцу при уменьшении первого пункта if (SubMenuItem>0) SubMenuItem--; } if ((pressed_key & 0b0010) && xFlag==false) // нажата kRight { if (SubMenuItem==0 && MenuItem0 && SubMenuItem<9) SubMenuItem++; } if ((pressed_key & 0b1000) && xFlag==false) // нажата kBack { if (SubMenuItem>0) { SubMenuItem=0; memcpy(SubMenuText, pxchar, 16); } digitalWrite(TransmitterEN, LOW); mySwitch.disableTransmit(); mySwitch.disableReceive(); } if ((pressed_key & 0b0100) && xFlag==false) // нажата kGet { if (SubMenuItem==0) { SubMenuItem=1; } } // ============ обработка подменю ============ // --------- подменю Send main code ---------- if (MenuItem==1 && SubMenuItem==1 && xFlag==false) { xFlag=true; // берем управление обработки кнопок на себя pressed_key = 128; } if (xFlag==true && MenuItem==1) { if (SubMenuItem==1 && (pressed_key & 0b1000)) // нажата kBack { xFlag=false; pressed_key=0; SubMenuItem=0; } if (eMainCodePos>0 && (pressed_key & 0b0001)) // нажата kLeft { eMainCodePos--; } if (eMainCodePos<29 && (pressed_key & 0b0010)) // нажата kRight { eMainCodePos++; } if (pressed_key > 0) { ReadCode((eMainCodePos*12)+100); memcpy(SubMenuText, pxchar, 16); if (!(pressed_key & 0b0100)) ShowMenu(MenuItem, 1); } // отправка выбранного в блоке Main кода if (SubMenuItem==1 && (pressed_key & 0b0100)) // нажата kGet { digitalWrite(TransmitterEN, HIGH); mySwitch.enableTransmit(SendPin); mySwitch.setProtocol(Tprot); mySwitch.setRepeatTransmit(5); mySwitch.send(Tdata, Tclng); mySwitch.disableTransmit(); digitalWrite(TransmitterEN, LOW); ShowMenu(MenuItem, 3); } } // --------- подменю Send saved code --------- if (MenuItem==2 && SubMenuItem==1 && xFlag==false) { xFlag=true; // берем управление обработки кнопок на себя pressed_key = 128; } if (xFlag==true && MenuItem==2) { if (SubMenuItem==1 && (pressed_key & 0b1000)) // нажата kBack { xFlag=false; pressed_key=0; SubMenuItem=0; } if (eSavedCodePos>0 && (pressed_key & 0b0001)) // нажата kLeft { eSavedCodePos--; } if (eSavedCodePos<29 && (pressed_key & 0b0010)) // нажата kRight { eSavedCodePos++; } if (pressed_key > 0) { ReadCode((eSavedCodePos*12)+400); memcpy(SubMenuText, pxchar, 16); if (!(pressed_key & 0b0100)) ShowMenu(MenuItem, 2); } // отправка выбранного в блоке Saved кода if (SubMenuItem==1 && (pressed_key & 0b0100)) // нажата kGet { digitalWrite(TransmitterEN, HIGH); mySwitch.enableTransmit(SendPin); mySwitch.setProtocol(Tprot); mySwitch.setRepeatTransmit(5); mySwitch.send(Tdata, Tclng); mySwitch.disableTransmit(); digitalWrite(TransmitterEN, LOW); ShowMenu(MenuItem, 4); } } // --------- подменю Grabbing radio ---------- // слушаем радио и заносим принятое в блок Saved if (MenuItem==3 && SubMenuItem==1 && xFlag==false) { digitalWrite(TransmitterEN, LOW); mySwitch.enableReceive(RcvInt); xFlag=true; // берем управление обработки кнопок на себя memcpy(SubMenuText, "wait...", 16); ShowMenu(MenuItem, 255); } if (MenuItem==3 && SubMenuItem==1 && xFlag==true) { if (mySwitch.available()) { // зажигаем подсветку - если надо if(BackLight > 1){ StartMS = millis(); lcd.backlight(); } // пикаем в 50 раз дольше чем на нажатие кнопки Beeper(50); // бжикаем в 50 раз дольше чем на нажатие кнопки Vibration(50); Tprot=mySwitch.getReceivedProtocol(); Tclng=mySwitch.getReceivedBitlength(); Tdata=mySwitch.getReceivedValue(); // кому надо берут mySwitch.getReceivedDelay() // кому надо берут mySwitch.getReceivedRawdata() mySwitch.resetAvailable(); memcpy(SubMenuText, pxchar, 16); if (eSavedCodePos<29) eSavedCodePos++; else eSavedCodePos=0; ShowMenu(MenuItem, 2); SaveCode((eSavedCodePos*12)+400); EEPROM.write(20, eSavedCodePos); } if (pressed_key & 0b1000) // нажата kBack { xFlag=false; SubMenuItem=0; } } // ---------- подменю Bruting code ----------- // перебор кодов шлагбаума статики CAME/NICE 12 (протоколы 7 и 8) if (MenuItem==4 && SubMenuItem==1 && xFlag==false) { xFlag=true; // берем управление обработки кнопок на себя pressed_key = 128; Tprot=0; memcpy(SubMenuText, "sel_pr press <->", 16); } if (xFlag==true && MenuItem==4) { if (pressed_key & 0b1000) // нажата kBack { xFlag=false; pressed_key=0; SubMenuItem=0; } if (SubMenuItem>1 && (pressed_key & 0b0001)) // нажата kLeft { SubMenuItem--; } if (SubMenuItem<2 && (pressed_key & 0b0010)) // нажата kRight { SubMenuItem++; } // обработка пункта подменю граббера if (pressed_key & 0b0100) // нажата kGet { Tprot = 6 + SubMenuItem; digitalWrite(TransmitterEN, HIGH); mySwitch.enableTransmit(SendPin); mySwitch.setProtocol(Tprot); Tclng=12; mySwitch.setRepeatTransmit(4); do { mySwitch.send(Tdata, Tclng); memcpy(SubMenuText, pxchar, 16); ShowMenu(MenuItem, 5); delay(50); if (digitalRead(kBack)==false) { delay(300); xFlag=false; BrutDir=false; BrutLevel=false; Tdata=1; break; } if (digitalRead(kLeft)==false) { delay(200); BrutDir=true; BrutLevel=true; SubMenuItem=6; Tdata--; } if (digitalRead(kRight)==false) { delay(200); BrutDir=false; BrutLevel=true; SubMenuItem=6; Tdata++; } if (digitalRead(kGet)==false) { delay(300); BrutLevel=false; SubMenuItem=6; } if(BrutLevel) break; if(BrutDir) { if(Tdata>1) Tdata--; } else { if(Tdata<4096) Tdata++; } } while(Tdata>1 && Tdata<4096); mySwitch.disableTransmit(); digitalWrite(TransmitterEN, LOW); } if (pressed_key > 0) { // отображение пунктов подменю (листать влево-вправо) switch(SubMenuItem) { case 1 : memcpy(SubMenuText, "pr 7/12 press OK", 16); break; case 2 : memcpy(SubMenuText, "pr 8/12 press OK", 16); break; } ShowMenu(MenuItem, 0); } } /* if (MenuItem==4 && SubMenuItem==1 && xFlag==false) { Tprot=0; memcpy(SubMenuText, "sel_pro press ->", 16); } if (MenuItem==4 && (SubMenuItem==2 || SubMenuItem==5)) { SubMenuItem=3; xFlag=true; } if (MenuItem==4 && SubMenuItem==3) { Tprot=7; // в библиотеке RCSwitch выбираем протокол 7 для Came memcpy(SubMenuText, "pr 7/12 press OK", 16); } if (MenuItem==4 && SubMenuItem==4) { Tprot=8; // в библиотеке RCSwitch выбираем протокол 8 для Nice memcpy(SubMenuText, "pr 8/12 press OK", 16); } if ((MenuItem==4 && SubMenuItem==1 && Tprot>0 && xFlag==true) || (MenuItem==4 && SubMenuItem>4)) { digitalWrite(TransmitterEN, HIGH); mySwitch.enableTransmit(SendPin); mySwitch.setProtocol(Tprot); Tclng=12; mySwitch.setRepeatTransmit(4); do { Tdata=BrutStep; mySwitch.send(Tdata, Tclng); memcpy(SubMenuText, pxchar, 16); ShowMenu(MenuItem, 5); delay(50); if (digitalRead(kBack)==false) { delay(300); xFlag=false; BrutDir=false; BrutLevel=false; BrutStep=1; break; } if (digitalRead(kLeft)==false) { delay(200); BrutDir=true; BrutLevel=true; SubMenuItem=6; BrutStep--; } if (digitalRead(kRight)==false) { delay(200); BrutDir=false; BrutLevel=true; SubMenuItem=6; BrutStep++; } if (digitalRead(kGet)==false) { delay(300); BrutLevel=false; SubMenuItem=6; } if(BrutLevel) break; if(BrutDir) { if(BrutStep>1) BrutStep--; } else { if(BrutStep<4096) BrutStep++; } } while(BrutStep>1 && BrutStep<4096); mySwitch.disableTransmit(); digitalWrite(TransmitterEN, LOW); } */ // ----------- подменю Noice Maker ----------- // просто глушилка всего и вся в доступном радиусе if (MenuItem==5 && SubMenuItem==1) { digitalWrite(TransmitterEN, HIGH); mySwitch.enableTransmit(SendPin); memcpy(SubMenuText, "working...", 16); ShowMenu(MenuItem, 0); do { digitalWrite(10, HIGH); delayMicroseconds(350); digitalWrite(10, LOW); delayMicroseconds(350); if (digitalRead(kBack)==false) break; }while(1); } // -------- подменю Copy saved->main --------- // перенос кодов из блока памяти Saved в блок памяти Main if (MenuItem==6 && SubMenuItem==1 && xFlag==false) { xFlag=true; // берем управление обработки кнопок на себя pressed_key = 128; } if (xFlag==true && MenuItem==6) { if (SubMenuItem==1 && (pressed_key & 0b1000)) // нажата kBack { xFlag=false; pressed_key=0; SubMenuItem=0; } if (eSavedCodePos>0 && (pressed_key & 0b0001)) // нажата kLeft { eSavedCodePos--; } if (eSavedCodePos<29 && (pressed_key & 0b0010)) // нажата kRight { eSavedCodePos++; } if (pressed_key > 0) { ReadCode((eSavedCodePos*12)+400); memcpy(SubMenuText, pxchar, 16); if (!(pressed_key & 0b0100)) { ShowMenu(MenuItem, 2); delay(1500); ShowMenu(MenuItem, 6); } } // копирование выбранного в блоке Saved кода в Main if (SubMenuItem==1 && (pressed_key & 0b0100)) // нажата kGet { eMainCodePos=EEPROM.read(10); if (eMainCodePos<29) eMainCodePos++; else eMainCodePos=0; ReadCode((eSavedCodePos*12)+400); SaveCode((eMainCodePos*12)+100); EEPROM.write(10, eMainCodePos); ShowMenu(MenuItem, 6); lcd.setCursor(15,1); lcd.print("*"); } } // --------- подменю Send user code ---------- // отправка кода нарисованного пользователем вручную ??? if (MenuItem==7) { } // --------- подменю Settings --------- // настройки, сохраняемые в памяти, а также режимы сортировки принятого // 1. подсветка 0/длительность (включена_постоянно/длительность_в_сек) // 2. пищалка, 0-255 - длительность в мс // 3. вибратор, 0-255 - длительность в мс // 4. ...? if (MenuItem==8 && SubMenuItem==1 && xFlag==false) { xFlag=true; // берем управление обработки кнопок на себя pressed_key = 128; } if (xFlag==true && MenuItem==8) { if (pressed_key & 0b1000) // нажата kBack { xFlag=false; pressed_key=0; SubMenuItem=0; } if (SubMenuItem>1 && (pressed_key & 0b0001)) // нажата kLeft { SubMenuItem--; } if (SubMenuItem<3 && (pressed_key & 0b0010)) // нажата kRight { SubMenuItem++; } // изменение пунктов подменю нажатием kGet по кругу if (pressed_key & 0b0100) // нажата kGet { if (SettingParm<33) SettingParm++; else SettingParm=0; EEPROM.write(46+(4*SubMenuItem), SettingParm); } if (pressed_key > 0) { EEPROM.get(46+(4*SubMenuItem), SettingParm); // отображение пунктов подменю (листать влево-вправо) switch(SubMenuItem) { case 1 : memcpy(SubMenuText, "BackLight ", 16); BackLight = SettingParm; break; case 2 : memcpy(SubMenuText, "Buzzer long ", 16); BeeperEnable = SettingParm; break; case 3 : memcpy(SubMenuText, "Vibro long ", 16); VibroEnable = SettingParm; break; } ShowMenu(MenuItem, 0); lcd.setCursor(12, 1); lcd.print(SettingParm); } } // ---------- подменю Clear memory ----------- // очистка блоков памяти Main и Saved с занесением в Main некоторых значений if (MenuItem==9 && SubMenuItem==1) { memcpy(SubMenuText, "clear press ->", 16); } if (MenuItem==9 && SubMenuItem==2) { memcpy(SubMenuText, "wait...", 16); ShowMenu(MenuItem, 255); for (int i=0; i