/* Dmitry OSIPOV. http://www.youtube.com/user/d36073?feature=watch
  Arduino Audio Recorder V.1-20160524.
  ----------
   Arduino Projects: Digital Audio Recorder.
  http://apcmag.com/arduino-projects-digital-audio-recorder.htm/
  ----------
  SD-карту лучше класс 10 и выше.
  Питание от батареи или аккумулятора.
  Микрофон подключить к pin (A5).
  ----------
  Download - sketch "Arduino Audio Recorder V.1-20160524"
  Библиотеку - SdFat.
  Фото.
  https://yadi.sk/d/zqi8-L5xruhFq
  + sketch оставлю в комментарии - под видео.
  ----------
*/
#include <LiquidCrystal_I2C.h>
#include <SdFat.h> // https://yadi.sk/d/zqi8-L5xruhFq
//#include <EEPROM.h>

#include <RTClib.h>

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

SdFat sd;
SdFile rec;
RTC_DS1307 rtc;
LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
const int chipSelect = 4; /* Поменял на 10-pln */
unsigned long fileSize = 0L;
unsigned long waveChunk = 16;
unsigned int waveType = 1;
unsigned int numChannels = 1;
unsigned long sampleRate = 22050;
unsigned long bytesPerSec = 22050;
unsigned int blockAlign = 1;
unsigned int bitsPerSample = 8;
unsigned long dataSize = 0L;
unsigned long recByteCount = 0L;
unsigned long recByteSaved = 0L;
const int btnStart = 6;
const int btnStop = 5;
const int ledStart = 2;
const int ledStop = 3;
int recPressed = 0;
int stopPressed = 0;
unsigned long oldTime = 0L;
unsigned long newTime = 0L;
byte buf00[64]; // buffer array 1
byte buf01[64]; // buffer array 2
unsigned int bufByteCount;
byte bufWrite;

unsigned long timer1;
int timer1on = 0;
//----------
//char NameRecord[4]; /* Имя нового - записываемого файла на SD-карту. */
//int RecordNumber; /* Номер записи - храним в EEPROM. в диапазоне 0..255. */
//char filename[12];

char filename[22];
char foldername[10];

unsigned long timerLCL;
int timerLCLon=0;

unsigned long timerRS;
int timerRSon=0;

int SrV,rsValue, procSrV, SrVmid;

char buffer[16] ;

char sound = -1; //звук с канала 0х61
volatile int nosound = -1; //где что-то с канала 0x60

void setup()  // THIS RUNS ONCE //
{
    lcd.init();
    lcd.backlight();
    //lcd.setCursor(3,0);
    //lcd.print("Hello, world!");

    rtc.begin();

    //EEPROM.put(0, 20000);
  
    /// Timer ///
    TCCR2B = _BV(CS21);  // Timer2 Clock Prescaler to : 8   
    TCCR2A = _BV(WGM21); // Interupt frequency  = 16MHz / (8 x 90 + 1) = 22191Hz
    OCR2A = 90; // Compare Match register set to 90
    
    /// ADC ///
    ADMUX = 0x61; // set ADC to read pin A5, ADLAR to 1 (left adjust)
    ADCSRA = 0x03; // set prescaler to 8 / ADC clock = 2MHz
    sbi(ADCSRA, ADIE); // включили прерывания

    /// PIN ///
    pinMode(10, OUTPUT);
    pinMode(ledStart, OUTPUT);
    pinMode(ledStop, OUTPUT);
    pinMode(btnStop, INPUT_PULLUP);
    pinMode(btnStart, INPUT_PULLUP);

    /// SD ///
    if (sd.begin(chipSelect, SPI_FULL_SPEED))// initialise card on SPI to 8MHz SPI bus speed
    { 
        for (char dloop = 3; dloop > 0; dloop--)
        {
            digitalWrite(ledStart, !digitalRead(ledStart));
            delay(100);
        }
    } 
    else 
    {
        lcd.setCursor(0,1);
        lcd.print("SD Card not found");    
        while (1) // if error, flash LED twice per second, until reset
        {
            digitalWrite(ledStart, !digitalRead(ledStart));
            delay(500);
        }
    }
    sei();  // включить глобальные прерывания
    /// запуск ADC одиночное преобразование  ///
    sbi(ADCSRA, ADSC);
}


