Дубликатор домофоных ключей

Здравствуйте! Решил потренироваться собрать дубликатор. GitHub - SibMan54/Arduino-RFID-iButton-Duplicator: RFID (EM-Marie, Mifare) and wired (Dallas, Cyfral, Metacom) key dublicator with arduino. Supports EM4305, T5577, Mifare 1k, RW-1990.1 RW-1990.2 TM-01 TM2004 protocols.
Есть несколько вопросов, задам походу.


Вот схема, не знаю как тут скетч выложить.

Если разуть поднять глаза, то можно увидеть как вставлять код.

Вот этот?

/*
  Доработка проекта "Копировальщик ключей для домофона RFID с OLED дисплеем и хранением 8 ключей в памяти EEPROM"
  Автор: МЕХАТРОН DIY, AlexMalov, 2019 https://github.com/AlexMalov/EasyKeyDublicatorRFID_OLED/
  Аппаратная часть построена на Arduino Nano или Arduino Pro Mini
  ---------------------Мои доработки-----------------------------
  1. Переназначил вывод генерации 125 кГц с вывода 11 на вывод 3 чтобы освободить шину SPI
  2. Добавлена подержка работы с модулем RC522, модуль подключается по SPI шине к 11-12-13 пинам Arduino
  3. Написаны функции чтения и записи UID в RFID метки типа Mifare 1k на заготовки типа Mifare Zero, этого как правило достаточно для нормальной работы брелка.
  4. Добавил возможность принудительной очистки EEPROM, для этого перед вкл. прибора нужно зажать кнопку энкодера и неотпускать ее пока не засветится красный светодиод
  5. Также в место режима blueMode я сделал режим очистки и восстановления Mifare метки, когда метка неопределяется и нечитается пробуем этот режим. Если кому то нужен режим blueMode, то просто раскоментируйте #define BLUE_MODE в начале кода.
*/


// Подключаем библиотеки
#include <OneWire.h>
#include <OneWireSlave.h>
#include "pitches.h"
#include <EEPROM.h>
#include <OLED_I2C.h> 
OLED myOLED(SDA, SCL); //создаем экземпляр класса OLED с именем myOLED
extern uint8_t SmallFont[];
extern uint8_t BigNumbers[];
#include "GyverEncoder.h"
#include "TimerOne.h"
#include <SPI.h>
#include <MFRC522.h>
/* 
 * Typical pin layout used:
 * -----------------------------------------------------------------------------------------
 *             MFRC522      Arduino       Arduino   Arduino    Arduino          Arduino
 *             Reader/PCD   Uno/101       Mega      Nano v3    Leonardo/Micro   Pro Micro
 * Signal      Pin          Pin           Pin       Pin        Pin              Pin
 * -----------------------------------------------------------------------------------------
 * SPI SS      SDA(SS)      10            53        D10        10               10
 * SPI MOSI    MOSI         11 / ICSP-4   51        D11        ICSP-4           16
 * SPI MISO    MISO         12 / ICSP-1   50        D12        ICSP-1           14
 * SPI SCK     SCK          13 / ICSP-3   52        D13        ICSP-3           15
 */

// Настройки
// #define DEBUG_ENABLED     // Раскоментируйте для вкл отладки
// #define BLUE_MODE         // Раскоментируйте если вам нужен BlueMode
#define rfidUsePWD 0        // ключ использует пароль для изменения
#define rfidPWD 123456      // пароль для ключа
#define rfidBitRate 2       // Скорость обмена с rfid в kbps

// Контакты
#define iButtonPin A3      // Линия data ibutton
#define iBtnEmulPin A1     // Линия эмулятора ibutton
#define R_Led 2            // RGB Led
#define G_Led 4
#define B_Led 5
#define ACpin 6            // Вход Ain0 аналогового компаратора 0.1В для EM-Marie 
#define speakerPin A2       // Спикер, он же buzzer, он же beeper
#define FreqGen 3         // генератор 125 кГц
#define CLK 8              // s1 энкодера
#define DT 9               // s2 энкодера
#define BtnPin 0          // Кнопка переключения режима чтение/запись
#define SS_PIN 10
#define RST_PIN A0

MFRC522 rfid(SS_PIN, RST_PIN);            // Создаем объект MFRC522
MFRC522::MIFARE_Key key;
Encoder enc1(CLK, DT, BtnPin);
OneWire ibutton (iButtonPin);
OneWireSlave iBtnEmul(iBtnEmulPin);       //Эмулятор iButton для BlueMode
byte maxKeyCount;                         // максимальное кол-во ключей, которое влазит в EEPROM, но не > 20
byte EEPROM_key_count;                    // количество ключей 0..maxKeyCount, хранящихся в EEPROM
byte EEPROM_key_index = 0;                // 1..EEPROM_key_count номер последнего записанного в EEPROM ключа  
byte addr[8];                             // временный буфер
byte keyID[8];                            // ID ключа для записи
byte rfidData[5];                         // значащие данные frid em-marine
byte halfT;                               // полупериод для метаком
bool statOk = false;                      // статус успешной операции, чтобы запись или очистка не проиходила по кругу
enum emRWType {rwUnknown, TM01, RW1990_1, RW1990_2, TM2004, T5557, EM4305};               // тип болванки
enum emkeyType {keyUnknown, keyDallas, keyTM2004, keyCyfral, keyMetacom, keyEM_Marine, keyMifare};    // тип оригинального ключа  
emkeyType keyType;
enum emMode {md_empty, md_read, md_write, md_threeMode};               // режим раоты копировальщика
emMode copierMode = md_empty;

void OLED_printKey(byte buf[8], byte msgType = 0){
  String st;
  switch (msgType){
    case 0: st = "The key " + String(EEPROM_key_index) + " of " + String(EEPROM_key_count) + " in ROM"; break;      
    case 1: st = "Hold the Btn to save";  break; 
    case 3: st = "The key " + String(indxKeyInROM(buf)) + " exists in ROM";  break;   
  }
  myOLED.clrScr();
  myOLED.print(st, 0, 0);  
  st = "";
  for (byte i = 0; i < 8; i++) st += String(buf[i], HEX) +":";
  myOLED.print(st, 0, 12);
  st = "Type ";
  switch (keyType){
    case keyDallas: st += "Dallas wire"; break;      
    case keyCyfral: st += "Cyfral wire";  break;  
    case keyMetacom: st += "Metakom wire"; break;             
    case keyEM_Marine: st += "EM_Marine rfid"; break;
    case keyMifare: st += "Mifare rfid"; break;
    case keyUnknown: st += "Unknown"; break;
  }
  myOLED.print(st, 0, 24);
  myOLED.update();
}

void OLED_printError(String st, bool err = true){
  myOLED.clrScr();
  if (err) myOLED.print(F("Error!"), 0, 0);
    else myOLED.print(F("OK"), 0, 0);
  myOLED.print(st, 0, 12);  
  myOLED.update();
}

