Глюки записи Ардуино Диктофона

forfrends
Offline
Зарегистрирован: 24.02.2015

Всем привет! Нашел интересный проект Ардуино микрофона: https://www.youtube.com/watch?v=WV5bLv1erxk

Скетч (Много мусора!):

/* Dmitry OSIPOV. http://www.youtube.com/user/d36073?feature=watch
  V.2 Arduino Audio Recorder VAS 20160903.
  ____________.

  Этот видео урок, продолжение предыдущего видео урока.
  Arduino Самый маленький диктофон The smallest Audio Recorder Лайфхак Своими руками
  https://www.youtube.com/watch?v=E9LKPFY3GGI
  _____________.

  Обновление:
  1. Функция Активация по голосу (VAS).
  Запись начинается, как только сигнал, поступающий с микрофона,
  превышает определенный уровень.
  + Установка продолжительности записи.
  2. Шумоподавление.
  EXTERNAL: внешний источник опорного напряжения, подключенный к выводу AREF.
  Желательно отдельный источник питания, к выводу AREF и AVCC,
  или DC/DC преобразователь питания B0505, или через конденсатор.
  _____________.
  SD-карту лучше класс 10 и выше.
  Питание от батареи или аккумулятора.
  Микрофон подключить к pin (A5).
  Внешний источник опорного напряжения, подключаем! к выводу AREF.
  _____________.
  Download - sketch V.2 Arduino Audio Recorder VAS 20160903.
  Библиотеку - SdFat.
  TEST - Аудио файл.
  Фото.
  https://yadi.sk/d/TDgjuyY8uikzP
  _____________.
*/


#include <SdFat.h> /* Библиотека для SD-карты */
#include <EEPROM.h>
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

SdFat sd;
SdFile rec;

const int chipSelect = 10; /* Поменял на 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 ledStart = 2; /* PIN LED Start */
const int ledStop = 3;  /* PIN LED Stop */
int recPressed = 0;
int stopPressed = 0;
unsigned long oldTime = 0L;
unsigned long newTime = 0L;
byte buf00[512]; // buffer array 1
byte buf01[512]; // buffer array 2
byte byte1, byte2, byte3, byte4;
unsigned int bufByteCount;
byte bufWrite;

/*_____________*/

char NameRecord[6]; /* Имя нового - записываемого файла на SD-карту. */
int RecordNumber; /* Номер записи - храним в EEPROM. в диапазоне от 0 до 32767. */

/* Время - интервал - остановки записи - time interval stop recording */
unsigned long TimeIntervalStopRecording;
byte OnOffRecord; /* подготовка - к старту записи */

/*_____________*/

void setup() { // THIS RUNS ONCE

  pinMode(10, OUTPUT);
  pinMode(ledStart, OUTPUT);
  pinMode(ledStop, OUTPUT);


  /*_____________*/

  /* Функция analogReference определяет опорное напряжение относительно которого
    происходят аналоговые измерения в analogRead().

    EXTERNAL: внешний источник опорного напряжения, подключенный к выводу AREF.

    1. Чтобы уменьшить шумы во время записи.
    2. Возможность аппаратного изменения звукового порога, срабатывания авто записи,
    подключив переменный резистор к (выводу-AREF). */

  analogReference(EXTERNAL);

  /*_____________*/

  Setup_timer2();
  Setup_ADC();

  /* Если SD-карта не обнаружена - включаем ledStop и ledStart*/

  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) { // if error, flash LED twice per second, until reset.
    while (1) {
      // digitalWrite(ledStart, !digitalRead(ledStart));
      // delay(500);
      digitalWrite(ledStop, HIGH);
      digitalWrite(ledStart, HIGH);
    }
  }

  digitalWrite(ledStop, HIGH);

  /*
    Читаем значение (int - (2-byte) из EEPROM (ячейки - №0 и №1).
    И записываем в RecordNumber.
  */

  EEPROM.get(0, RecordNumber);
  // delay(50);

  OnOffRecord = EEPROM.read(5); /* Подготовка - к старту записи */
  // delay(50);
}

/*_____________*/


