Диктофон Ардуино с активацией по голосу

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

Всем привет!
Делаю диктофон на ардуино. Микрофон - MAX4466, подключен к любому аналоговому пину (А1), TL431 к AREF и по SPI подключена MicroSD. 
Скетч:
 

/*
This sketch demonstrates recording of standard WAV files that can be played on any device that supports WAVs. The recording
uses a single ended input from any of the analog input pins. Uses AVCC (5V) reference currently.

Requirements:
Class 4 or 6 SD Card
Audio Input Device (Microphone, etc)
Arduino Uno,Nano, Mega, etc.

Steps:
1. Edit pcmConfig.h
    a: On Uno or non-mega boards, #define buffSize 128. May need to increase.
    b: Uncomment #define ENABLE_RECORDING and #define BLOCK_COUNT 10000UL

2. Usage is as below. See https://github.com/TMRh20/TMRpcm/wiki/Advanced-Features#wiki-recording-a... for
   additional informaiton.

Notes: Recording will not work in Multi Mode.
Performance is very dependant on SD write speed, and memory used.
Better performance may be seen using the SdFat library. See included example for usage.
Running the Arduino from a battery or filtered power supply will reduce noise.
*/

#include <SD.h>
#include <SPI.h>
#include <TMRpcm.h>
#include <EEPROM.h>

#define SD_ChipSelectPin 10  //

#define ledStart 2         /* PIN LED Start */
#define ledStop 3          /* PIN LED Stop */
#define MicPin A1          /* PIN LED Stop */

char NameRecord[10];    // Имя нового - записываемого файла на SD-карту
int RecordNumber;       // Номер записи - храним в EEPROM. в диапазоне от 0 до 32767
byte Recording = 0;     // Переменная определяет идет сейчас запись или нет
int RecInterval = 5;    // Минимальная продолжительность записи в секундах
unsigned long TimeInterval = 0; // Переменная для вычисления времени
int MaxAnalogPinValue = 1000;   // Уровень сигнала на аналоговом входе при котором произойдет старт записи
                                //

TMRpcm audio;   // create an object for use in this sketch 

void setup() {
    Serial.begin(9600);
    
    pinMode(ledStart, OUTPUT);
    pinMode(ledStop, OUTPUT);
    //pinMode(14, OUTPUT); //A0
    //pinMode(16, OUTPUT); //A2
    //digitalWrite(14, LOW);
    //digitalWrite(16, LOW);
    audio.speakerPin = 11; //5,6,11 or 46 on Mega, 9 on Uno, Nano, etc
    pinMode(10,OUTPUT);  //Pin pairs: 9,10 Mega: 5-2,6-7,11-12,46-45
    
    if (!SD.begin(SD_ChipSelectPin)) {  
        digitalWrite(ledStop, LOW);
        digitalWrite(ledStart, LOW);
        return;
    }else{
        digitalWrite(ledStop, LOW);
        digitalWrite(ledStart, HIGH);
    }
    analogReference(EXTERNAL);
    // The audio library needs to know which CS pin to use for recording
    audio.CSPin = SD_ChipSelectPin;
    
    RecordNumber = EEPROM.read(0);
    RecInterval = RecInterval * 1000;
}


void loop() {
    if (Recording == 0){
        int AnR = analogRead(MicPin);
        if (AnR >= MaxAnalogPinValue) {

            Serial.println(1);
            StartRec();
        }
    }
    
    if (Recording == 1 && millis() - TimeInterval >= RecInterval) {
        if (analogRead(MicPin) < MaxAnalogPinValue) {
            StopRec();
            Serial.println(2);
        }else{
            TimeInterval = millis()-1;
            Serial.println(3);
        }
    }
}

void StartRec() {                   // begin recording process
    Recording = 1;
    digitalWrite(ledStop, HIGH);
    digitalWrite(ledStart, LOW);
    RecordNumber++;
    if (RecordNumber > 32766)RecordNumber = 0;      // небольшое огриничение в номерации файлов
    EEPROM.write(0, RecordNumber);                  // Сохранение в EEPROM номера последнего аудиофайла
    TimeInterval = millis();           // Запоминаем  millis для отсчета времени записи
    sprintf(NameRecord,"%d.wav", RecordNumber);     // создаем название файла из номера и расширения ".wav"
    audio.startRecording(NameRecord, 16000, MicPin, 0);// Старт записи
}