void setup() {
  // Конфигурация пинов
  pinMode(BtnPin, INPUT_PULLUP);                            // включаем чтение и подягиваем пин кнопки режима к +5В
  pinMode(speakerPin, OUTPUT);
  pinMode(ACpin, INPUT);                                    // Вход аналогового компаратора 3В для Cyfral 
  pinMode(R_Led, OUTPUT); pinMode(G_Led, OUTPUT); pinMode(B_Led, OUTPUT);  //RGB-led
  clearLed();
  pinMode(FreqGen, OUTPUT);                               
  #ifdef DEBUG_ENABLED
  Serial.begin(115200);
  #endif
  // Конфигурация OLED дисплея
  myOLED.begin(SSD1306_128X32); //инициализируем дисплей
  myOLED.clrScr();                                          //Очищаем буфер дисплея.
  myOLED.setFont(SmallFont);                                //Перед выводом текста необходимо выбрать шрифт
  myOLED.print(F("Hello, read a key..."), LEFT, 0);
  char st[23] = { 98, 121, 32, 77, 69, 84, 80, 79, 72, 32, 38, 32, 83, 105, 98, 77, 97, 110, 32, 68, 73, 89, 0 };
  myOLED.print(st, LEFT, 24);
  myOLED.update();

  // Конфигурация RC522
  SPI.begin(); // Инициализируем SPI интерфейс
  rfid.PCD_Init(); // Инициализируем MFRC522

  if (!digitalRead(BtnPin)){       // Если зажать кнопку при включении то очистится EEPROM
    digitalWrite(R_Led, HIGH);
    for (int i = 0 ; i < EEPROM.length() ; i++) {
      EEPROM.write(i, 0);
    }
    Serial.print(F("EEPROM cleared"));
    delay(100);
    clearLed();
  }

  Sd_StartOK();       // Издаем звук удачного запуска

  EEPROM_key_count = EEPROM[0];
  maxKeyCount = EEPROM.length() / 8 - 1; if (maxKeyCount > 20) maxKeyCount = 20;
  if (EEPROM_key_count > maxKeyCount) EEPROM_key_count = 0;
  if (EEPROM_key_count != 0 ) {
    EEPROM_key_index = EEPROM[1];
    Serial.print(F("Read key code from EEPROM: "));
    EEPROM_get_key(EEPROM_key_index, keyID);
    for (byte i = 0; i < 8; i++) {
      Serial.print(keyID[i], HEX); Serial.print(F(":"));  
    }
    Serial.println();
    delay(3000);
    OLED_printKey(keyID);
    copierMode = md_read;
    digitalWrite(G_Led, HIGH);
  } else {
    myOLED.print(F("ROM has no keys yet."), 0, 12);
    myOLED.update();  
  }
  enc1.setTickMode(AUTO);
  enc1.setType(TYPE2);
  // enc1.setDirection(REVERSE);         // NORM / REVERSE
  Timer1.initialize(1000);            // установка таймера на каждые 1000 микросекунд (= 1 мс)
  Timer1.attachInterrupt(timerIsr);   // запуск таймера
}

void timerIsr() {   // прерывание таймера для энкодера
  enc1.tick();     
}

void clearLed(){
  digitalWrite(R_Led, LOW);
  digitalWrite(G_Led, LOW);
  digitalWrite(B_Led, LOW);  
}

byte indxKeyInROM(byte buf[]){ //возвращает индекс или ноль если нет в ROM
  byte buf1[8]; bool eq = true;
  for (byte j = 1; j<=EEPROM_key_count; j++){  // ищем ключ в eeprom. 
    EEPROM.get(j*sizeof(buf1), buf1);
    for (byte i = 0; i < 8; i++) 
      if (buf1[i] != buf[i]) { eq = false; break;}
    if (eq) return j;
    eq = true;
  }
  return 0;
}

bool EPPROM_AddKey(byte buf[]){
  byte buf1[8]; byte indx;
  indx = indxKeyInROM(buf);                 // ищем ключ в eeprom. Если находим, то не делаем запись, а индекс переводим в него
  if ( indx != 0) { 
    EEPROM_key_index = indx;
    EEPROM.update(1, EEPROM_key_index);
    return false; 
  }
  if (EEPROM_key_count <= maxKeyCount) EEPROM_key_count++;
  if (EEPROM_key_count < maxKeyCount) EEPROM_key_index = EEPROM_key_count;
    else EEPROM_key_index++;
  if (EEPROM_key_index > EEPROM_key_count) EEPROM_key_index = 1;
  Serial.println(F("Adding to EEPROM"));
  for (byte i = 0; i < 8; i++) {
    buf1[i] = buf[i];
    Serial.print(buf[i], HEX); Serial.print(F(":"));  
  }
  Serial.println();
  EEPROM.put(EEPROM_key_index*sizeof(buf1), buf1);
  EEPROM.update(0, EEPROM_key_count);
  EEPROM.update(1, EEPROM_key_index);
  return true;
}

void EEPROM_get_key(byte EEPROM_key_index1, byte buf[8]){
  byte buf1[8];
  int address = EEPROM_key_index1*sizeof(buf1);
  if (address > EEPROM.length()) return;
  EEPROM.get(address, buf1);
  for (byte i = 0; i < 8; i++) buf[i] = buf1[i];
  keyType = getKeyType(buf1);
}

emkeyType getKeyType(byte* buf){                       // Определение типа ключа
  if ((buf[0] == 0xFF) && vertEvenCheck(buf)) return keyEM_Marine;
  if (buf[4] == 0 && buf[5] == 0 && buf[6] == 0 && buf[7] == 0) return keyMifare;
  if (buf[0] == 0x01) return keyDallas;
  if ((buf[0] >> 4) == 0b0001) return keyCyfral;
  if ((buf[0] >> 4) == 0b0010) return keyMetacom;
  return keyUnknown;
}

//*************** dallas **************
emRWType getRWtype(){    
   byte answer;
  // TM01 это неизвестный тип болванки, делается попытка записи TM-01 без финализации для dallas или c финализацией под cyfral или metacom
  // RW1990_1 - dallas-совместимые RW-1990, RW-1990.1, ТМ-08, ТМ-08v2 
  // RW1990_2 - dallas-совместимая RW-1990.2
  // TM2004 - dallas-совместимая TM2004 в доп. памятью 1кб
  // пробуем определить RW-1990.1
  ibutton.reset(); ibutton.write(0xD1); // проуем снять флаг записи для RW-1990.1
  ibutton.write_bit(1);                 // записываем значение флага записи = 1 - отключаем запись
  delay(10); pinMode(iButtonPin, INPUT);
  ibutton.reset(); ibutton.write(0xB5); // send 0xB5 - запрос на чтение флага записи
  answer = ibutton.read();
  //Serial.print(F("\n Answer RW-1990.1: ")); Serial.println(answer, HEX);
  if (answer == 0xFE){
    Serial.println(F(" Type: dallas RW-1990.1 "));
    return RW1990_1;            // это RW-1990.1
  }
  // пробуем определить RW-1990.2
  ibutton.reset(); ibutton.write(0x1D);  // пробуем установить флаг записи для RW-1990.2 
  ibutton.write_bit(1);                  // записываем значение флага записи = 1 - включаем запись
  delay(10); pinMode(iButtonPin, INPUT);
  ibutton.reset(); ibutton.write(0x1E);  // send 0x1E - запрос на чтение флага записи
  answer = ibutton.read();
  if (answer == 0xFE){
    ibutton.reset(); ibutton.write(0x1D); // возвращаем оратно запрет записи для RW-1990.2
    ibutton.write_bit(0);                 // записываем значение флага записи = 0 - выключаем запись
    delay(10); pinMode(iButtonPin, INPUT);
    Serial.println(F(" Type: dallas RW-1990.2 "));
    return RW1990_2; // это RW-1990.2
  }
  // пробуем определить TM-2004
  ibutton.reset(); ibutton.write(0x33);                     // посылаем команду чтения ROM для перевода в расширенный 3-х байтовый режим
  for ( byte i=0; i<8; i++) ibutton.read();                 // читаем данные ключа
  ibutton.write(0xAA);                                      // пробуем прочитать регистр статуса для TM-2004    
  ibutton.write(0x00); ibutton.write(0x00);                 // передаем адрес для считывания
  answer = ibutton.read();                                  // читаем CRC комманды и адреса
  byte m1[3] = {0xAA, 0,0};                                 // вычисляем CRC комманды
  if (OneWire::crc8(m1, 3) == answer) {
    answer = ibutton.read();                                  // читаем регистр статуса
    //Serial.print(" status: "); Serial.println(answer, HEX);
    Serial.println(F(" Type: dallas TM2004"));
    ibutton.reset();
    return TM2004; // это Type: TM2004
  }
  ibutton.reset();
  Serial.println(F(" Type: dallas unknown, trying TM-01! "));
  return TM01;                              // это неизвестный тип DS1990, нужно перебирать алгоритмы записи (TM-01)
}