void loop() {

  if (recPressed == 0 && OnOffRecord == 1) {

    /* Для конвертации из int в char / из RecordNumber в NameRecord */
    String str;  //declaring string.
    str = String(RecordNumber); //converting integer into a string.
    str.toCharArray(NameRecord, 6); //passing the value of the string to the character array.


    TimeIntervalStopRecording = millis(); /* Запоминаем  millis */

    StartRec(); /* launch StartRec method - Включаем запись */
  }

  /*_____________*/


  /* Останавливаем запись - по таймеру */

  /* Продолжительность записи 10 секунд - можно изменять */
  if (OnOffRecord == 1 && millis() - TimeIntervalStopRecording >= 10000) {

    StopRec(); /* launch StopRec method - Останавливаем запись. */

    /* Блокируем повторный старт записи. */
    EEPROM.write(5, 0);
    OnOffRecord = 0;
  }

  /*_____________*/


  if (recByteCount % 1024 == 512 && recPressed == 1) {
    rec.write(buf00, 512);  // save buf01 to card
    recByteSaved += 512;
  }
  if (recByteCount % 1024 == 0 && recPressed == 1) {
    rec.write(buf01, 512);  // save buf02 to card
    recByteSaved += 512;
  }

  /*_____________*/



  /* Подготовка к старту записи "Активация по голосу (VAS)."
    Запись начнётся, как только сигнал, поступающий с микрофона,
    превышает определенный уровень.
  */

  /* Если уровень микрофона превысит порог 1023.
    порог - можно изменять! программно,
    И аппаратно -
    с помощью переменного резистора - "делитель напряжения"
    подключённого к выводу AREF, изменяем опорное напряжение "analogRead"
  */
  if (recPressed == 0 && analogRead(A5) == 1023) {

    EEPROM.write(5, 1); /* подготовка - к старту записи */

    if (RecordNumber == 32767)RecordNumber = 0;

    /*
      Записываем значение (int - (2-byte) в EEPROM (ячейки - №0 и №1).
      Переменная RecordNumber - увеличенная на 1.
    */

    EEPROM.put(0, RecordNumber + 1);
    // delay(100);


    /* После окончания записи, в памяти контроллера остаются данные.
      И за этого следующая запись бывает качеством хуже.
      Поэтому (Делаем программную перезагрузку) перед стартом новой записи. */

    ((void (*)())0x0000)(); /* Делаем программную перезагрузку - Arduino */
  }
}



/*_____________*/



void StartRec() { // begin recording process

  digitalWrite(ledStart, HIGH);
  digitalWrite(ledStop, LOW);
  recByteCount = 0;
  recByteSaved = 0;
  recPressed = 1; // recording button has been pressed
  stopPressed = 0;
  writeWavHeader();
  sbi (TIMSK2, OCIE2A); // enable timer interrupt, start grabbing audio

}

void StopRec() { // stop recording process, update WAV header, close file

  cbi (TIMSK2, OCIE2A); // disable timer interrupt
  writeOutHeader();
  digitalWrite(ledStart, LOW); // turn off recording LED
  digitalWrite(ledStop, HIGH); // light stop LED
  recPressed = 0;

}

void writeOutHeader() { // update WAV header with final filesize/datasize

  rec.seekSet(4);
  byte1 = recByteSaved & 0xff;
  byte2 = (recByteSaved >> 8) & 0xff;
  byte3 = (recByteSaved >> 16) & 0xff;
  byte4 = (recByteSaved >> 24) & 0xff;
  rec.write(byte1);  rec.write(byte2);  rec.write(byte3);  rec.write(byte4);
  rec.seekSet(40);
  rec.write(byte1);  rec.write(byte2);  rec.write(byte3);  rec.write(byte4);
  rec.close();

}

void writeWavHeader() { // write out original WAV header to file

  recByteSaved = 0;
  // rec.open("rec00000.wav", O_CREAT | O_TRUNC | O_RDWR);
  /* Имя нового - записываемого файла на SD-карту. */
  rec.open(NameRecord, O_CREAT | O_TRUNC | O_RDWR);

  rec.write("RIFF");
  byte1 = fileSize & 0xff;
  byte2 = (fileSize >> 8) & 0xff;
  byte3 = (fileSize >> 16) & 0xff;
  byte4 = (fileSize >> 24) & 0xff;
  rec.write(byte1);  rec.write(byte2);  rec.write(byte3);  rec.write(byte4);
  rec.write("WAVE");
  rec.write("fmt ");
  byte1 = waveChunk & 0xff;
  byte2 = (waveChunk >> 8) & 0xff;
  byte3 = (waveChunk >> 16) & 0xff;
  byte4 = (waveChunk >> 24) & 0xff;
  rec.write(byte1);  rec.write(byte2);  rec.write(byte3);  rec.write(byte4);
  byte1 = waveType & 0xff;
  byte2 = (waveType >> 8) & 0xff;
  rec.write(byte1);  rec.write(byte2);
  byte1 = numChannels & 0xff;
  byte2 = (numChannels >> 8) & 0xff;
  rec.write(byte1);  rec.write(byte2);
  byte1 = sampleRate & 0xff;
  byte2 = (sampleRate >> 8) & 0xff;
  byte3 = (sampleRate >> 16) & 0xff;
  byte4 = (sampleRate >> 24) & 0xff;
  rec.write(byte1);  rec.write(byte2);  rec.write(byte3);  rec.write(byte4);
  byte1 = bytesPerSec & 0xff;
  byte2 = (bytesPerSec >> 8) & 0xff;
  byte3 = (bytesPerSec >> 16) & 0xff;
  byte4 = (bytesPerSec >> 24) & 0xff;
  rec.write(byte1);  rec.write(byte2);  rec.write(byte3);  rec.write(byte4);
  byte1 = blockAlign & 0xff;
  byte2 = (blockAlign >> 8) & 0xff;
  rec.write(byte1);  rec.write(byte2);
  byte1 = bitsPerSample & 0xff;
  byte2 = (bitsPerSample >> 8) & 0xff;
  rec.write(byte1);  rec.write(byte2);
  rec.write("data");
  byte1 = dataSize & 0xff;
  byte2 = (dataSize >> 8) & 0xff;
  byte3 = (dataSize >> 16) & 0xff;
  byte4 = (dataSize >> 24) & 0xff;
  rec.write(byte1);  rec.write(byte2);  rec.write(byte3);  rec.write(byte4);

}