void loop()  // THIS RUNS LOTS!
{
    ///здесь обработали sound ОБРАБОТКA ВХОДА А1 ///
    if (sound >= 0) // ацп что-то сохранило в sound  
    {
        recByteCount++; // increment sample counter
        bufByteCount++;
        if ((bufByteCount == 64) && (!bufWrite))
        {
            bufByteCount = 0;
            bufWrite = 1;
        } 
        else if ((bufByteCount == 64) && (bufWrite))
        {
            bufByteCount = 0;
            bufWrite = 0;
        }

        if (!bufWrite) 
        {
            buf00[bufByteCount] = sound;
        }
        else
        {
            buf01[bufByteCount] = sound;
        }
        sound = -1; // сбросили признак того, что ацп что-то сохранило

        if (recPressed)
        {
            if (recByteCount % 128 == 64)
            {
                rec.write(buf00, 64);  // save buf01 to card
                recByteSaved += 64;
            }
            if (recByteCount % 128 == 0)
            {
                rec.write(buf01, 64);  // save buf02 to card
                recByteSaved += 64;
            }
        }
    } 

    ///здесь обработали nosound ОБРАБОТКA ВХОДА А0 ///
    if (nosound >= 0) // ацп что-то сохранило в nosound 
    {
        // Каждые 100 мс мы накапливаем значения (для усреднения)
        if (timerRSon==0)
        {   // запустим таймер для отображения часов
            timerRS=millis();  // включим миллис
            timerRSon=1;
        }
        if (timerRSon==1)
        {
            if (millis()-timerRS>50)
            {
                //rsValue=analogRead(A0);
                SrV=SrV+rsValue;
                timerRSon=0;
            }
        }
        // Каждые 1000 мс мы отображаем усредненное значение
        if (timerLCLon==0)
        {   // запустим таймер для отображения часов
            timerLCL=millis();  // включим миллис
            timerLCLon=1;
        }
        if (timerLCLon==1)
        {
            if (millis()-timerLCL>500)
            {
                lcd.setCursor(0,0);
                SrVmid=SrV/10;
                if (SrVmid<125) SrVmid=125;    // меньше 125 - это 0%
                procSrV=((SrVmid-125)*100)/194;  //в % макс -318(320), мин 125, 320-125=195   
                sprintf(buffer, "Power:%03i (%02i)",SrV/10,procSrV);
                lcd.print(buffer);
                //Show_time();
                timerLCLon=0;
     
                SrV=0;
                ///////////////////////
            }
        }
            
        nosound = -1;  // сбросили признак того, что ацп что-то сохранило
    }

    /// Обработка кнопок ///
    if (digitalRead(btnStart) == LOW && recPressed == 0)
    {
        StartRec(); // launch StartRec method
        timer1on = 0;
    }
    else if (digitalRead(btnStart) == HIGH && recPressed == 1 && timer1on == 0)
    {
        timer1 = millis();
        timer1on = 1;
    }
    else if (digitalRead(btnStart) == LOW && recPressed == 1 && timer1on == 1)
    {
        timer1on = 0;
    }
    else if (digitalRead(btnStart) == HIGH && recPressed == 1 && timer1on == 1 && (millis() - timer1) > 2000)
    {
        StopRec(); // launch StopRec method
        ((void (*)())0x0000)(); /* Делаем программную перезагрузку - Arduino */
    }
}

ISR(ADC_vect)
{
    if (bit_is_set(ADMUX,0))  // проверка 0х61
    {
        sound = ADCH; // сохраняем звук (как я понял, только старший байт берётся)
        cbi(ADMUX,0); //переключаем на другой канал (на A0)
     } else {
        notsound = ADCL; // сохраняем младший байт результата АЦП, при этом блокируется запись в ADCH
        notsound |= (ADCH << 8); // сохраняем старший байт АЦП, тем самым снимая блокировку и получая данные notsound
        sbi(ADMUX,0); //переключаем на другой канал (на A1)
     }
     sbi(ADCSRA, ADSC); // запуск ADC заново.
}


ISR(TIMER2_COMPA_vect) 
{

}