bool write2iBtnTM2004(){                // функция записи на TM2004
  byte answer; bool result = true;
  ibutton.reset();
  ibutton.write(0x3C);                                      // команда записи ROM для TM-2004    
  ibutton.write(0x00); ibutton.write(0x00);                 // передаем адрес с которого начинается запись
  for (byte i = 0; i<8; i++){
    digitalWrite(R_Led, !digitalRead(R_Led));
    ibutton.write(keyID[i]);
    answer = ibutton.read();
    //if (OneWire::crc8(m1, 3) != answer){result = false; break;}     // crc не верный
    delayMicroseconds(600); ibutton.write_bit(1); delay(50);         // испульс записи
    pinMode(iButtonPin, INPUT);
    Serial.print('*');
    Sd_WriteStep();
    if (keyID[i] != ibutton.read()) { result = false; break;}    //читаем записанный байт и сравниваем, с тем что должно записаться
  } 
  if (!result){
    ibutton.reset();
    Serial.println(F(" The key copy faild"));
    OLED_printError(F("The key copy faild"));
    Sd_ErrorBeep();
    digitalWrite(R_Led, HIGH);
    return false;    
  }
  ibutton.reset();
  Serial.println(F(" The key has copied successfully"));
  OLED_printError(F("The key has copied"), false);
  Sd_ReadOK();
  delay(2000);
  digitalWrite(R_Led, HIGH);
  return true;
}

bool write2iBtnRW1990_1_2_TM01(emRWType rwType){              // функция записи на RW1990.1, RW1990.2, TM-01C(F)
  byte rwCmd, bitCnt = 64, rwFlag = 1;
  switch (rwType){
    case TM01: rwCmd = 0xC1; if ((keyType == keyMetacom)||(keyType == keyCyfral)) bitCnt = 36; break;                   //TM-01C(F)
    case RW1990_1: rwCmd = 0xD1; rwFlag = 0; break;  // RW1990.1  флаг записи инвертирован
    case RW1990_2: rwCmd = 0x1D; break;              // RW1990.2
  }
  ibutton.reset(); ibutton.write(rwCmd);       // send 0xD1 - флаг записи
  ibutton.write_bit(rwFlag);                   // записываем значение флага записи = 1 - разрешить запись
  delay(5); pinMode(iButtonPin, INPUT);
  ibutton.reset(); 
  if (rwType == TM01) ibutton.write(0xC5);
    else ibutton.write(0xD5);        // команда на запись
  if (bitCnt == 36) BurnByteMC(keyID);
  else for (byte i = 0; i< (bitCnt >> 3); i++){
    digitalWrite(R_Led, !digitalRead(R_Led));
    if (rwType == RW1990_1) BurnByte(~keyID[i]);      // запись происходит инверсно для RW1990.1
      else BurnByte(keyID[i]);
    Serial.print('*');
    Sd_WriteStep();
  }
  if (bitCnt == 64) {
      ibutton.write(rwCmd);                     // send 0xD1 - флаг записи
      ibutton.write_bit(!rwFlag);               // записываем значение флага записи = 1 - отключаем запись
      delay(5); pinMode(iButtonPin, INPUT);
    }
  digitalWrite(R_Led, LOW);       
  if (!dataIsBurningOK(bitCnt)){                  // проверяем корректность записи
    Serial.println(F(" The key copy faild"));
    OLED_printError(F("The key copy faild"));
    Sd_ErrorBeep();
    digitalWrite(R_Led, HIGH);
    return false;
  }
  Serial.println(F(" The key has copied successfully"));
  if ((keyType == keyMetacom)||(keyType == keyCyfral)){      //переводим ключ из формата dallas
    ibutton.reset();
    if (keyType == keyCyfral) ibutton.write(0xCA);       // send 0xCA - флаг финализации Cyfral
      else ibutton.write(0xCB);                       // send 0xCB - флаг финализации metacom
    ibutton.write_bit(1);                             // записываем значение флага финализации = 1 - перевезти формат
    delay(10); pinMode(iButtonPin, INPUT);
  }
  OLED_printError(F("The key has copied"), false);
  Sd_ReadOK();
  delay(2000);
  digitalWrite(R_Led, HIGH);
  return true;
}

void BurnByte(byte data){
  for(byte n_bit = 0; n_bit < 8; n_bit++){ 
    ibutton.write_bit(data & 1);  
    delay(5);                        // даем время на прошивку каждого бита до 10 мс
    data = data >> 1;                // переходим к следующему bit
  }
  pinMode(iButtonPin, INPUT);
}

void BurnByteMC(byte buf[8]){
  byte j = 0;
  for(byte n_bit = 0; n_bit < 36; n_bit++){ 
    ibutton.write_bit(((~buf[n_bit>>3]) >> (7-j) ) & 1);  
    delay(5);                        // даем время на прошивку каждого бита 5 мс
    j++;
    if (j > 7) j = 0;
  }
  pinMode(iButtonPin, INPUT);
}

void convetr2MC(byte buff[8]){
  byte data;
  for (byte i = 0; i < 5; i++){
    data = ~buff[i];
    buff[i] = 0;
    for (byte j = 0; j < 8; j++) 
      if ( (data>>j)&1) bitSet(buff[i], 7-j);
  }
  buff[4] &= 0xf0;  buff[5] = 0; buff[6] = 0; buff[7] = 0;
}