void Setup_timer2() {

  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

}

void Setup_ADC() {

  ADMUX = 0x65; // set ADC to read pin A5, ADLAR to 1 (left adjust)
  cbi(ADCSRA, ADPS2); // set prescaler to 8 / ADC clock = 2MHz
  sbi(ADCSRA, ADPS1);
  sbi(ADCSRA, ADPS0);
}

ISR(TIMER2_COMPA_vect) {

  sbi(ADCSRA, ADSC); // start ADC sample
  while (bit_is_set(ADCSRA, ADSC)); // wait until ADSC bit goes low = new sample ready
  recByteCount++; // increment sample counter
  bufByteCount++;
  if (bufByteCount == 512 && bufWrite == 0) {
    bufByteCount = 0;
    bufWrite = 1;
  } else if (bufByteCount == 512 & bufWrite == 1) {
    bufByteCount = 0;
    bufWrite = 0;
  }

  if (bufWrite == 0) {
    buf00[bufByteCount] = ADCH;
  }
  if (bufWrite == 1) {
    buf01[bufByteCount] = ADCH;
  }


  //  if (recByteCount % 1024 < 512) { // determine which buffer to store sample into
  //    buf01[recByteCount % 512] = ADCH;
  //  } else {
  //    buf02[recByteCount % 512] = ADCH;
  //  }
}

Собрал, вроде работает, но есть одна странность: если питать ардуино от компьютера, то почти нет посторонних шумов. А если запитать от аккумулятора, то запись получается просто ужасной! Вот ссылка на Гугл диск с примерами записи: https://drive.google.com/open?id=1K6BrjRhTSzYjrvIRmf6IJEgVOv9GfAN5

Файл 69.wav - запись с питанием от компьютера. Усиление микрофона стоит почти на минимуме. Файл 101.wav - запись от аккумулятора (4.1 вольт). Схема устройства крайне простая: MicroSD подключена по SPI, к пину А5 подключен выход микрофонного усилителя MAX4466, светодиоды к пинам 2 и 3, и TL431 в качестве опорника. Пробовал собирать на Arduino UNO и на PRO MINI - в обоих случаях результат одинаков - питается от компьютера - запись по-лучше, а если от аккумулятора - запись плохая, много шумов и "рипит"... Аккумуляторы пробовал разные. Пробовал разные флешки, как "НоНейм" - так и качественные с 10-й скоростью.

Не могу понять почему идут шумы при работе от аккумулятора... по идее должно быть наоборот...

Может у вас есть идеи?

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Идеи есть - не хватает питания. Запитайте от ардуинку от БП. Если все хорошо - то точно в питании.

forfrends
Offline
Зарегистрирован: 24.02.2015
Покопался немного и нашел источник шума. Пульсации (шум) в основном записываются от работающей Ардуино. Стоит включить запись на флешку так сразу же микрофон начинает сильно шуметь.
Посторонние шумы можно свести к минимуму если питать микрофон и Ардуино от отдельных источников питания. Только что это проверил. Но для моего устройства это не вариант. Нужно питать все от одного источника питания (аккумулятора).
Вот теперь стал вопрос: как правильно развязать микрофон и Ардуино?
forfrends
Offline
Зарегистрирован: 24.02.2015

В Общем, решается все это диодом и конденсатором на микрофоне.