Doorhan, keeloq, arduino...

Здесь я хочу поделиться интересным опытом. Кому-то может показаться интересным. Хочу рассказать как я "подружил" Arduino c пультом от ворот Doorhan и им научился включать лампочку.
Здесь я хочу поделиться интересным опытом. Кому-то может показаться интересным. Хочу рассказать как я "подружил" Arduino c пультом от ворот Doorhan и им научился включать лампочку.
Дело в том что в нашем гараже есть общие секционные ворота Doorhan, а дальше ты проходишь в коридор, нужно включить свет, открыть уже свою дверь, выехать, и при выезде не забыть свет в общем коридоре выключить. То же самое и при вьезде - заехать, включить свет, выходя не забыть выключить. Захотелось удобств - включать и выключать свет дистанционно, тем более дистанционный пульт от общих дверей уже есть. Возникла такая идею - собрать устройство, с помощью которого можно было бы включать лампочку дистанционно. Она бы включалась на короткое время, достаточное для открытия/закрытия ворот и выхода из общего коридора - затем сама бы выключалась.
В процессе изучения про пульты doorhan было выясненно что они работают на 433 Mhz (такие приемники и передатчики у меня оказались в наличии) и используют в своей работе алгоритмы Keeloq. Оговорюсь сразу - свет включаю и выключаю я со своего индивидуального счетчика, лампочка висит напротив моих ворот, поэтому решено было привязаться именно к своему пульту. Алгоритмы keeloq никто не взломал, но мне вообще-то это и не нужно - мне нужно было только проверить что это ключ мой
На просторах интернета была найдена одна программа, которая помогла мне решить эту задачу, на ее основе я затем и написал свою
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);  // Отключаем спикер
}