bool dataIsBurningOK(byte bitCnt){
  byte buff[8];
  if (!ibutton.reset()) return false;
  ibutton.write(0x33);
  ibutton.read_bytes(buff, 8);
  if (bitCnt == 36) convetr2MC(buff);
  byte Check = 0;
  for (byte i = 0; i < 8; i++){ 
    if (keyID[i] == buff[i]) Check++;       // сравниваем код для записи с тем, что уже записано в ключе.
    Serial.print(buff[i], HEX); Serial.print(":");
  }
  if (Check != 8) return false;             // если коды совпадают, ключ успешно скопирован
  return true;
}

bool write2iBtn(){
  int Check = 0;
  if (!ibutton.search(addr)) { 
    ibutton.reset_search(); 
    return false;
  }
  Serial.print(F("The new key code is: "));
  for (byte i = 0; i < 8; i++) {
    Serial.print(addr[i], HEX); Serial.print(":");  
    if (keyID[i] == addr[i]) Check++;         // сравниваем код для записи с тем, что уже записано в ключе.
  }
  if (Check == 8) {                           // если коды совпадают, ничего писать не нужно
    digitalWrite(R_Led, LOW); 
    Serial.println(F(" it is the same key. Writing in not needed."));
    OLED_printError(F("It is the same key"));
    Sd_ErrorBeep();
    digitalWrite(R_Led, HIGH);
    delay(1000);
    return false;
  }
  emRWType rwType = getRWtype();                    // определяем тип RW-1990.1 или 1990.2 или TM-01
  Serial.print(F("\n Burning iButton ID: "));
  if (rwType == TM2004) return write2iBtnTM2004();  //шьем TM2004
    else return write2iBtnRW1990_1_2_TM01(rwType);  //пробуем прошить другие форматы
}

bool searchIbutton(){
  if (!ibutton.search(addr)) { 
    ibutton.reset_search(); 
    return false;
  }  
  for (byte i = 0; i < 8; i++) {
    Serial.print(addr[i], HEX); Serial.print(":");
    keyID[i] = addr[i];                               // копируем прочтенный код в ReadID
  }
  if (addr[0] == 0x01) {                              // это ключ формата dallas
    keyType = keyDallas;
    if (getRWtype() == TM2004) keyType = keyTM2004;
    if (OneWire::crc8(addr, 7) != addr[7]) {
      Serial.println(F("CRC is not valid!"));
      OLED_printError(F("CRC is not valid!"));
      Sd_ErrorBeep();
      digitalWrite(B_Led, HIGH);
      return false;
    }
    return true;
  }
  switch (addr[0]>>4){
    case 1: Serial.println(F(" Type: May be cyfral in dallas key")); break;      
    case 2: Serial.println(F(" Type: May be metacom in dallas key"));  break;  
    case 3: Serial.println(F(" Type: unknown family dallas")); break;             
  }
  keyType = keyUnknown;
  return true;
}

//************ Cyfral ***********************
unsigned long pulseACompA(bool pulse, byte Average = 80, unsigned long timeOut = 1500){  // pulse HIGH or LOW
  bool AcompState;
  unsigned long tEnd = micros() + timeOut;
  do {
    ADCSRA |= (1<<ADSC);
    while(ADCSRA & (1 << ADSC)); // Wait until the ADSC bit has been cleared
    if (ADCH > 200) return 0;
    if (ADCH > Average) AcompState = HIGH;  // читаем флаг компаратора
      else AcompState = LOW;
    if (AcompState == pulse) {
      tEnd = micros() + timeOut;
      do {
          ADCSRA |= (1<<ADSC);
          while(ADCSRA & (1 << ADSC)); // Wait until the ADSC bit has been cleared
        if (ADCH > Average) AcompState = HIGH;  // читаем флаг компаратора
          else AcompState = LOW;
        if (AcompState != pulse) return (unsigned long)(micros() + timeOut - tEnd);  
      } while (micros() < tEnd);
      return 0;                                                 //таймаут, импульс не вернуся оратно
    }             // end if
  } while (micros() < tEnd);
  return 0;
}

void ADCsetOn(){
  ADMUX = (ADMUX&0b11110000) | 0b0011 | (1<<ADLAR);// (1 << REFS0);          // подключаем к AC Линию A3 ,  левое выравние, измерение до Vcc
  ADCSRB = (ADCSRB & 0b11111000) | (1<<ACME);                // источник перезапуска ADC FreeRun, включаем мультиплексор AC
  ADCSRA = (ADCSRA & 0b11111000) |0b011 | (1<<ADEN) | (1<<ADSC);// | (1<<ADATE);      // 0b011 делитель скорости ADC, // включаем ADC и запускаем ADC и autotriger ADC 
}

void ACsetOn(){
  ACSR |= 1<<ACBG;                            // Подключаем ко входу Ain0 1.1V для Cyfral/Metacom
  ADCSRA &= ~(1<<ADEN);                       // выключаем ADC
  ADMUX = (ADMUX&0b11110000) | 0b0011;        // подключаем к AC Линию A3
  ADCSRB |= 1<<ACME;                          // включаем мультиплексор AC
}

bool read_cyfral(byte* buf, byte CyfralPin){
  unsigned long ti; byte i=0, j = 0, k = 0;
  analogRead(iButtonPin);
  ADCsetOn(); 
  byte aver = calcAverage();
  unsigned long tEnd = millis() + 30;
  do{
    ti = pulseACompA(HIGH, aver);
    if ((ti == 0) || (ti > 260) || (ti < 10)) {i = 0; j=0; k = 0; continue;}
    if ((i < 3) && (ti > halfT)) {i = 0; j = 0; k = 0; continue;}      //контроль стартовой последовательности 0b0001
    if ((i == 3) && (ti < halfT)) continue;      
    if (ti > halfT) bitSet(buf[i >> 3], 7-j);
      else if (i > 3) k++; 
    if ((i > 3) && ((i-3)%4 == 0) ){        //начиная с 4-го бита проверяем количество нулей каждой строки из 4-и бит
      if (k != 1) {for (byte n = 0; n < (i >> 3)+2; n++) buf[n] = 0; i = 0; j = 0; k = 0; continue;}        //если нулей больше одной - начинаем сначала 
      k = 0; 
    }
    j++; if (j>7) j=0;
    i++;
  } while ((millis() < tEnd) && (i < 36));
  if (i < 36) return false;
  return true;
}

bool searchCyfral(){
  byte buf[8];
  for (byte i = 0; i < 8; i++) {addr[i] =0; buf[i] = 0;}
  if (!read_cyfral(addr, iButtonPin)) return false;
  if (!read_cyfral(buf, iButtonPin)) return false;
  for (byte i = 0; i < 8; i++) 
    if (addr[i] != buf[i]) return false;
  keyType = keyCyfral;
  for (byte i = 0; i < 8; i++) {
    Serial.print(addr[i], HEX); Serial.print(":");
    keyID[i] = addr[i];                                         // копируем прочтенный код в ReadID
  }
  Serial.println(F(" Type: Cyfral "));
  return true;  
}