void dateTime(uint16_t* date, uint16_t* time) 
{
    DateTime now = rtc.now();    
    *date = FAT_DATE(now.year(), now.month(), now.day());// return date using FAT_DATE macro to format fields    
    *time = FAT_TIME(now.hour(), now.minute(), now.second());// return time using FAT_TIME macro to format fields
}

void Show_time()
{
    DateTime now = rtc.now();
    lcd.setCursor(1, 1);
    lcd.print(now.hour());
    lcd.print(":");
    lcd.print(now.minute());
    lcd.print(":");     
    lcd.print(now.second());
}

void StartRec() // begin recording process
{ 
    DateTime now = rtc.now();
    String strFile,strFolder;  //declaring string.
    strFolder = String(now.timestamp(DateTime::TIMESTAMP_DATE));
    strFolder.replace("-", "");
    strFolder.toCharArray(foldername, sizeof(foldername));
    sd.mkdir(foldername);  // Создаю директорию - пробовал без этого - никак
    strFolder.concat("/");
    strFile=String(now.timestamp(DateTime::TIMESTAMP_TIME));
    strFile.replace(":", "-");
    strFile.concat(".wav");
    strFolder.concat(strFile);
    strFolder.toCharArray(filename, sizeof(filename));

    digitalWrite(ledStart, HIGH);
    digitalWrite(ledStop, LOW);
    recByteCount = 0;
    recByteSaved = 0;
    recPressed = 1; // recording button has been pressed
    stopPressed = 0;
    
    /// Start write out original WAV header to file ///
    recByteSaved = 0;
    SdFile::dateTimeCallback(dateTime);
    rec.open(filename, O_CREAT | O_TRUNC | O_RDWR); /* Имя нового - записываемого файла на SD-карту. */
    
    rec.write("RIFF");
    
    rec.write(fileSize & 0xff);
    rec.write((fileSize >> 8) & 0xff);
    rec.write((fileSize >> 16) & 0xff);
    rec.write((fileSize >> 24) & 0xff);
    
    rec.write("WAVE");
    rec.write("fmt ");
    
    rec.write(waveChunk & 0xff);
    rec.write((waveChunk >> 8) & 0xff);
    rec.write((waveChunk >> 16) & 0xff);
    rec.write((waveChunk >> 24) & 0xff);
    
    rec.write(waveType & 0xff);
    rec.write((waveType >> 8) & 0xff);
    
    rec.write(numChannels & 0xff);
    rec.write((numChannels >> 8) & 0xff);
    
    rec.write(sampleRate & 0xff);
    rec.write((sampleRate >> 8) & 0xff);
    rec.write((sampleRate >> 16) & 0xff);
    rec.write((sampleRate >> 24) & 0xff);
    rec.write(bytesPerSec & 0xff);
    rec.write((bytesPerSec >> 8) & 0xff);
    rec.write((bytesPerSec >> 16) & 0xff);
    rec.write((bytesPerSec >> 24) & 0xff);
    rec.write(blockAlign & 0xff);
    rec.write((blockAlign >> 8) & 0xff);
    
    rec.write(bitsPerSample & 0xff);
    rec.write((bitsPerSample >> 8) & 0xff);
    
    rec.write("data");
    
    rec.write(dataSize & 0xff);
    rec.write((dataSize >> 8) & 0xff);
    rec.write((dataSize >> 16) & 0xff);
    rec.write((dataSize >> 24) & 0xff);
    /// End write out original WAV header to file ///
    sbi (TIMSK2, OCIE2A); // enable timer interrupt, start grabbing audio
}

 void StopRec() // stop recording process, update WAV header, close file
 { 
    void _write() // Для update WAV header with final filesize/datasize
    {
        rec.write(recByteSaved & 0xff);  rec.write((recByteSaved >> 8) & 0xff);  rec.write((recByteSaved >> 16) & 0xff);  rec.write((recByteSaved >> 24) & 0xff);
    }

    cbi(TIMSK2, OCIE2A); // disable timer interrupt

    /// Start update WAV header with final filesize/datasize ///
    rec.seekSet(4);
    _write();
    rec.seekSet(40);
    _write();
    rec.close();
    /// End update WAV header with final filesize/datasize ///
    digitalWrite(ledStart, LOW); // turn off recording LED
    digitalWrite(ledStop, HIGH); // light stop LED
    recPressed = 0;
}