Doorhan, keeloq, arduino...
Здесь я хочу поделиться интересным опытом. Кому-то может показаться интересным.
Хочу рассказать как я "подружил" Arduino c пультом от ворот Doorhan и им научился включать лампочку.
Здесь я хочу поделиться интересным опытом. Кому-то может показаться интересным.
Хочу рассказать как я "подружил" Arduino c пультом от ворот Doorhan и им научился включать лампочку.
Дело в том что в нашем гараже есть общие секционные ворота Doorhan, а дальше ты проходишь в коридор,
нужно включить свет, открыть уже свою дверь, выехать, и при выезде не забыть свет в общем коридоре выключить.
То же самое и при вьезде - заехать, включить свет, выходя не забыть выключить.
Захотелось удобств - включать и выключать свет дистанционно, тем более дистанционный пульт от общих дверей уже есть.
Возникла такая идею - собрать устройство, с помощью которого можно было бы включать лампочку дистанционно.
Она бы включалась на короткое время, достаточное для открытия/закрытия ворот и выхода из общего коридора - затем сама бы выключалась.
В процессе изучения про пульты doorhan было выясненно что они работают на 433 Mhz (такие приемники и передатчики у меня оказались в наличии)
и используют в своей работе алгоритмы Keeloq. Оговорюсь сразу - свет включаю и выключаю я со своего индивидуального счетчика, лампочка висит напротив моих ворот,
поэтому решено было привязаться именно к своему пульту. Алгоритмы keeloq никто не взломал, но мне вообще-то это и не нужно - мне нужно было только проверить что это ключ мой
На просторах интернета была найдена одна программа, которая помогла мне решить эту задачу, на ее основе я затем и написал свою
Download file keeloq_came_starline.ino
Download file keeloq_came_starline.ino
#define RX 2 volatile byte level=255; volatile unsigned long last, len; byte p_level; unsigned long p_len, p_len_prev; struct { byte state; unsigned long TE; byte pre_count, data[8], dat_bit; } keeloq; struct { byte state; byte pre_count, data[8], dat_bit; } starline; struct { uint8_t state; uint8_t data[3], dat_bit; } came; void setbit(byte *data, byte n) { data[n/8]|=1<<(n%8); } #define KL_MIN_PRE_COUNT 4 #define KL_MAX_TE 500 #define KL_MIN_TE 300 #define KL_MAX_BITS 64 void process_keeloq() { switch(keeloq.state) { case 0: if(p_level) break; keeloq.state=1; keeloq.pre_count=0; break; case 1: //pre+hdr if(p_len>=KL_MIN_TE && p_len<=KL_MAX_TE) keeloq.pre_count++; else if(!p_level && p_len>=KL_MIN_TE*10 && p_len<=KL_MAX_TE*10 && keeloq.pre_count>=KL_MIN_PRE_COUNT) { keeloq.TE=p_len/10; keeloq.state=2; keeloq.dat_bit=0; keeloq.data[0]=0x00; keeloq.data[1]=0x00; keeloq.data[2]=0x00; keeloq.data[3]=0x00; keeloq.data[4]=0x00; keeloq.data[5]=0x00; keeloq.data[6]=0x00; keeloq.data[7]=0x00; } else { keeloq.state=0; break; } break; case 2: //dat if(!p_level) break; if(p_len<keeloq.TE/2 || p_len>keeloq.TE*3) { keeloq.state=0; break; } if(p_len<=keeloq.TE+keeloq.TE/2) setbit(keeloq.data, keeloq.dat_bit); if(++keeloq.dat_bit==KL_MAX_BITS) keeloq.state=100; break; } } #define SL_MIN_PRE_COUNT 4 #define SL_MAX_PRE 1150 #define SL_MIN_PRE 850 #define SL_MAX_ZERO 350 #define SL_MIN_ZERO 100 #define SL_MAX_ONE 700 #define SL_MIN_ONE 400 #define SL_MIN_BITS 16 #define SL_MAX_BITS 64 void process_starline() { byte b; switch(starline.state) { case 0: if(p_level) break; starline.state=1; starline.pre_count=0; break; case 1: //pre if(p_len>=SL_MIN_PRE && p_len<=SL_MAX_PRE) starline.pre_count++; else if(p_len<SL_MIN_PRE && starline.pre_count>=SL_MIN_PRE_COUNT) { starline.state=2; starline.dat_bit=0; starline.data[0]=0x00; starline.data[1]=0x00; starline.data[2]=0x00; starline.data[3]=0x00; starline.data[4]=0x00; starline.data[5]=0x00; starline.data[6]=0x00; starline.data[7]=0x00; } else { starline.state=0; break; } break; case 2: //dat if(p_level) break; if(p_len_prev>=SL_MIN_ONE && p_len_prev<=SL_MAX_ONE && p_len>=SL_MIN_ONE && p_len<=SL_MAX_ONE) b=1; else if(p_len_prev>=SL_MIN_ZERO && p_len_prev<=SL_MAX_ZERO && p_len>=SL_MIN_ZERO && p_len<=SL_MAX_ZERO) b=0; else { if(starline.dat_bit>=SL_MIN_BITS) starline.state=100; else starline.state=0; break; } if(b) setbit(starline.data, starline.dat_bit); if(++starline.dat_bit==SL_MAX_BITS) starline.state=100; break; } } #define TIMER_DIV #define CM_MAX_TE 450 #define CM_MIN_TE 250 #define CM_BITS12 12 #define CM_BITS24 24 void process_came() { unsigned char b; switch(came.state) { case 0: if(p_level) break; came.state=1; break; case 1: //start if(!p_level) break; if(p_len>CM_MIN_TE && p_len<=CM_MAX_TE) { came.dat_bit=0; came.data[0]=0x00; came.data[1]=0x00; came.data[2]=0x00; came.state=2; } else came.state=0; break; case 2: //dat if(p_level) { if(came.dat_bit==CM_BITS24) { came.state=0; break; } if(p_len_prev<=CM_MAX_TE && p_len_prev>=CM_MIN_TE && p_len<=CM_MAX_TE*2 && p_len>=CM_MIN_TE*2) b=0; else if(p_len_prev<=CM_MAX_TE*2 && p_len_prev>=CM_MIN_TE*2 && p_len<=CM_MAX_TE && p_len>=CM_MIN_TE) b=1; else { came.state=0; break; } if(b) setbit(came.data, came.dat_bit); came.dat_bit++; break; } else { if((p_len>5000) && (came.dat_bit==CM_BITS12 || came.dat_bit==CM_BITS24)) came.state=100; } break; } } void print_time() { unsigned long time = millis(); char tbs[32]; int dys = time / 86400000; int hr = (time - dys*86400000) / 3600000; int mn = (time - dys*86400000 - hr * 3600000) / 60000; int sc = (time - dys*86400000 - hr * 3600000 - mn * 60000) / 1000; sprintf(tbs, "[ %02dd %02dh %02dm %02ds]: ", dys, hr, mn, sc); /* Serial.print("["); Serial.print(hr); Serial.print("h"); Serial.print(mn); Serial.print("m"); Serial.print(sc); Serial.print("s"); Serial.print("]: "); */ Serial.print(tbs); } void dump_hex(byte *buf, byte bits) { byte a; for(a=0; a<(bits+7)/8; a++) { if(buf[a]<=0x0f) Serial.print('0'); Serial.print(buf[a], HEX); Serial.print(" "); } Serial.println(""); } void rx_int() { if(level!=255) return; len=micros()-last; last=micros(); if(digitalRead(RX)==HIGH) level=0; else level=1; } void setup() { attachInterrupt(0, rx_int, CHANGE); Serial.begin(115200); while(!Serial); Serial.println("STARLINE, CAME and KEELOQ receiver"); Serial.println(""); interrupts(); } byte a; void loop() { if(level!=255) { noInterrupts(); p_level=level; p_len=len; len=0; level=255; interrupts(); process_keeloq(); process_starline(); process_came(); p_len_prev=p_len; } if(keeloq.state==100) { Serial.print("KEELOQ: "); dump_hex(keeloq.data, 64); keeloq.state=0; } if(came.state==100) { print_time(); Serial.print("CAME: "); dump_hex(came.data, 24); came.state=0; } if(starline.state==100) { Serial.print("STARLINE["); Serial.print(starline.dat_bit); Serial.print("]: "); dump_hex(starline.data, starline.dat_bit); starline.state=0; } }Она логгирует перехваченные коды от разных кодировок, среди которых и keeloq. Нажатия моего пульта она перехватила
15:09:27.448 -> STARLINE, CAME and KEELOQ receiver 15:09:27.482 -> 15:13:59.930 -> KEELOQ: 7A 46 F9 4B 7C CA 28 88 15:14:01.486 -> KEELOQ: 7A 46 F9 4B 7C CA 28 88 15:14:01.792 -> KEELOQ: 01 4A 56 38 7C CA 28 28 15:14:02.842 -> KEELOQ: 0D 59 3D 8A 7C CA 28 28 15:14:02.944 -> KEELOQ: 0D 59 3D 8A 7C CA 28 28 15:14:03.046 -> KEELOQ: 55 6E D5 56 7C CA 28 28 15:14:03.250 -> KEELOQ: 39 B8 DB B5 7C CA 28 28 15:14:03.352 -> KEELOQ: 39 B8 DB B5 7C CA 28 88 15:14:03.896 -> KEELOQ: CB A4 2E BB 7C CA 28 28 15:14:04.780 -> KEELOQ: 9F 06 78 48 7C CA 28 28 15:14:06.203 -> KEELOQ: C3 30 B9 DF 7C CA 28 98 15:14:06.880 -> KEELOQ: 6D F1 1F E2 7C CA 28 B8 15:14:07.931 -> KEELOQ: 24 A8 CD 62 7C CA 28 68 15:14:09.726 -> KEELOQ: AB 50 3E 32 7C CA 28 88
Исходя из найденной информации в инете и нажимая на разные кнопки на пульте - стало понятно, что первые 4 байта здесь
это некая переменная алгоритмическая часть, потом идут 3 байта (и на самом деле младшие 4 бита последнего байта) которые можно
взять как серийный номер ключа (он всегда оставался одинаковым), а последний байт, точнее только 4 бита из него - это нажатые кнопки на пульте,
каждый бит - по кнопке, причем 0x80 это 2-я кнопка, 0x40 - 4-я кнопка, 0x20 - 1-я, 0x10 - 3-я (а самое смешное потом выяснилось,
что на китайский клонах дурхановских пультов - все эти кнопки идут совсем в другом порядке)
В общем взял я все свои три пульта (два из которых были китайскими), прописал их жестко в программе и сделал так чтобы при нажатии
2-й кнопки свет включался на одну минуту, а если нажать 4-ю кнопку - свет сразу выключался.
Собрал и запаял простенькую схему с тиристором и ардиуно мини, засунул ее в ящик и бед не знал. Было очень удобно.
Но один из китайских пультов приказал долго жить, да и на моем оригинальном стали залипать кнопочки. Заказал еще пару ключей из Поднебесной,
и встал вопрос - как залить их в свою поделку. Подумалось - что не очень то это удобно каждый раз эти ключи так жестко прописывать.
Нужно же сделать так чтобы их можно было и дистанционно записать в устройство. Решено было - раз все равно устройство свое снимать и перепрошивать
- сделать доработку, сделать его обучаемым. Ардуино, как известно, имеет EEPROM, куда можно как раз записать ключи и дописывать туда если понадобится.
А еще оказалось что теперь есть пульты у которых есть только две кнопки, причем 1-я и 2-я - значит надо предусмотреть что включать свет может
не только жестко 2-я кнопка, а назначенная, обученная (т.е. может и 1-я). И еще хорошо бы знать что процесс обучения происходит -
устройство мое спрятано в ящике, видимых индикаторов нет (хотя и есть управление встроенным светодиодиком) - значит должен быть звуковой.
Задумано - сделано - в программу был добавлено обучение. Чтобы обучить новому пульту - нужно на уже известном устройству ключе нажать и удерживать первую кнопку
если таких нажатий будет больше 10 в течении 5 секунд - устройство издает определенный звуковой сигнал и переходит на 10 секунд в режим "программирования".
В эти 10 секунд надо успеть нажать на новом пульте ту кнопку которая будет включать свет. После прописывания ключа в EEPROM, опять таки будет издан звуковой сигнал,
и этим ключом тоже можно будет включат свет. А вот выключение я оставил жестко на 4 кнопке. В общем написал программу, впаял пищалку
(обычную с материнскими платами которая идет), включил, работает. Два новых ключа прописал через режим обучения - полет нормальный.
Делюсь кодом - вдруг кому-то окажется полезным.
Download file keeloq_my_v2_with_eeprom.ino
#include <avr/eeprom.h> //#include <EEPROM.h> #include "pitches.h" //================================= // распиновка // модуль приемника ARDUINO // WL101-341/331 mini // // VIN 5V // DO любой D2 // GND GND // // тиристор // VCC D3 // GND GND // // пищалка // красный D8 // черный GND #define RX 2 #define RESET_EEPROM false // set to true when first upload byte NUMKEYS = 0; byte keys[100][5]; //============= Прошитые ключи ===================// //const byte NUMKEYS = 3; //byte keys[NUMKEYS][4] = {{0x7C,0xCA,0x28,0x08}, // {0x20,0x00,0x00,0x00}, // {0x25,0x00,0x00,0x00}}; //=================================================// volatile byte level=255; volatile unsigned long last, len; byte p_level, n, /*byte_key, */byte_btn; unsigned long p_len, p_len_prev; const byte tyrPin = 3; const byte ledPin = 13; unsigned long currentTime; boolean SwState = false; unsigned long listenProgTime, progModeTime; boolean listenProgBegin = false, progMode = false, isOurKey; byte listenProgCnt = 0; struct { byte state; unsigned long TE; byte pre_count, data[8], dat_bit; } keeloq; void setbit(byte *data, byte n) { data[n/8]|=1<<(n%8); } #define KL_MIN_PRE_COUNT 4 #define KL_MAX_TE 500 #define KL_MIN_TE 300 #define KL_MAX_BITS 64 void process_keeloq() { switch(keeloq.state) { case 0: if(p_level) break; keeloq.state=1; keeloq.pre_count=0; break; case 1: //pre+hdr if(p_len>=KL_MIN_TE && p_len<=KL_MAX_TE) keeloq.pre_count++; else if(!p_level && p_len>=KL_MIN_TE*10 && p_len<=KL_MAX_TE*10 && keeloq.pre_count>=KL_MIN_PRE_COUNT) { keeloq.TE=p_len/10; keeloq.state=2; keeloq.dat_bit=0; keeloq.data[0]=0x00; keeloq.data[1]=0x00; keeloq.data[2]=0x00; keeloq.data[3]=0x00; keeloq.data[4]=0x00; keeloq.data[5]=0x00; keeloq.data[6]=0x00; keeloq.data[7]=0x00; } else { keeloq.state=0; break; } break; case 2: //dat if(!p_level) break; if(p_len<keeloq.TE/2 || p_len>keeloq.TE*3) { keeloq.state=0; break; } if(p_len<=keeloq.TE+keeloq.TE/2) setbit(keeloq.data, keeloq.dat_bit); if(++keeloq.dat_bit==KL_MAX_BITS) keeloq.state=100; break; } } void dump_hex(byte *buf, byte bits) { byte a; Serial.print(" "); for(a=0; a<(bits+7)/8; a++) { if(buf[a]<=0x0f) Serial.print('0'); Serial.print(buf[a], HEX); Serial.print(" "); } Serial.println(""); } void rx_int() { if(level!=255) return; len=micros()-last; last=micros(); if(digitalRead(RX)==HIGH) level=0; else level=1; } void save_key(byte n, byte k1, byte k2, byte k3, byte keybtn) { eeprom_write_byte(4*n+1, k1); eeprom_write_byte(4*n+2, k2); eeprom_write_byte(4*n+3, k3); //keybtn = low 4 bits = permanent part of serial // high 4 bits = pressed buttons eeprom_write_byte(4*n+4, keybtn); keys[n][0] = k1; keys[n][1] = k2; keys[n][2] = k3; keys[n][3] = keybtn; } void setup() { Serial.begin(115200); while(!Serial); if( RESET_EEPROM ) // для первичной прошивки запишем ключи устанавлива RESET_EEPROM = true { NUMKEYS = 3; eeprom_write_byte(0, NUMKEYS); save_key(0, 0x7C,0xCA,0x28,0x88); save_key(1, 0x20,0x00,0x00,0x80); save_key(2, 0x25,0x00,0x00,0x80); } NUMKEYS = eeprom_read_byte(0); for(n = 0; n<NUMKEYS; n++) { keys[n][0] = eeprom_read_byte(4*n+1); keys[n][1] = eeprom_read_byte(4*n+2); keys[n][2] = eeprom_read_byte(4*n+3); keys[n][3] = eeprom_read_byte(4*n+4); Serial.print("read key #"); Serial.print(n); dump_hex(keys[n], 32); } currentTime = millis(); listenProgTime = millis(); attachInterrupt(0, rx_int, CHANGE); Serial.println("Start listen..."); Serial.println(""); interrupts(); } byte a; void loop() { if(level!=255) { noInterrupts(); p_level=level; p_len=len; len=0; level=255; interrupts(); process_keeloq(); p_len_prev=p_len; } if(SwState && (currentTime+60000 < millis())) { SwState = false; digitalWrite(ledPin, LOW); } if(progMode && (progModeTime+10000 < millis())) { progMode = false; } if(listenProgBegin && (listenProgTime+5000 < millis())) { listenProgBegin = false; listenProgCnt = 0; progMode = false; } if(keeloq.state==100) { Serial.print("KEELOQ: "); //play_ready(); dump_hex(keeloq.data, 64); keeloq.state=0; byte_btn = keeloq.data[7]&0xF0; if((byte_btn&0x80)==0x80) Serial.print(" #2"); if((byte_btn&0x40)==0x40) Serial.print(" #4"); if((byte_btn&0x20)==0x20) Serial.print(" #1"); if((byte_btn&0x10)==0x10) Serial.print(" #3"); Serial.println(""); isOurKey = false; for(n = 0; n<NUMKEYS; n++) { if( (keeloq.data[4] == keys[n][0])&& (keeloq.data[5] == keys[n][1])&& (keeloq.data[6] == keys[n][2])&& ( (keeloq.data[7]&0x0F) == (keys[n][3]&0x0F) ) ) { Serial.print("OUR KEY! pressed "); isOurKey = true; if((byte_btn&0x20)==0x20) { // наш ключ. нажата первая кнопка, в режим программирования входим только по первой кнопке - она есть вроде на всех пультах // Serial.println("numkey = #1, maybe prog mode begin"); if(listenProgBegin) { listenProgCnt++; // Serial.print("increment count = "); // Serial.println(listenProgCnt); } else { // Serial.println("begin listen count"); listenProgBegin = true; listenProgTime = millis(); listenProgCnt = 1; } if(listenProgCnt > 10) { play_ready(); progMode = true; progModeTime = millis(); } } byte saved_btn = keys[n][4]& 0xF0; // if((keeloq.data[7]&0x80)==0x80) // теперь не только вторая кнопка может быть if((keeloq.data[7]&saved_btn)==saved_btn) { // Serial.print(" #2 - light on"); // не обязательно теперь вторая кнопка digitalWrite(ledPin, HIGH); currentTime = millis(); SwState = true; } if((keeloq.data[7]&0x40)==0x40) { Serial.println(" #4 - light force off"); // на тех пультах где есть эта кнопка - будет возможность форсированно выключать SwState = false; digitalWrite(ledPin, LOW); } } } if(!isOurKey) { Serial.println("NOT OUR KEY!"); if(progMode) { Serial.print(" prog mode = must save key: "); if(NUMKEYS < 100) { save_key(NUMKEYS, keeloq.data[4],keeloq.data[5],keeloq.data[6],keeloq.data[7]); NUMKEYS++; eeprom_update_byte(0, NUMKEYS); play_win(); } else { Serial.print(" prog mode = but 100 keys limit reached :( "); play_StarWars(); } } } } }Download file pitches.h
// notes constant #define NOTE_B0 31 #define NOTE_C1 33 #define NOTE_CS1 35 #define NOTE_D1 37 #define NOTE_DS1 39 #define NOTE_E1 41 #define NOTE_F1 44 #define NOTE_FS1 46 #define NOTE_G1 49 #define NOTE_GS1 52 #define NOTE_A1 55 #define NOTE_AS1 58 #define NOTE_B1 62 #define NOTE_C2 65 #define NOTE_CS2 69 #define NOTE_D2 73 #define NOTE_DS2 78 #define NOTE_E2 82 #define NOTE_F2 87 #define NOTE_FS2 93 #define NOTE_G2 98 #define NOTE_GS2 104 #define NOTE_A2 110 #define NOTE_AS2 117 #define NOTE_B2 123 #define NOTE_C3 131 #define NOTE_CS3 139 #define NOTE_D3 147 #define NOTE_DS3 156 #define NOTE_E3 165 #define NOTE_F3 175 #define NOTE_FS3 185 #define NOTE_G3 196 #define NOTE_GS3 208 #define NOTE_A3 220 #define NOTE_AS3 233 #define NOTE_B3 247 #define NOTE_C4 262 #define NOTE_CS4 277 #define NOTE_D4 294 #define NOTE_DS4 311 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_FS4 370 #define NOTE_G4 392 #define NOTE_GS4 415 #define NOTE_A4 440 const int aS = 455; #define NOTE_AS4 466 #define NOTE_B4 494 #define NOTE_C5 523 #define NOTE_CS5 554 #define NOTE_D5 587 #define NOTE_DS5 622 #define NOTE_E5 659 #define NOTE_F5 698 #define NOTE_FS5 740 #define NOTE_G5 784 #define NOTE_GS5 831 #define NOTE_A5 880 #define NOTE_AS5 932 #define NOTE_B5 988 #define NOTE_C6 1047 #define NOTE_CS6 1109 #define NOTE_D6 1175 #define NOTE_DS6 1245 #define NOTE_E6 1319 #define NOTE_F6 1397 #define NOTE_FS6 1480 #define NOTE_G6 1568 #define NOTE_GS6 1661 #define NOTE_A6 1760 #define NOTE_AS6 1865 #define NOTE_B6 1976 #define NOTE_C7 2093 #define NOTE_CS7 2217 #define NOTE_D7 2349 #define NOTE_DS7 2489 #define NOTE_E7 2637 #define NOTE_F7 2794 #define NOTE_FS7 2960 #define NOTE_G7 3136 #define NOTE_GS7 3322 #define NOTE_A7 3520 #define NOTE_AS7 3729 #define NOTE_B7 3951 #define NOTE_C8 4186 #define NOTE_CS8 4435 #define NOTE_D8 4699 #define NOTE_DS8 4978 void beep(int ton, int time) { tone(8, ton, time); delay(time + 20); } void play_win() { beep(NOTE_C4, 250); beep(NOTE_G3, 125); beep(NOTE_G3, 125); beep(NOTE_A3, 250); beep(NOTE_G3, 250); delay(250); beep(NOTE_B3, 250); beep(NOTE_C4, 250); noTone(8); } void play_ready() { beep(NOTE_C5, 125); beep(NOTE_A4, 125); beep(NOTE_F4, 250); noTone(8); // Отключаем спикер } void play_StarWars() { beep(NOTE_A4, 500); beep(NOTE_A4, 500); beep(NOTE_A4, 500); beep(NOTE_F4, 350); beep(NOTE_C5, 150); beep(NOTE_A4, 500); beep(NOTE_F4, 350); beep(NOTE_C5, 150); beep(NOTE_A4, 650); delay(150); beep(NOTE_E5, 500); beep(NOTE_E5, 500); beep(NOTE_E5, 500); beep(NOTE_F5, 350); beep(NOTE_C5, 150); beep(NOTE_GS4, 500); beep(NOTE_F4, 350); beep(NOTE_C5, 150); beep(NOTE_A4, 650); delay(150); beep(NOTE_A5, 500); beep(NOTE_A4, 300); beep(NOTE_A4, 150); beep(NOTE_A5, 400); beep(NOTE_GS5, 200); beep(NOTE_G5, 200); beep(NOTE_FS5, 125); beep(NOTE_F5, 125); beep(NOTE_FS5, 250); delay(250); beep(aS, 250); beep(NOTE_DS5, 400); beep(NOTE_D5, 200); beep(NOTE_CS5, 200); beep(NOTE_C5, 125); beep(NOTE_AS4, 125); beep(NOTE_C5, 250); delay(250); beep(NOTE_F4, 125); beep(NOTE_GS4, 500); beep(NOTE_F4, 375); beep(NOTE_A4, 125); beep(NOTE_C5, 500); beep(NOTE_A4, 375); beep(NOTE_C5, 125); beep(NOTE_E5, 650); beep(NOTE_A5, 500); beep(NOTE_A4, 300); beep(NOTE_A4, 150); beep(NOTE_A5, 400); beep(NOTE_GS5, 200); beep(NOTE_G5, 200); beep(NOTE_FS5, 125); beep(NOTE_F5, 125); beep(NOTE_FS5, 250); delay(250); beep(aS, 250); beep(NOTE_DS5, 400); beep(NOTE_D5, 200); beep(NOTE_CS5, 200); beep(NOTE_C5, 125); beep(NOTE_AS4, 125); beep(NOTE_C5, 250); delay(250); beep(NOTE_F4, 250); beep(NOTE_GS4, 500); beep(NOTE_F4, 375); beep(NOTE_C5, 125); beep(NOTE_A4, 500); beep(NOTE_F4, 375); beep(NOTE_C5, 125); beep(NOTE_A4, 650); // Конец песни noTone(8); // Отключаем спикер }