byte calcAverage(){
  unsigned int sum = 127; byte preADCH = 0, j = 0; 
  for (byte i = 0; i<255; i++) {
    ADCSRA |= (1<<ADSC);
    delayMicroseconds(10);
    while(ADCSRA & (1 << ADSC)); // Wait until the ADSC bit has been cleared
    sum += ADCH;
  }
  sum = sum >> 8;
  unsigned long tSt = micros();
  for (byte i = 0; i<255; i++) {
    delayMicroseconds(4);
    ADCSRA |= (1<<ADSC);
    while(ADCSRA & (1 << ADSC)); // Wait until the ADSC bit has been cleared
    if (((ADCH > sum)&&(preADCH < sum)) | ((ADCH < sum)&&(preADCH > sum))) {
      j++;
      preADCH = ADCH;
    }   
  }
  halfT = (byte)((micros() - tSt) / j);
  return (byte)sum;
}

bool read_metacom(byte* buf, byte MetacomPin){
  unsigned long ti; byte i = 0, j = 0, k = 0;
  analogRead(iButtonPin);
  ADCsetOn();
  byte aver = calcAverage();
  unsigned long tEnd = millis() + 30;
  do{
    ti = pulseACompA(LOW, aver);
    if ((ti == 0) || (ti > 500)) {i = 0; j=0; k = 0; continue;}
    if ((i == 0) && (ti+30 < (halfT<<1))) continue;      //вычисляем период;
    if ((i == 2) && (ti > halfT)) {i = 0; j = 0;  continue;}      //вычисляем период;
    if (((i == 1) || (i == 3)) && (ti < halfT)) {i = 0; j = 0; continue;}      //вычисляем период;
    if (ti < halfT) {   
      bitSet(buf[i >> 3], 7-j);
      if (i > 3) k++;                             // считаем кол-во единиц
    }
    if ((i > 3) && ((i-3)%8 == 0) ){        //начиная с 4-го бита проверяем контроль четности каждой строки из 8-и бит
      if (k & 1) { for (byte n = 0; n < (i >> 3)+1; n++) buf[n] = 0; i = 0; j = 0;  k = 0; continue;}              //если нечетно - начинаем сначала
      k = 0;
    }   
    j++; if (j>7) j=0;
    i++;
  }  while ((millis() < tEnd) && (i < 36));
  if (i < 36) return false;
  return true;
}

bool searchMetacom(){
  byte buf[8];
  for (byte i = 0; i < 8; i++) {addr[i] =0; buf[i] = 0;}
  if (!read_metacom(addr, iButtonPin)) return false;
  if (!read_metacom(buf, iButtonPin)) return false;
  for (byte i = 0; i < 8; i++) 
    if (addr[i] != buf[i]) return false; 
  keyType = keyMetacom;
  for (byte i = 0; i < 8; i++) {
    Serial.print(addr[i], HEX); Serial.print(":");
    keyID[i] = addr[i];                               // копируем прочтенный код в ReadID
  }
  Serial.println(F(" Type: Metacom "));
  return true;  
}

//**********EM-Marine***************************
bool vertEvenCheck(byte* buf){        // проверка четности столбцов с данными
  byte k;
  k = 1&buf[1]>>6 + 1&buf[1]>>1 + 1&buf[2]>>4 + 1&buf[3]>>7 + 1&buf[3]>>2 + 1&buf[4]>>5 + 1&buf[4] + 1&buf[5]>>3 + 1&buf[6]>>6 + 1&buf[6]>>1 + 1&buf[7]>>4;
  if (k&1) return false;
  k = 1&buf[1]>>5 + 1&buf[1] + 1&buf[2]>>3 + 1&buf[3]>>6 + 1&buf[3]>>1 + 1&buf[4]>>4 + 1&buf[5]>>7 + 1&buf[5]>>2 + 1&buf[6]>>5 + 1&buf[6] + 1&buf[7]>>3;
  if (k&1) return false;
  k = 1&buf[1]>>4 + 1&buf[2]>>7 + 1&buf[2]>>2 + 1&buf[3]>>5 + 1&buf[3] + 1&buf[4]>>3 + 1&buf[5]>>6 + 1&buf[5]>>1 + 1&buf[6]>>4 + 1&buf[7]>>7 + 1&buf[7]>>2;
  if (k&1) return false;
  k = 1&buf[1]>>3 + 1&buf[2]>>6 + 1&buf[2]>>1 + 1&buf[3]>>4 + 1&buf[4]>>7 + 1&buf[4]>>2 + 1&buf[5]>>5 + 1&buf[5] + 1&buf[6]>>3 + 1&buf[7]>>6 + 1&buf[7]>>1;
  if (k&1) return false;
  if (1&buf[7]) return false;
  //номер ключа, который написан на корпусе
  rfidData[0] = (0b01111000&buf[1])<<1 | (0b11&buf[1])<<2 | buf[2]>>6;
  rfidData[1] = (0b00011110&buf[2])<<3 | buf[3]>>4;
  rfidData[2] = buf[3]<<5 | (0b10000000&buf[4])>>3 | (0b00111100&buf[4])>>2;
  rfidData[3] = buf[4]<<7 | (0b11100000&buf[5])>>1 | 0b1111&buf[5];
  rfidData[4] = (0b01111000&buf[6])<<1 | (0b11&buf[6])<<2 | buf[7]>>6;
  return true;
}

byte ttAComp(unsigned long timeOut = 7000){  // pulse 0 or 1 or -1 if timeout
  byte AcompState, AcompInitState;
  unsigned long tEnd = micros() + timeOut;
  AcompInitState = (ACSR >> ACO)&1;               // читаем флаг компаратора
  do {
    AcompState = (ACSR >> ACO)&1;                 // читаем флаг компаратора
    if (AcompState != AcompInitState) {
      delayMicroseconds(1000/(rfidBitRate*4));    // 1/4 Period on 2 kBps = 125 mks 
      AcompState = (ACSR >> ACO)&1;               // читаем флаг компаратора      
      delayMicroseconds(1000/(rfidBitRate*2));    // 1/2 Period on 2 kBps = 250 mks 
      return AcompState;  
    }
  } while (micros() < tEnd);
  return 2;                                             //таймаут, компаратор не сменил состояние
}

bool readEM_Marie(byte* buf){
  unsigned long tEnd = millis() + 50;
  byte ti; byte j = 0, k=0;
  for (int i = 0; i<64; i++){    // читаем 64 bit
    ti = ttAComp();
    if (ti == 2)  break;         //timeout
    if ( ( ti == 0 ) && ( i < 9)) {  // если не находим 9 стартовых единиц - начинаем сначала
      if (millis() > tEnd) { ti=2; break;}  //timeout
      i = -1; j=0; continue;
    }
    if ((i > 8) && (i < 59)){     //начиная с 9-го бита проверяем контроль четности каждой строки
      if (ti) k++;                // считаем кол-во единиц
      if ( (i-9)%5 == 4 ){        // конец строки с данными из 5-и бит, 
        if (k & 1) {              //если нечетно - начинаем сначала
          i = -1; j = 0; k = 0; continue; 
        }
        k = 0;
      }
    }
    if (ti) bitSet(buf[i >> 3], 7-j);
      else bitClear(buf[i >> 3], 7-j);
    j++; if (j>7) j=0; 
  }
  if (ti == 2) return false;         //timeout
  return vertEvenCheck(buf);
}