void StopRec() {                    // stop recording process, close file
    audio.stopRecording(NameRecord);// Стоп записи
    digitalWrite(ledStop, LOW);
    digitalWrite(ledStart, HIGH);
    Recording = 0;
}

Библиотека: https://github.com/TMRh20/TMRpcm   
Пытаюсь сделать активацию записи голосом. То есть если превысить звуковой порог - включится запись. Дольше, через определенный интервал проверять наличие звукового сигнала. Это в LOOP прописано в условии:

if (Recording == 1 && millis() - TimeInterval >= RecInterval) {

И если есть звуковой сигнал - то продолжать запись. Если нет, то тогда стоп записи. Вроде должно все работать, но не работает! Проблема в том что после выполнения команды: 

audio.startRecording(NameRecord, 16000, MicPin, 0);// Старт записи

обращение к аналоговому пину происходит неправильно! Вместо нормального значения возвращается постоянное и неизменное значение - 840. Из-за этого не срабатывает функция "продления" записи, так как не получает необходимого значения... И из-за этого не стартует новая запись после остановки предыдущей записи. Это можно как-то исправить? Есть какие-то идеи?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А автор кода чего говорит?

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

Я лишь взял пример из библиотеки:


/*
This sketch demonstrates recording of standard WAV files that can be played on any device that supports WAVs. The recording
uses a single ended input from any of the analog input pins. Uses AVCC (5V) reference currently.

Requirements:
Class 4 or 6 SD Card
Audio Input Device (Microphone, etc)
Arduino Uno,Nano, Mega, etc.

Steps:
1. Edit pcmConfig.h
    a: On Uno or non-mega boards, #define buffSize 128. May need to increase.
    b: Uncomment #define ENABLE_RECORDING and #define BLOCK_COUNT 10000UL

2. Usage is as below. See <a data-cke-saved-href="https://github.com/TMRh20/TMRpcm/wiki/Advanced-Features#wiki-recording-audio" href="https://github.com/TMRh20/TMRpcm/wiki/Advanced-Features#wiki-recording-audio" rel="nofollow">https://github.com/TMRh20/TMRpcm/wiki/Advanced-Features#wiki-recording-audio</a> for
   additional informaiton.

Notes: Recording will not work in Multi Mode.
Performance is very dependant on SD write speed, and memory used.
Better performance may be seen using the SdFat library. See included example for usage.
Running the Arduino from a battery or filtered power supply will reduce noise.
*/

#include <SD.h>
#include <SPI.h>
#include <TMRpcm.h>

#define SD_ChipSelectPin 53  //example uses hardware SS pin 53 on Mega2560
//#define SD_ChipSelectPin 4  //using digital pin 4 on arduino nano 328, can use other pins

TMRpcm audio;   // create an object for use in this sketch 

void setup() {
  
  audio.speakerPin = 11; //5,6,11 or 46 on Mega, 9 on Uno, Nano, etc
  pinMode(12,OUTPUT);  //Pin pairs: 9,10 Mega: 5-2,6-7,11-12,46-45
  
  Serial.begin(115200);
  
  if (!SD.begin(SD_ChipSelectPin)) {  
    return;
  }else{
    Serial.println("SD OK"); 
  }
  // The audio library needs to know which CS pin to use for recording
  audio.CSPin = SD_ChipSelectPin;
}


void loop() {
  
    if(Serial.available()){                          //Send commands over serial to play
      switch(Serial.read()){
        case 'r': audio.startRecording("test.wav",16000,A0); break;    //Record at 16khz sample rate on pin A0
        case 'R': audio.startRecording("test.wav",16000,A0,1); break;  //Record, but with passthrough to speaker.
        case 't': audio.startRecording("test.wav",16000,A0,2); break;  //Do not record. Output direct to speaker
        							       //Note: If samples are dropped before writing, it
        							       //      will not be heard in passthrough mode
        case 's': audio.stopRecording("test.wav"); break;              //Stop recording
        case 'p': audio.play("test.wav"); break;                       //Play the recording 
        case '=': audio.volume(1); break;                              //Increase volume by 1. Does not affect recording
        case '-': audio.volume(0); break;                              //Decrease volume by 1. Does not affect recording
        case 'S': audio.stopPlayback(); break;                         //Stop all playback
        
      }
    }
}

И пытаюсь его переделать под управление голосом. Изначально запись стартовала и останавливалась через сериал. Почитал что пишут на Гитхабе.. Библиотека задействует таймеры, настраивает АЦП, и даже отключает его. Вот кусок из библиотеки:

void TMRpcm::stopRecording(char *fileName){
      .....
	ADCSRA = 0;
       ADCSRB = 0;
   .....
}

Я решил эту проблему так: сохраняю начальное значения ADCSRA и ADCSRB, и после их восстанавливаю:

void StartRec() {                   // begin recording process
    SaveADC1 = ADCSRA;
    SaveADC2 = ADCSRB;
    Recording = 1;
    digitalWrite(ledStop, HIGH);
    digitalWrite(ledStart, LOW);
    RecordNumber++;
    if (RecordNumber > 32766)RecordNumber = 0;      // небольшое огриничение в номерации файлов
    EEPROM.write(0, RecordNumber);                  // Сохранение в EEPROM номера последнего аудиофайла
    TimeInterval = millis();           // Запоминаем  millis для отсчета времени записи
    sprintf(NameRecord,"%d.wav", RecordNumber);     // создаем название файла из номера и расширения ".wav"
    audio.startRecording(NameRecord, 16000, MicPin, 0);// Старт записи
}

void StopRec() {                    // stop recording process, close file
    audio.stopRecording(NameRecord);// Стоп записи
    digitalWrite(ledStop, LOW);
    digitalWrite(ledStart, HIGH);
    Recording = 0;
    ADCSRA = SaveADC1;
    ADCSRB = SaveADC2;
}

Это решило проблему с неработающим analogRead().

Осталось только придумать способ правильно "понимать" что речь все еще звучит. Простое 

if (analogRead(MicPin) < MaxAnalogPinValue) {

Не срабатывает, так как часто "ловит" не самый высокий фронт звуковой волны, из-за чего "думает" что уже тихо, и нет звука...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

forfrends пишет:

Я лишь взял пример из библиотеки:

Так я и спрашиваю Вас, что говорит автор примера/библиотеки. Вы его спрашивали?

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

Я ему описал все, пока что молчит. По поводу работы АЦП уже подымался вопрос: https://github.com/TMRh20/TMRpcm/issues/101

Но автор не стал это включать в библиотеку. Я это исправление использовал в скетче, по этому Старт/стоп стали работать нормально. Сейчас для меня стоит вопрос как правильно считать значение с аналогового входа, так чтобы понять что есть постоянный звук. Ведь звук - колебание. И если просто делать analogRead(MicPin) то легко можно попасть на уровень, не соответствующий заданному, хотя это все равно будет звуковой волной.

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

Все, проблему решил. Вот полный скетч:

/*
This sketch demonstrates recording of standard WAV files that can be played on any device that supports WAVs. The recording
uses a single ended input from any of the analog input pins. Uses AVCC (5V) reference currently.

Requirements:
Class 4 or 6 SD Card
Audio Input Device (Microphone, etc)
Arduino Uno,Nano, Mega, etc.

Steps:
1. Edit pcmConfig.h
    a: On Uno or non-mega boards, #define buffSize 128. May need to increase.
    b: Uncomment #define ENABLE_RECORDING and #define BLOCK_COUNT 10000UL

2. Usage is as below. See <a href="https://github.com/TMRh20/TMRpcm/wiki/Advanced-Features#wiki-recording-audio" title="https://github.com/TMRh20/TMRpcm/wiki/Advanced-Features#wiki-recording-audio" rel="nofollow">https://github.com/TMRh20/TMRpcm/wiki/Advanced-Features#wiki-recording-a...</a> for
   additional informaiton.

Notes: Recording will not work in Multi Mode.
Performance is very dependant on SD write speed, and memory used.
Better performance may be seen using the SdFat library. See included example for usage.
Running the Arduino from a battery or filtered power supply will reduce noise.
*/

#include <SD.h>
#include <SPI.h>
#include <TMRpcm.h>
#include <EEPROM.h>

#define ENABLE_DEBUG        //Включение вывода отладочной информации через Serial. Только для отладки! Закоментировать эту строку перед заливкой в готовое устройство!

#define SD_ChipSelectPin 10  //

#define ledStart 2         /* PIN LED Start */
#define ledStop 3          /* PIN LED Stop */
#define MicPin A1          // Аналоговый пин, к которму подключен микрофон

char NameRecord[10];        // Имя нового - записываемого файла на SD-карту
int RecordNumber;           // Номер записи - храним в EEPROM. в диапазоне от 0 до 32767
byte Recording = 0;         // Переменная определяет идет сейчас запись или нет
                            //-----------------------------------------------
int RecInterval = 5;        //! Минимальная продолжительность записи в секундах !
                            //-----------------------------------------------
unsigned long TimeInterval = 0; // Переменная для вычисления времени
int MaxAnalogPinValue = 1000;   // Уровень сигнала на аналоговом входе при котором произойдет старт записи
                                // Значение подбирается индивидуально! (100 - 1024)
int SaveADC1 = 0;
int SaveADC2 = 0;

unsigned int sample;
unsigned int signalMax = 0;
unsigned int signalMin = 256;
unsigned int peakToPeak = 0;
#define peakToPeakMinLevel 100   // Уровень срабатывания на звук. Значение подбирается индивидуально!
                                // Максимальное значение - 255

TMRpcm audio;   // create an object for use in this sketch 

void setup() {
    #if defined (ENABLE_DEBUG)
        Serial.begin(9600);
    #endif
    
    pinMode(ledStart, OUTPUT);
    pinMode(ledStop, OUTPUT);
    audio.speakerPin = 11; //5,6,11 or 46 on Mega, 9 on Uno, Nano, etc
    pinMode(10,OUTPUT);  //Pin pairs: 9,10 Mega: 5-2,6-7,11-12,46-45
    
    if (!SD.begin(SD_ChipSelectPin)) {  
        digitalWrite(ledStop, LOW);
        digitalWrite(ledStart, LOW);
        return;
    }else{
        digitalWrite(ledStop, LOW);
        digitalWrite(ledStart, HIGH);
    }
    analogReference(EXTERNAL);
    // The audio library needs to know which CS pin to use for recording
    audio.CSPin = SD_ChipSelectPin;
    
    RecordNumber = EEPROM.read(0);
    RecInterval = RecInterval * 1000;
}


void loop() {
    if (Recording == 0){
        int AnR = analogRead(MicPin);
        #if defined (ENABLE_DEBUG)
            Serial.print("Analog level: ");
            Serial.println(AnR);
        #endif
        if (AnR >= MaxAnalogPinValue) {
            #if defined (ENABLE_DEBUG)
                Serial.print("Start recording! File: ");
                Serial.print(RecordNumber+1);
                Serial.println(".wav");
            #endif
            StartRec();
        }
    }
    
    if (Recording == 1){
        sample = ADCH;
        if (sample < 256)
        {
            if (sample > signalMax)
            {
                signalMax = sample;  // save just the max levels
            }
            else if (sample < signalMin)
            {
                signalMin = sample;  // save just the min levels
            }
        }
        #if defined (ENABLE_DEBUG)
            Serial.print("Max MIC signal: ");
            Serial.println(signalMax);
        #endif
        if (millis() - TimeInterval >= RecInterval) {
            peakToPeak = signalMax - signalMin;
            signalMax = 0;
            signalMin = 256;
            #if defined (ENABLE_DEBUG)
                Serial.print("peakToPeak = ");
                Serial.println(peakToPeak);
            #endif
            if (peakToPeak < peakToPeakMinLevel) {
                analogRead(MicPin);
                StopRec();
                #if defined (ENABLE_DEBUG)
                    Serial.println("Recording stopped");
                #endif
            }else{
                TimeInterval = millis()-1;
                #if defined (ENABLE_DEBUG)
                    Serial.println("Continue recording...");
                #endif
            }
        }
    }
}

void StartRec() {                   // begin recording process
    SaveADC1 = ADCSRA;
    SaveADC2 = ADCSRB;
    Recording = 1;
    digitalWrite(ledStop, HIGH);
    digitalWrite(ledStart, LOW);
    RecordNumber++;
    if (RecordNumber > 32766)RecordNumber = 0;      // небольшое огриничение в номерации файлов
    EEPROM.write(0, RecordNumber);                  // Сохранение в EEPROM номера последнего аудиофайла
    TimeInterval = millis();                        // Запоминаем  millis для отсчета времени записи
    sprintf(NameRecord,"%d.wav", RecordNumber);     // создаем название файла из номера и расширения ".wav"
    audio.startRecording(NameRecord, 16000, MicPin, 0);// Старт записи
}

void StopRec() {                    // stop recording process, close file
    audio.stopRecording(NameRecord);// Стоп записи
    digitalWrite(ledStop, LOW);
    digitalWrite(ledStart, HIGH);
    Recording = 0;
    ADCSRA = SaveADC1;
    ADCSRB = SaveADC2;
}