void rfidACsetOn(){
  //включаем генератор 125кГц
  pinMode(FreqGen, OUTPUT);
  // настраиваем таймер 2 для генерации сигнала на выводе 3
  TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); // Включаем режим Phase correct PWM (8-битный) и счет таймера2 до OCR2A
  TCCR2B = _BV(WGM22) | _BV(CS20);               // Задаем делитель для таймера2 = 1 (частота 16 МГц)
  OCR2A = 125;                                  // Задаем период таймера (125 тактов на период, 125 кГц на выводе 3)
  OCR2B = 62;                                   // Задаем скважность на 50% (62/125)

  // включаем компаратор
  ADCSRB &= ~(1<<ACME);           // отключаем мультиплексор AC
  ACSR &= ~(1<<ACBG);             // отключаем от входа Ain0 1.1V
}

bool searchEM_Marine( bool copyKey = true){
//  byte gr = digitalRead(G_Led);
  bool rez = false;
  rfidACsetOn();            // включаем генератор 125кГц и компаратор
  delay(6);                //13 мс длятся переходные прцессы детектора 
  if (!readEM_Marie(addr)) {
    if (!copyKey) TCCR2A &= ~_BV(COM2B1);              //Оключить ШИМ COM2B (pin 3)
//    digitalWrite(G_Led, gr);
    return rez;
  }
  rez = true;
  keyType = keyEM_Marine;
  for (byte i = 0; i<8; i++){
    if (copyKey) keyID[i] = addr [i];
    Serial.print(addr[i], HEX); Serial.print(":");
  }
  Serial.print(F(" ( id "));
  Serial.print(rfidData[0]); Serial.print(" key ");
  unsigned long keyNum = (unsigned long)rfidData[1]<<24 | (unsigned long)rfidData[2]<<16 | (unsigned long)rfidData[3]<<8 | (unsigned long)rfidData[4];
  Serial.print(keyNum);
  Serial.println(F(") Type: EM-Marie "));
  if (!copyKey) TCCR2A &= ~_BV(COM2B1);              //Оключить ШИМ COM2B (pin 3)
//  digitalWrite(G_Led, gr);
  return rez;
}

void TxBitRfid(byte data){
  if (data & 1) delayMicroseconds(54*8); 
    else delayMicroseconds(24*8);
  rfidGap(19*8);                       //write gap
}

void TxByteRfid(byte data){
  for(byte n_bit=0; n_bit<8; n_bit++){
    TxBitRfid(data & 1);
    data = data >> 1;                   // переходим к следующему bit
  }
}

void rfidGap(unsigned int tm){
  TCCR2A &= ~_BV(COM2B1);                //Оключить ШИМ COM2B (pin 3)   
  delayMicroseconds(tm);
  TCCR2A |= _BV(COM2B1);              // Включить ШИМ COM2B (pin 3)      
}

bool T5557_blockRead(byte* buf){
  byte ti; byte j = 0, k=0;
  for (int i = 0; i<33; i++){                           // читаем стартовый 0 и 32 значащих bit
    ti = ttAComp(2000);
    if (ti == 2)  break;                                //timeout
    if ( ( ti == 1 ) && ( i == 0)) { ti=2; break; }     // если не находим стартовый 0 - это ошибка
    if (i > 0){                                         //начиная с 1-го бита пишем в буфер
      if (ti) bitSet(buf[(i-1) >> 3], 7-j);
        else bitClear(buf[(i-1) >> 3], 7-j);
      j++; if (j>7) j=0;
    }
  }
  if (ti == 2) return false;                           //timeout
  return true;
}

bool sendOpT5557(byte opCode, unsigned long password = 0, byte lockBit = 0, unsigned long data = 0, byte blokAddr = 1){
  TxBitRfid(opCode >> 1); TxBitRfid(opCode & 1); // передаем код операции 10
  if (opCode == 0b00) return true;
  // password
  TxBitRfid(lockBit & 1);               // lockbit 0
  if (data != 0){
    for (byte i = 0; i<32; i++) {
      TxBitRfid((data>>(31-i)) & 1);
    }
  }
  TxBitRfid(blokAddr>>2); TxBitRfid(blokAddr>>1); TxBitRfid(blokAddr & 1);      // адрес блока для записи
  delay(4);                                                                     // ждем пока пишутся данные
  return true;
}

bool write2rfidT5557(byte* buf){
  bool result; unsigned long data32;
  delay(6);
  for (byte k = 0; k<2; k++){                                       // send key data
    data32 = (unsigned long)buf[0 + (k<<2)]<<24 | (unsigned long)buf[1 + (k<<2)]<<16 | (unsigned long)buf[2 + (k<<2)]<<8 | (unsigned long)buf[3 + (k<<2)];
    rfidGap(30 * 8);                                                 //start gap
    sendOpT5557(0b10, 0, 0, data32, k+1);                            //передаем 32 бита ключа в blok k
    Serial.print('*'); delay(6);
  }
  delay(6);
  rfidGap(30 * 8);                  //start gap
  sendOpT5557(0b00);
  delay(4);
  result = readEM_Marie(addr);
  TCCR2A &= ~_BV(COM2B1);              //Оключить ШИМ COM2B (pin 3)
  for (byte i = 0; i < 8; i++)
    if (addr[i] != keyID[i]) { result = false; break; }
  if (!result){
    Serial.println(F(" The key copy faild"));
    OLED_printError(F("The key copy faild"));
    Sd_ErrorBeep();
  } else {
    Serial.println(F(" The key has copied successfully"));
    OLED_printError(F("The key has copied"), false);
    Sd_ReadOK();
    delay(2000);
  }
  digitalWrite(R_Led, HIGH);
  return result;  
}

emRWType getRfidRWtype(){
  unsigned long data32, data33; byte buf[4] = {0, 0, 0, 0}; 
  rfidACsetOn();                // включаем генератор 125кГц и компаратор
  delay(13);                    //13 мс длятся переходные процессы детектора
  rfidGap(30 * 8);              //start gap
  sendOpT5557(0b11, 0, 0, 0, 1); //переходим в режим чтения Vendor ID 
  if (!T5557_blockRead(buf)) return rwUnknown; 
  data32 = (unsigned long)buf[0]<<24 | (unsigned long)buf[1]<<16 | (unsigned long)buf[2]<<8 | (unsigned long)buf[3];
  delay(4);
  rfidGap(20 * 8);          //gap  
  data33 = 0b00000000000101001000000001000000 | (rfidUsePWD << 4);   //конфиг регистр 0b00000000000101001000000001000000
  sendOpT5557(0b10, 0, 0, data33, 0);   //передаем конфиг регистр
  delay(4);
  rfidGap(30 * 8);          //start gap
  sendOpT5557(0b11, 0, 0, 0, 1); //переходим в режим чтения Vendor ID 
  if (!T5557_blockRead(buf)) return rwUnknown; 
  data33 = (unsigned long)buf[0]<<24 | (unsigned long)buf[1]<<16 | (unsigned long)buf[2]<<8 | (unsigned long)buf[3];
  sendOpT5557(0b00, 0, 0, 0, 0);  // send Reset
  delay(6);
  if (data32 != data33) return rwUnknown;    
  Serial.print(F(" The rfid RW-key is T5557. Vendor ID is "));
  Serial.println(data32, HEX);
  return T5557;
}

bool write2rfid(){
  bool Check = true;
  if (searchEM_Marine(false)) {
    for (byte i = 0; i < 8; i++)
      if (addr[i] != keyID[i]) { Check = false; break; }  // сравниваем код для записи с тем, что уже записано в ключе.
    if (Check) {                                          // если коды совпадают, ничего писать не нужно
      digitalWrite(R_Led, LOW); 
      Serial.println(F(" it is the same key. Writing in not needed."));
      OLED_printError(F("It is the same key"));
      Sd_ErrorBeep();
      digitalWrite(R_Led, HIGH);
      delay(1000);
      return false;
    }
  }
  emRWType rwType = getRfidRWtype(); // определяем тип T5557 (T5577) или EM4305
  if (rwType != rwUnknown) Serial.print(F("\n Burning rfid ID: "));
  switch (rwType){
    case T5557: return write2rfidT5557(keyID); break;                    //пишем T5557
    //case EM4305: return write2rfidEM4305(keyID); break;                  //пишем EM4305
    case rwUnknown: break;
  }
  return false;
}


//**********Mifare***************************
bool searchMifare() {
  bool result = false;
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }
  // Ожидаем считывания метки
  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
    // Копируем UID метки в массив
    for (byte i = 0; i < rfid.uid.size; i++) {
      addr[i] = rfid.uid.uidByte[i];
    }
    rfid.PICC_HaltA();          // Останавливаем считывание метки
    keyType = keyMifare;
    for (byte i = 0; i<8; i++){
      keyID[i] = addr [i];
      Serial.print(addr[i], HEX); Serial.print(":");
    }
    Serial.println(F(") Type: Mifare "));
    result = true;
  }
  return result;
}

bool write2Mifare() {
  bool result = false;
  bool Check = false;
  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }
  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
    // Считываем UID метки
    for (byte i = 0; i < rfid.uid.size; i++) {
      addr[i] = rfid.uid.uidByte[i];
      if (addr[i] != keyID[i]) { Check = true; break; }  // сравниваем код для записи с тем, что уже записано в ключе.
    }
    if (Check) {        // сравниваем код для записи с тем, что уже записано в ключе.
      // Если коды не совпадают пишем новый UID на метку
      if ( rfid.MIFARE_SetUid(keyID, (byte)4, true) ) {
        Serial.println(F(" The key has copied successfully "));
        OLED_printError(F("The key has copied"), false);
        Sd_ReadOK();
        result = true;
        statOk = result;
      }
      else {
        digitalWrite(R_Led, LOW); 
        Serial.println(F(" The key copy faild "));
        OLED_printError(F("The key copy faild"));
        Sd_ErrorBeep();
        digitalWrite(R_Led, HIGH);
        delay(1000);
      }
    }
    else {
      digitalWrite(R_Led, LOW); 
      Serial.println(F(" it is the same key. Writing in not needed."));
      OLED_printError(F("It is the same key"));
      Sd_ErrorBeep();
      digitalWrite(R_Led, HIGH);
      delay(1000);
    }
  }
  rfid.PICC_HaltA();                // Останавливаем считывание метки
  rfid.PCD_StopCrypto1();
  return result;
}

#ifndef BLUE_MODE
bool clear2Mifare() {
  bool result = false;
  if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
    Serial.println("Card detected");
    // Очищаем карту
    for (byte i = 0; i < 16; i++) {
      byte buffer[18];
      memset(buffer, 0, sizeof(buffer));
      if (rfid.MIFARE_Write(i, buffer, 16)) {
        Serial.println("Block " + String(i) + " cleared");
        OLED_printError(F("Clear ..."), false);
        Sd_WriteStep();
      } 
      else {
        Serial.println("Failed to clear block " + String(i));
        OLED_printError(F("Failed cleared"));
        Sd_ErrorBeep();
        result = false;
      }
    }
    // Останавливаем считывание метки
    rfid.PICC_HaltA();
    rfid.PCD_StopCrypto1();
    Sd_ReadOK();
    OLED_printError(F("Cleared successfully"), false);
    result = true;
  }
  else if ( rfid.MIFARE_UnbrickUidSector(false) ) {
    Serial.println(F("Cleared sector 0, set UID to 1234. Card should be responsive again now."));
    OLED_printError(F("Unbrick Sector 0"), false);
    Sd_ReadOK();
    result = true;
  }
  statOk = result;
  return result;
}
#endif

//**********BlueMode***************************
#ifdef BLUE_MODE
void SendEM_Marine(byte* buf){ 
  TCCR2A &= ~_BV(COM2B1); // отключаем шим 
  digitalWrite(FreqGen, LOW);
  //FF:A9:8A:A4:87:78:98:6A
  delay(20);
  for (byte k = 0; k<10; k++){
    for (byte i = 0; i<8; i++){
      for (byte j = 0; j<8; j++){
        if (1 & (buf[i]>>(7-j))) {
          pinMode(FreqGen, INPUT);
          delayMicroseconds(250);
          pinMode(FreqGen, OUTPUT); 
          delayMicroseconds(250);
        } else {
          pinMode(FreqGen, OUTPUT);
          delayMicroseconds(250);
          pinMode(FreqGen, INPUT);
          delayMicroseconds(250);
        }
      }
    }
   // delay(1);
  }  
}

void SendDallas(byte* buf){
  iBtnEmul.init(buf);
  //iBtnEmul.waitForRequest(false);
  unsigned long tStart = millis();
  do {
    if (!iBtnEmul.waitReset(10) ) continue;
    if (!iBtnEmul.presence() ) continue;
    if (iBtnEmul.recvAndProcessCmd() ) break;
  } while (millis() < 200 + tStart);  
}

void BM_SendKey(byte* buf){
  switch (keyType){
    case keyEM_Marine: SendEM_Marine(buf); break;
    default: SendDallas(buf); break;
  }
}
#endif

unsigned long stTimer = millis();
void loop() {
  char echo = Serial.read();
  if (echo == 'e'){
    myOLED.print(F("EEPROM cleared success!"), 0, 0);
    Serial.println(F("EEPROM cleared"));
    EEPROM.update(0, 0); EEPROM.update(1, 0);
    EEPROM_key_count = 0; EEPROM_key_index = 0;
    Sd_ReadOK();
    myOLED.update();
  }
  if ((echo == 't') || enc1.isClick()) {  // переключаель режима чтение/запись
    switch (copierMode){
      case md_empty: Sd_ErrorBeep(); break;
      case md_read: copierMode = md_write; clearLed(); digitalWrite(R_Led, HIGH);  break;
        delayMicroseconds(5); break;
      case md_write: copierMode = md_threeMode; clearLed(); digitalWrite(B_Led, HIGH); 
        delayMicroseconds(5); break;
      case md_threeMode: copierMode = md_read; clearLed(); digitalWrite(G_Led, HIGH); 
        delayMicroseconds(5); break;
    }
    OLED_printKey(keyID);
    Serial.print(F("Mode: ")); Serial.println(copierMode);
    Sd_WriteStep();
    statOk = false;
  }
  if (enc1.isLeft() && (EEPROM_key_count > 0)){       //при повороте энкодера листаем ключи из eeprom
    EEPROM_key_index--;
    if (EEPROM_key_index < 1) EEPROM_key_index = EEPROM_key_count;
    EEPROM_get_key(EEPROM_key_index, keyID);
    OLED_printKey(keyID);
    Sd_WriteStep();
    // statOk = false;
  }
  if (enc1.isRight() && (EEPROM_key_count > 0)){
    EEPROM_key_index++;
    if (EEPROM_key_index > EEPROM_key_count) EEPROM_key_index = 1;
    EEPROM_get_key(EEPROM_key_index, keyID);
    OLED_printKey(keyID);
    Sd_WriteStep();
    // statOk = false;
  }
  if ((copierMode != md_empty) && enc1.isHolded()){     // Если зажать кнопкку - ключ сохранися в EEPROM
    if (EPPROM_AddKey(keyID)) {
      OLED_printError(F("The key saved"), false);
      Sd_ReadOK();
      delay(1000); 
    }
      else Sd_ErrorBeep();
    OLED_printKey(keyID);  
  }

  if (millis() - stTimer < 100) return; //задержка в 100 мс
  stTimer = millis();
  switch (copierMode){
      case md_empty: case md_read: 
        if (searchCyfral() || searchMetacom() || searchEM_Marine() || searchIbutton() || searchMifare()){     // запускаем поиск cyfral, затем поиск EM_Marine, затем поиск dallas
          //keyID[0] = 0xFF; keyID[1] = 0xA9; keyID[2] =  0x8A; keyID[3] = 0xA4; keyID[4] = 0x87; keyID[5] = 0x78; keyID[6] = 0x98; keyID[7] = 0x6A;
          Sd_ReadOK();
          copierMode = md_read;
          digitalWrite(G_Led, HIGH);
          if (indxKeyInROM(keyID) == 0) OLED_printKey(keyID, 1);
            else OLED_printKey(keyID, 3);
          } 
        break;
      case md_write:
        if (keyType == keyEM_Marine) write2rfid();
          else if (keyType == keyMifare) write2Mifare();
          else write2iBtn(); 
        break;
      case md_threeMode: 
        #ifdef BLUE_MODE
          BM_SendKey(keyID);
          break;
        #endif
        #ifndef BLUE_MODE
          if(!statOk){
          clear2Mifare();
          break;
          }
        #endif
    } //end switch
}

//***************** звуки****************
void Sd_ReadOK() {  // звук ОК
  for (int i=400; i<6000; i=i*1.5) { tone(speakerPin, i); delay(20); }
  noTone(speakerPin);
}

void Sd_WriteStep(){  // звук "очередной шаг"
  for (int i=2500; i<6000; i=i*1.5) { tone(speakerPin, i); delay(10); }
  noTone(speakerPin);
}

void Sd_ErrorBeep() {  // звук "ERROR"
  for (int j=0; j <3; j++){
    for (int i=1000; i<2000; i=i*1.1) { tone(speakerPin, i); delay(10); }
    delay(50);
    for (int i=1000; i>500; i=i*1.9) { tone(speakerPin, i); delay(10); }
    delay(50);
  }
  noTone(speakerPin);
}

void Sd_StartOK(){   // звук "Успешное включение"
  tone(speakerPin, NOTE_A7); delay(100);
  tone(speakerPin, NOTE_G7); delay(100);
  tone(speakerPin, NOTE_E7); delay(100); 
  tone(speakerPin, NOTE_C7); delay(100);  
  tone(speakerPin, NOTE_D7); delay(100); 
  tone(speakerPin, NOTE_B7); delay(100); 
  tone(speakerPin, NOTE_F7); delay(100); 
  tone(speakerPin, NOTE_C7); delay(100);
  noTone(speakerPin); 
}


Вот предыдущая схема.
На пин D6 должно поступать напряжение 0,1 вольта. Так настроить не смог у меня получилось 0,5 вольта стабильно работает.
В последней схеме, автор посадил пин D6 на землю, наверное 0,1 вольта задаются в скетче? Если так можно ли задать изменить на 0,5 вольт?

Да спасибо! Вроде Бы он.

У меня смутные подозрения…
Это цифровые пины.
Ааа, D6 у нас компаратор, поняна.

В таком варианте работает устойчиво. Самое главное как можно точнее подобрать число витков катушки под емкость.

на меандре это не актуально, ± 5 витков погоды не сделают

Компаратор, обычно там 2 входа должно быть, а 2 вход на какой пин выходит?

Я много перепробовал, предполагаю это должно быть идеальный конденсатор, кварц, и супер добротная катушка. Да и при таких условиях будут возникать ситуации какие то ключи не читает и ошибки при записи. Поэтому пока поставил на 6 пин резистор чтобы не на земле был а 0,5в

Точно не помню, но он может быть и внутренний от ИОН.
Может кто из форумчан посмотрит в коде настройку компаратора, мне пока лень

void rfidACsetOn(){  
// включаем компаратор
  ADCSRB &= ~(1<<ACME);           // отключаем мультиплексор AC
  ACSR &= ~(1<<ACBG);             // отключаем от входа Ain0 1.1V
}
void ACsetOn(){
  ACSR |= 1<<ACBG;                            // Подключаем ко входу Ain0 1.1V для Cyfral/Metacom
  ADCSRA &= ~(1<<ADEN);                       // выключаем ADC
  ADMUX = (ADMUX&0b11110000) | 0b0011;        // подключаем к AC Линию A3
  ADCSRB |= 1<<ACME;                          // включаем мультиплексор AC
}

Тут не понятно что делает, мне не реально на телефоне отследить в этой простыне.
Кстати, а где SETUP, LOOP? Не могу найти.

@мелкий Вы бы уж определились, какую версию дубликатора Вы пытаетесь реализовать. В первом сообщении привели одну схему, потом вдруг начали задавать вопросы уже по другой. Если делаете ту, из которой автор выкинул эту самую подстройку напряжения на D6, зачем её (подстройку) обсуждать? Сказали на землю - значит на землю! :slight_smile:

А что не устраивает? Устанавливаете, с помощью делителя любое напряжение…
Только внутреннее опорное отключаете

 ACSR &= ~(1<<ACBG);             // отключаем от входа Ain0 1.1V

В Том то и дело что пришлось в схему без резистора поставить его для стабильной работы на 125кгц.

Я не отключал опорное просто резистор поставил.

Просто резистор ничего не даст. Нужно отключить внутреннее опорное напряжение, и, установить внешнее, делителем на резисторах, (от напряжения питания 5В). Можно взять многооборотный подстроечник, и установить нужное значение

Я пока нечего в скетчи не менял, но с делителем сразу все мои настроенные катушки заработали. Хотел ещë чтобы данные выводились на экран и в монитор порта, порт видимо закомментирован.

Нет.(Если это тот скетч ).Скорость соответствующую в мониторе установите

Значит в коде уже отключено, иначе не получится.

Опять какая то не увязка, вчера и перепрошивал скорость не менял там вроде 115 000 так и выставлена, предыдущий вариант схемы, пока был собран выводил инфу последний не выводит, только на дсплей.

115200 - стандартная скорость. Но должна быть не только в скетче выставлена, но и в мониторе на компе, где смотрите