Compare commits

..

1 Commits
main ... stable

Author SHA1 Message Date
c9dc770880 Changed config and endpoint 2024-05-16 13:11:39 +03:00
26 changed files with 174 additions and 854 deletions

1
.gitignore vendored
View File

@ -3,4 +3,3 @@
.vscode/c_cpp_properties.json .vscode/c_cpp_properties.json
.vscode/launch.json .vscode/launch.json
.vscode/ipch .vscode/ipch
test/build/*

View File

@ -21,4 +21,3 @@ lib_deps =
arduino-libraries/Ethernet@^2.0.2 arduino-libraries/Ethernet@^2.0.2
paulstoffregen/Time@^1.6.1 paulstoffregen/Time@^1.6.1
bblanchon/ArduinoJson@^6.21.3 bblanchon/ArduinoJson@^6.21.3
google/googletest@^1.12.1

View File

@ -27,4 +27,3 @@
// make it configurable with some panel // make it configurable with some panel
//#define NOISE_THRESHOLD 3000000 //#define NOISE_THRESHOLD 3000000
#define NOISE_THRESHOLD 10000000 #define NOISE_THRESHOLD 10000000
//#define NOISE_THRESHOLD 20000000

View File

@ -1,125 +0,0 @@
#include "domain/AidStrategy.h"
#include "utils/Time.h"
#include "config/config.h"
void AidStrategy::setup() {
_task1_mutex = xSemaphoreCreateMutex();
_task2_mutex = xSemaphoreCreateMutex();
_http_mutex = xSemaphoreCreateMutex();
}
void AidStrategy::process() {
xTaskCreatePinnedToCore(processTask, "ProcessTask1", 4096, this, 1, NULL, 0);
//xTaskCreatePinnedToCore(processTask, "ProcessTask2", 4096, this, 1, NULL, 1);
}
void AidStrategy::processTask(void* parameters) {
AidStrategy* aidStrategy = static_cast<AidStrategy*>(parameters);
Audio* audioSource =
(xTaskGetAffinity(NULL) == 0) ? aidStrategy->_audio : aidStrategy->_audio1;
aidStrategy->_writeToPrecedingBuffer(audioSource);
if (aidStrategy->_isNoiseDetected(audioSource)) {
aidStrategy->_recordAudio(audioSource);
}
vTaskDelete(NULL);
}
void AidStrategy::_writeToPrecedingBuffer(Audio* audioSource) {
if (!_isWritingToPrecedingBuffer && !_recordingAudio) {
_isWritingToPrecedingBuffer = true;
audioSource->writeToPrecedingBuffer();
_isWritingToPrecedingBuffer = false;
}
}
bool AidStrategy::_isNoiseDetected(Audio* audioSource) {
return audioSource->isNoiseDetected();
}
void AidStrategy::_recordAudio(Audio* audioSource) {
if (_recordingAudio)
return;
Serial.println("!!!");
bool isReady = false;
_recordingAudio = true;
if (xSemaphoreTake(_http_mutex, portMAX_DELAY)) {
isReady = _ready();
xSemaphoreGive(_http_mutex);
}
if (isReady) {
Serial.println("========================================");
Serial.println("| RECORDING...");
audioSource->record();
Serial.println("| Recording finished");
}
if (xSemaphoreTake(_http_mutex, portMAX_DELAY)) {
if (_http->connect(RAT_IP, RAT_PORT)) {
_sendAudio(audioSource);
_http->stop();
} else {
Serial.println("| Connection failed");
}
xSemaphoreGive(_http_mutex);
}
_recordingAudio = false;
}
String AidStrategy::_ready() {
return _http->send("POST", "/aid/ready", RAT_IP, RAT_PORT, "", "");
}
void AidStrategy::_sendAudio(Audio* audioSource) {
String payloadStart = _formPayloadStart(_audio->getStartMicros());
String payloadEnd = _formPayloadEnd();
int audioLength = _audio->getSize();
int jsonLength = payloadStart.length() + audioLength + payloadEnd.length();
_http->printHeader("POST", "/aid/save", RAT_IP, "application/json");
_http->printContentLength(jsonLength);
this->_printContent(payloadStart, payloadEnd, _http->getClient());
}
void AidStrategy::_printContent(String payloadStart, String payloadEnd, Stream* stream) {
stream->print(payloadStart);
_audio->print(stream);
stream->print(payloadEnd);
_logContent(payloadStart, payloadEnd);
}
void AidStrategy::_logContent(String payloadStart, String payloadEnd) {
Serial.print("| ");
Serial.print(payloadStart);
Serial.print("...");
Serial.print(payloadEnd);
}
String AidStrategy::_formPayloadStart(unsigned long startMicros) {
String payloadStart = "";
payloadStart += "{\"audio\":{\"micros\":";
payloadStart += String(_audio->getStartMicros() - _timeModule.getInitialMicros());
payloadStart += ",\"initialTime\":";
payloadStart += String(_timeModule.getInitialTime());
payloadStart += ",\"content\":\"";
return payloadStart;
}
String AidStrategy::_formPayloadEnd() {
return "\"}}\r\n\r\n";
}

View File

@ -1,104 +0,0 @@
#include "domain/AidStrategy.h"
#include "utils/Time.h"
#include "config/config.h"
void AidStrategy::setup() {}
void AidStrategy::process() {
_writeToPrecedingBuffer(_audio);
if (_isNoiseDetected(_audio)) {
_recordAudio(_audio);
}
}
void AidStrategy::_writeToPrecedingBuffer(Audio* audioSource) {
if (!_isWritingToPrecedingBuffer && !_recordingAudio) {
_isWritingToPrecedingBuffer = true;
audioSource->writeToPrecedingBuffer();
_isWritingToPrecedingBuffer = false;
}
}
bool AidStrategy::_isNoiseDetected(Audio* audioSource) {
return audioSource->isNoiseDetected();
}
void AidStrategy::_recordAudio(Audio* audioSource) {
if (_recordingAudio)
return;
Serial.println("!!!");
bool isReady = false;
_recordingAudio = true;
isReady = _ready();
if (!isReady)
return;
if (_http->connect(RAT_IP, RAT_PORT)) {
Serial.println("========================================");
Serial.println("| RECORDING...");
audioSource->record();
Serial.println("| Recording finished");
_sendAudio(audioSource);
_http->stop();
} else {
Serial.println("| Connection failed");
}
_recordingAudio = false;
}
String AidStrategy::_ready() {
return _http->send("POST", "/aid/ready", RAT_IP, RAT_PORT, "", "");
}
void AidStrategy::_sendAudio(Audio* audioSource) {
String payloadStart = _formPayloadStart(_audio->getStartMicros());
String payloadEnd = _formPayloadEnd();
int audioLength = _audio->getSize();
int jsonLength = payloadStart.length() + audioLength + payloadEnd.length();
_http->printHeader("POST", "/aid/save", RAT_IP, "application/json");
_http->printContentLength(jsonLength);
this->_printContent(payloadStart, payloadEnd, _http->getClient());
}
void AidStrategy::_printContent(String payloadStart, String payloadEnd, Stream* stream) {
stream->print(payloadStart);
_audio->print(stream);
stream->print(payloadEnd);
_logContent(payloadStart, payloadEnd);
}
void AidStrategy::_logContent(String payloadStart, String payloadEnd) {
Serial.print("| ");
Serial.print(payloadStart);
Serial.print("...");
Serial.print(payloadEnd);
}
String AidStrategy::_formPayloadStart(unsigned long startMicros) {
String payloadStart = "";
payloadStart += "{\"audio\":{\"micros\":";
payloadStart += String(_audio->getStartMicros() - _timeModule.getInitialMicros());
payloadStart += ",\"initialTime\":";
payloadStart += String(_timeModule.getInitialTime());
payloadStart += ",\"content\":\"";
return payloadStart;
}
String AidStrategy::_formPayloadEnd() {
return "\"}}\r\n\r\n";
}

View File

@ -1,37 +0,0 @@
#pragma once
#include "domain/BaseStrategy.h"
#include "utils/Time.h"
class AidStrategy : public BaseStrategy {
private:
Time& _timeModule = Time::getInstance();
SemaphoreHandle_t _task1_mutex;
SemaphoreHandle_t _task2_mutex;
SemaphoreHandle_t _http_mutex;
bool _isWritingToPrecedingBuffer = false;
bool _recordingAudio = false;
void _recordAudio(Audio* audioSource);
void _writeToPrecedingBuffer(Audio* audioSource);
bool _isNoiseDetected(Audio* audioSource);
String _ready();
void _sendAudio(Audio* audioSource);
void _printContent(String payloadStart, String payloadEnd, Stream* stream);
void _logContent(String payloadStart, String payloadEnd);
String _formPayloadStart(unsigned long startMicros);
String _formPayloadEnd();
static void processTask(void* parameters);
public:
AidStrategy(MicType micType, Http* http) : BaseStrategy(micType, http) {}
~AidStrategy() {}
void setup();
void process();
};

View File

@ -1,37 +0,0 @@
#pragma once
#include "infra/Audio.h"
#include "infra/Http.h"
class BaseStrategy {
protected:
Audio* _audio;
Audio* _audio1;
Http* _http;
public:
BaseStrategy(MicType micType, Http* http) {
Serial.print("Free heap: ");
Serial.println(esp_get_minimum_free_heap_size());
_audio = new Audio(micType, I2S_NUM_0);
Serial.print("Free heap: ");
Serial.println(esp_get_minimum_free_heap_size());
_audio1 = new Audio(micType, I2S_NUM_1);
Serial.print("Free heap: ");
Serial.println(esp_get_minimum_free_heap_size());
_http = http;
}
~BaseStrategy() {
delete _audio;
delete _audio1;
}
virtual void setup() = 0;
virtual void process() = 0;
};

View File

@ -1,30 +0,0 @@
#include "domain/Controller.h"
Controller Controller::_instance;
Controller::Controller() : _strategy(nullptr) {}
void Controller::setStrategy(BaseStrategy* newStrategy) {
if (_strategy) {
delete _strategy;
}
_strategy = newStrategy;
if (_strategy) {
_strategy->setup();
}
}
Controller::~Controller() {
if (_strategy) {
delete _strategy;
}
}
Controller& Controller::getInstance() {
return _instance;
}
void Controller::process() {
_strategy->process();
}

View File

@ -1,17 +0,0 @@
#include "domain/BaseStrategy.h"
class Controller {
private:
static Controller _instance;
BaseStrategy* _strategy;
Controller();
Controller(Controller const&) = delete;
void operator=(Controller const&) = delete;
~Controller();
public:
static Controller& getInstance();
void setStrategy(BaseStrategy* strategy);
void process();
};

83
src/domain/Recorder.cpp Normal file
View File

@ -0,0 +1,83 @@
#include "domain/Recorder.h"
#include "config/config.h"
#include "utils/Time.h"
Time& timeModule = Time::getInstance();
Recorder::Recorder(MicType micType, Http* http) {
_audio = new Audio(micType);
_http = http;
}
Recorder::~Recorder() {
delete _audio;
}
void Recorder::recordAudio() {
_ready();
if (_http->connect(RAT_IP, RAT_PORT)) {
Serial.println("========================================");
Serial.println("| RECORDING...");
_audio->record();
Serial.println("| Recording finished");
_transcribe();
} else {
Serial.println("| Connection failed");
}
}
void Recorder::_ready() {
_http->send("POST", "/ready", RAT_IP, RAT_PORT, "", "");
}
void Recorder::_transcribe() {
String payloadStart = _formPayloadStart();
String payloadEnd = _formPayloadEnd();
int audioLength = _audio->getSize();
int jsonLength = payloadStart.length() + audioLength + payloadEnd.length();
_http->printHeader("POST", "/save_to_file", RAT_IP, "application/json");
_http->printContentLength(jsonLength);
this->_printContent(payloadStart, payloadEnd, _http->getClient());
}
void Recorder::_printContent(String payloadStart, String payloadEnd, Stream* stream) {
stream->print(payloadStart);
_audio->print(stream);
stream->print(payloadEnd);
_logContent(payloadStart, payloadEnd);
}
void Recorder::_logContent(String payloadStart, String payloadEnd) {
Serial.print("| ");
Serial.print(payloadStart);
Serial.print("...");
Serial.print(payloadEnd);
}
String Recorder::_formPayloadStart() {
String payloadStart = "";
payloadStart += "{\"audio\":{\"micros\":";
payloadStart += String(_audio->getStartMicros() - timeModule.getInitialMicros());
payloadStart += ",\"initialTime\":";
payloadStart += String(timeModule.getInitialTime());
payloadStart += ",\"content\":\"";
return payloadStart;
}
String Recorder::_formPayloadEnd() {
return "\"}}\r\n\r\n";
}
bool Recorder::isNoiseDetected(int threshold) {
return _audio->isNoiseDetected(threshold);
}

25
src/domain/Recorder.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "infra/Audio.h"
#include "infra/Http.h"
class Recorder {
private:
Audio* _audio;
Http* _http;
void _ready();
void _transcribe();
void _printContent(String payloadStart, String payloadEnd, Stream* stream);
void _logContent(String payloadStart, String payloadEnd);
void _printAudio();
String _formPayloadStart();
String _formPayloadEnd();
public:
Recorder(MicType micType, Http* http);
~Recorder();
void recordAudio();
bool isNoiseDetected(int threshold);
};

View File

@ -1,13 +0,0 @@
#include "domain/RecorderStrategy.h"
#include "config/config.h"
void RecorderStrategy::setup() {}
void RecorderStrategy::process() {
}
void RecorderStrategy::_check() {
_http->send("POST", "/recorder/check", RAT_IP, RAT_PORT, "", "");
}

View File

@ -1,16 +0,0 @@
#pragma once
#include "domain/BaseStrategy.h"
#include "utils/Time.h"
class RecorderStrategy : public BaseStrategy {
private:
Time& _timeModule = Time::getInstance();
void _check();
public:
void setup();
void process();
};

View File

@ -1,26 +1,17 @@
#include "infra/Audio.h" #include "infra/Audio.h"
#include <base64.h> #include <base64.h>
#include "config/config.h"
#define PRECEDING_BUFFER_SIZE 4 Audio::Audio(MicType micType) {
Audio::Audio(MicType micType, i2s_port_t i2sNum) {
_wavData = new char*[_wavDataSize/_dividedWavDataSize]; _wavData = new char*[_wavDataSize/_dividedWavDataSize];
for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) _wavData[i] = new char[_dividedWavDataSize];
_wavData[i] = new char[_dividedWavDataSize]; i2s = new I2S(micType);
_precedingWavBuffer = new CircularBuffer<char*>(_precedingWavPartsAmount);
i2s = new I2S(micType, i2sNum);
} }
Audio::~Audio() { Audio::~Audio() {
for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) delete[] _wavData[i]; for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) delete[] _wavData[i];
delete[] _wavData; delete[] _wavData;
delete i2s; delete i2s;
emptyPrecedingBuffer();
} }
void Audio::_createWavHeader(byte* header, int waveDataSize){ void Audio::_createWavHeader(byte* header, int waveDataSize){
@ -47,7 +38,7 @@ void Audio::_createWavHeader(byte* header, int waveDataSize){
header[19] = 0x00; header[19] = 0x00;
header[20] = 0x01; // linear PCM header[20] = 0x01; // linear PCM
header[21] = 0x00; header[21] = 0x00;
header[22] = 0x02; // stereo header[22] = 0x01; // monoral
header[23] = 0x00; header[23] = 0x00;
header[24] = 0x80; // sampling rate 16000 header[24] = 0x80; // sampling rate 16000
header[25] = 0x3E; header[25] = 0x3E;
@ -72,78 +63,30 @@ void Audio::_createWavHeader(byte* header, int waveDataSize){
} }
char** Audio::record() { char** Audio::record() {
_noiseDetectedInPrecedingBuffer = false;
_createWavHeader(_paddedHeader, _wavDataSize); _createWavHeader(_paddedHeader, _wavDataSize);
_startMicros = micros();
_readFromPrecedingBuffer(_wavData);
_recordToBuffer(_wavData + _precedingWavPartsAmount, _wavDataSize - _precedingWavDataSize);
return _wavData;
}
void Audio::_recordToBuffer(char** wavBuffer, int waveDataSize) {
int bitBitPerSample = i2s->GetBitPerSample(); int bitBitPerSample = i2s->GetBitPerSample();
_startMicros = micros();
if (bitBitPerSample == 16) { if (bitBitPerSample == 16) {
for (int j = 0; j < waveDataSize / _dividedWavDataSize; ++j) { for (int j = 0; j < _wavDataSize/_dividedWavDataSize; ++j) {
i2s->Read(_i2sBuffer, _i2sBufferSize/2); i2s->Read(_i2sBuffer, _i2sBufferSize/2);
_convertBufferToWav(wavBuffer[j]); for (int i = 0; i < _i2sBufferSize/8; ++i) {
_wavData[j][2*i] = _i2sBuffer[4*i + 2];
_wavData[j][2*i + 1] = _i2sBuffer[4*i + 3];
}
} }
} }
else if (bitBitPerSample == 32) { else if (bitBitPerSample == 32) {
for (int j = 0; j < waveDataSize / _dividedWavDataSize; ++j) { for (int j = 0; j < _wavDataSize/_dividedWavDataSize; ++j) {
i2s->Read(_i2sBuffer, _i2sBufferSize); i2s->Read(_i2sBuffer, _i2sBufferSize);
for (int i = 0; i < _i2sBufferSize/8; ++i) { for (int i = 0; i < _i2sBufferSize/8; ++i) {
wavBuffer[j][2*i] = _i2sBuffer[8*i + 2] << 4; // TODO: check << 4 in 16bit _wavData[j][2*i] = _i2sBuffer[8*i + 2] << 4;
wavBuffer[j][2*i + 1] = _i2sBuffer[8*i + 3] << 4; _wavData[j][2*i + 1] = _i2sBuffer[8*i + 3] << 4;
} }
} }
} }
} return _wavData;
void Audio::_readFromPrecedingBuffer(char** wavBuffer) {
for (int i = 0; i < _precedingWavPartsAmount; i++) {
if (!_precedingWavBuffer->isEmpty()) {
char* tmp;
_precedingWavBuffer->popFront(tmp);
delete wavBuffer[i];
wavBuffer[i] = tmp;
}
}
emptyPrecedingBuffer();
}
void Audio::emptyPrecedingBuffer() {
while (!_precedingWavBuffer->isEmpty()) {
char* tmp;
_precedingWavBuffer->popFront(tmp);
delete tmp;
}
}
void Audio::writeToPrecedingBuffer() {
if (_precedingWavBuffer->isFull()) {
char* tmp;
_precedingWavBuffer->popFront(tmp);
delete tmp;
}
i2s->Read(_i2sBuffer, _i2sBufferSize/2);
_noiseDetectedInPrecedingBuffer = _isNoiseDetectedInPrecedingBuffer();
char* _partialWavBuffer = new char[_dividedWavDataSize];
_convertBufferToWav(_partialWavBuffer);
CircularBufferErrorCode err = _precedingWavBuffer->pushBack(_partialWavBuffer);
}
void Audio::_convertBufferToWav(char* partialWavBuffer) {
for (int i = 0; i < _i2sBufferSize/2; ++i) {
partialWavBuffer[i] = _i2sBuffer[i];
}
} }
int Audio::getSize() { int Audio::getSize() {
@ -151,30 +94,12 @@ int Audio::getSize() {
} }
String Audio::print(Stream* stream) { String Audio::print(Stream* stream) {
return printHeader(stream) + printContent(stream);
}
String Audio::printHeader(Stream* stream) {
String enc = base64::encode(_paddedHeader, sizeof(_paddedHeader)); String enc = base64::encode(_paddedHeader, sizeof(_paddedHeader));
enc.replace("\n", ""); enc.replace("\n", "");
stream->print(enc); stream->print(enc);
return enc;
}
String Audio::printContent(Stream* stream) {
String enc;
Serial.print("Free heap: ");
Serial.println(esp_get_minimum_free_heap_size());
for (int j = 0; j < _wavDataSize / _dividedWavDataSize; ++j) { for (int j = 0; j < _wavDataSize / _dividedWavDataSize; ++j) {
enc = base64::encode((byte*)_wavData[j], _dividedWavDataSize/2); enc = base64::encode((byte*)_wavData[j], _dividedWavDataSize);
enc.replace("\n", "");
stream->print(enc);
enc = base64::encode((byte*)_wavData[j] + _dividedWavDataSize/2,
_dividedWavDataSize/2);
enc.replace("\n", ""); enc.replace("\n", "");
stream->print(enc); stream->print(enc);
} }
@ -186,15 +111,13 @@ unsigned long Audio::getStartMicros() {
return _startMicros; return _startMicros;
} }
bool Audio::_isNoiseDetectedInPrecedingBuffer() { bool Audio::isNoiseDetected(int threshold) {
if (*(int*)_i2sBuffer > NOISE_THRESHOLD || *(int*)_i2sBuffer < -NOISE_THRESHOLD) { i2s->Read(_i2sBuffer2, _i2sBufferSize/2);
Serial.println("Noise detected"); if (*(int*)_i2sBuffer2 > threshold || *(int*)_i2sBuffer2 < -threshold) {
Serial.print(*(int*)_i2sBuffer2);
Serial.print(" ");
return true; return true;
} else { } else {
return false; return false;
} }
} }
bool Audio::isNoiseDetected() {
return _noiseDetectedInPrecedingBuffer;
}

View File

@ -3,52 +3,34 @@
#include <Arduino.h> #include <Arduino.h>
#include "libs/CircularBuffer.h"
#include "infra/I2S.h" #include "infra/I2S.h"
// 16bit, monoral, 16000Hz, linear PCM // 16bit, monoral, 16000Hz, linear PCM
class Audio { class Audio {
private: private:
I2S* i2s; I2S* i2s;
static const int _headerSize = 44; static const int _headerSize = 44;
static const int _i2sBufferSize = 6048/2; static const int _i2sBufferSize = 6000;
char _i2sBuffer[_i2sBufferSize/2]; char _i2sBuffer[_i2sBufferSize];
char _i2sBuffer2[_i2sBufferSize];
static const int _wavDataSize = 90720/2; // It must be multiple of _dividedWavDataSize. Recording time is about 1.9 second. static const int _wavDataSize = 90000; // It must be multiple of _dividedWavDataSize. Recording time is about 1.9 second.
static const int _dividedWavDataSize = _i2sBufferSize/2; static const int _dividedWavDataSize = _i2sBufferSize/4;
char** _wavData; // It's divided. Because large continuous memory area can't be allocated in esp32. char** _wavData; // It's divided. Because large continuous memory area can't be allocated in esp32.
static const int _precedingWavPartsAmount = (_wavDataSize/_dividedWavDataSize)/5;
static const int _precedingWavDataSize =
_precedingWavPartsAmount*_dividedWavDataSize;
char** _precedingWavData;
CircularBuffer<char*>* _precedingWavBuffer;
bool _precedingBufferFull = false;
bool _noiseDetectedInPrecedingBuffer = false;
byte _paddedHeader[_headerSize + 4] = {0}; // The size must be multiple of 3 for Base64 encoding. Additional byte size must be even because wave data is 16bit. byte _paddedHeader[_headerSize + 4] = {0}; // The size must be multiple of 3 for Base64 encoding. Additional byte size must be even because wave data is 16bit.
unsigned long _startMicros = 0; unsigned long _startMicros = 0;
void _recordToBuffer(char** wavBuffer, int waveDataSize);
void _readFromPrecedingBuffer(char** wavBuffer);
void _convertBufferToWav(char* partialWavBuffer);
void _createWavHeader(byte* header, int waveDataSize); void _createWavHeader(byte* header, int waveDataSize);
bool _isNoiseDetectedInPrecedingBuffer();
public: public:
Audio(MicType micType, i2s_port_t i2sNum=I2S_NUM_0); Audio(MicType micType);
~Audio(); ~Audio();
char** record(); char** record();
int getSize(); int getSize();
String print(Stream* stream); String print(Stream* stream);
String printHeader(Stream* stream);
String printContent(Stream* stream);
unsigned long getStartMicros(); unsigned long getStartMicros();
void emptyPrecedingBuffer(); bool isNoiseDetected(int threshold);
void writeToPrecedingBuffer();
bool isNoiseDetected();
}; };
#endif // _AUDIO_H #endif // _AUDIO_H

View File

@ -62,8 +62,6 @@ void Http::printContentLength(int contentLength) {
_client->print("Content-Length: "); _client->print("Content-Length: ");
_client->println(contentLength); _client->println(contentLength);
_client->println(); _client->println();
_logContentLength(contentLength);
} }
String Http::readResponse() { String Http::readResponse() {
@ -135,8 +133,3 @@ void Http::_logPayload(String payload) {
Serial.println(payload); Serial.println(payload);
} }
} }
void Http::_logContentLength(int contentLength) {
Serial.print("| Content-Length: ");
Serial.println(contentLength);
}

View File

@ -11,7 +11,6 @@ private:
void _logHeader(String method, String endpoint, String host, String contentType); void _logHeader(String method, String endpoint, String host, String contentType);
void _logPayload(String payload); void _logPayload(String payload);
void _logConnected(String host, int port); void _logConnected(String host, int port);
void _logContentLength(int contentLength);
public: public:
Http(Eth* eth); Http(Eth* eth);

View File

@ -1,24 +1,10 @@
#include "infra/I2S.h" #include "infra/I2S.h"
#include "config/config.h"
#define SAMPLE_RATE (16000) #define SAMPLE_RATE (16000)
#define PIN_I2S_BCLK (GPIO_NUM_26)
// Define pins for I2S_NUM_0 #define PIN_I2S_LRC (GPIO_NUM_25)
#define PIN_I2S_0_BCLK (GPIO_NUM_26) #define PIN_I2S_DIN (GPIO_NUM_3)
#define PIN_I2S_0_LRC (GPIO_NUM_25) #define PIN_I2S_DOUT (GPIO_NUM_22)
#define PIN_I2S_0_DIN (GPIO_NUM_3) #define PIN_I2S_MCK (GPIO_NUM_0)
#define PIN_I2S_0_DOUT (GPIO_NUM_22)
#define PIN_I2S_0_MCK (GPIO_NUM_0)
// Define pins for I2S_NUM_1
#define PIN_I2S_1_BCLK (GPIO_NUM_32)
#define PIN_I2S_1_LRC (GPIO_NUM_33)
#define PIN_I2S_1_DIN (GPIO_NUM_35)
#define PIN_I2S_1_DOUT (GPIO_NUM_0)
#define PIN_I2S_1_MCK (GPIO_NUM_0)
SemaphoreHandle_t dmaInterruptSemaphore;
static intr_handle_t dmaInterruptRetHandle;
// This I2S specification : // This I2S specification :
// - LRC high is channel 2 (right). // - LRC high is channel 2 (right).
@ -27,7 +13,7 @@ static intr_handle_t dmaInterruptRetHandle;
// - Data bits are MSB first. // - Data bits are MSB first.
// - DATA bits are left-aligned with respect to LRC edge. // - DATA bits are left-aligned with respect to LRC edge.
// - DATA bits are right-shifted by one with respect to LRC edges. // - DATA bits are right-shifted by one with respect to LRC edges.
I2S::I2S(MicType micType, i2s_port_t i2sNum) : _i2sNum(i2sNum) { I2S::I2S(MicType micType) {
if (micType == M5GO || micType == M5STACKFIRE ) { if (micType == M5GO || micType == M5STACKFIRE ) {
BITS_PER_SAMPLE = I2S_BITS_PER_SAMPLE_16BIT; BITS_PER_SAMPLE = I2S_BITS_PER_SAMPLE_16BIT;
i2s_config_t i2s_config = { i2s_config_t i2s_config = {
@ -36,14 +22,14 @@ I2S::I2S(MicType micType, i2s_port_t i2sNum) : _i2sNum(i2sNum) {
.bits_per_sample = BITS_PER_SAMPLE, .bits_per_sample = BITS_PER_SAMPLE,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S_MSB), .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .intr_alloc_flags = 0,
.dma_buf_count = 2, .dma_buf_count = 2,
.dma_buf_len = 1024 .dma_buf_len = 1024
}; };
i2s_driver_install(_i2sNum, &i2s_config, 0, NULL); i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_6); i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_6);
i2s_set_clk(_i2sNum, SAMPLE_RATE, BITS_PER_SAMPLE, I2S_CHANNEL_STEREO); i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, BITS_PER_SAMPLE, I2S_CHANNEL_STEREO);
i2s_adc_enable(_i2sNum); i2s_adc_enable(I2S_NUM_0);
} }
else if (micType == ADMP441 || micType == ICS43434 ) { else if (micType == ADMP441 || micType == ICS43434 ) {
BITS_PER_SAMPLE = I2S_BITS_PER_SAMPLE_16BIT;// I2S_BITS_PER_SAMPLE_32BIT; BITS_PER_SAMPLE = I2S_BITS_PER_SAMPLE_16BIT;// I2S_BITS_PER_SAMPLE_32BIT;
@ -53,44 +39,29 @@ I2S::I2S(MicType micType, i2s_port_t i2sNum) : _i2sNum(i2sNum) {
.bits_per_sample = BITS_PER_SAMPLE, .bits_per_sample = BITS_PER_SAMPLE,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .intr_alloc_flags = 0,
.dma_buf_count = 16, .dma_buf_count = 16,
.dma_buf_len = 60, .dma_buf_len = 60
}; };
i2s_driver_install(_i2sNum, &i2s_config, 0, NULL);
_setPins();
i2s_set_clk(_i2sNum, SAMPLE_RATE, BITS_PER_SAMPLE, I2S_CHANNEL_STEREO);
i2s_zero_dma_buffer(_i2sNum);
}
dmaInterruptSemaphore = xSemaphoreCreateBinary();
}
void I2S::_setPins() {
i2s_pin_config_t pin_config; i2s_pin_config_t pin_config;
if (_i2sNum == I2S_NUM_0) { pin_config.bck_io_num = PIN_I2S_BCLK;
pin_config.bck_io_num = PIN_I2S_0_BCLK; pin_config.ws_io_num = PIN_I2S_LRC;
pin_config.ws_io_num = PIN_I2S_0_LRC;
pin_config.data_out_num = I2S_PIN_NO_CHANGE; pin_config.data_out_num = I2S_PIN_NO_CHANGE;
pin_config.data_in_num = PIN_I2S_0_DIN; pin_config.data_in_num = PIN_I2S_DIN;
pin_config.mck_io_num = PIN_I2S_0_MCK; pin_config.mck_io_num = PIN_I2S_MCK;
} else if (_i2sNum == I2S_NUM_1) {
pin_config.bck_io_num = PIN_I2S_1_BCLK;
pin_config.ws_io_num = PIN_I2S_1_LRC;
pin_config.data_out_num = I2S_PIN_NO_CHANGE;
pin_config.data_in_num = PIN_I2S_1_DIN;
pin_config.mck_io_num = PIN_I2S_1_MCK;
}
i2s_set_pin(_i2sNum, &pin_config); i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);
i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, BITS_PER_SAMPLE, I2S_CHANNEL_STEREO);
}
} }
int I2S::Read(char* data, int numData) { int I2S::Read(char* data, int numData) {
size_t bytes_read; size_t bytes_read;
if (i2s_read(_i2sNum, (char *)data, numData, &bytes_read, portMAX_DELAY)) { if (i2s_read(I2S_NUM_0, (char *)data, numData, &bytes_read, portMAX_DELAY)) {
return (int)bytes_read; return (int)bytes_read;
} }

View File

@ -14,15 +14,11 @@ enum MicType {
}; };
class I2S { class I2S {
i2s_bits_per_sample_t BITS_PER_SAMPLE;
public: public:
I2S(MicType micType, i2s_port_t i2sNum=I2S_NUM_0); I2S(MicType micType);
int Read(char* data, int numData); int Read(char* data, int numData);
int GetBitPerSample(); int GetBitPerSample();
private:
i2s_port_t _i2sNum;
i2s_bits_per_sample_t BITS_PER_SAMPLE;
void _setPins();
}; };
#endif // _I2S_H #endif // _I2S_H

View File

@ -1,73 +0,0 @@
#include <Arduino.h>
enum class CircularBufferErrorCode {
SUCCESS,
BUFFER_FULL,
BUFFER_EMPTY,
MEMORY_ERROR
};
template<typename T>
class CircularBuffer {
public:
CircularBuffer(size_t _capacity) : _capacity(_capacity) {
_buffer = static_cast<T*>(malloc(_capacity * sizeof(T)));
if (_buffer == nullptr) {
// Handle memory allocation error
_errorCode = CircularBufferErrorCode::MEMORY_ERROR;
} else {
_bufferEnd = _buffer + _capacity;
_count = 0;
_head = _buffer;
_tail = _buffer;
_errorCode = CircularBufferErrorCode::SUCCESS;
}
}
~CircularBuffer() {
free(_buffer);
}
CircularBufferErrorCode pushBack(const T& item) {
if (isFull()) {
return CircularBufferErrorCode::BUFFER_FULL;
}
*_head = item;
_head++;
if (_head == _bufferEnd) {
_head = _buffer;
}
_count++;
return CircularBufferErrorCode::SUCCESS;
}
CircularBufferErrorCode popFront(T& item) {
if (isEmpty()) {
return CircularBufferErrorCode::BUFFER_EMPTY;
}
item = *_tail;
_tail++;
if (_tail == _bufferEnd) {
_tail = _buffer;
}
_count--;
return CircularBufferErrorCode::SUCCESS;
}
bool isFull() {
return _count == _capacity;
}
bool isEmpty() {
return _count == 0;
}
private:
T *_buffer; // Data _buffer
T *_bufferEnd; // End of data buffer
size_t _capacity; // Maximum number of items in the buffer
size_t _count; // Number of items in the buffer
T *_head; // Pointer to head
T *_tail; // Pointer to tail
CircularBufferErrorCode _errorCode; // Error code
};

View File

@ -10,10 +10,7 @@
#include "infra/Http.h" #include "infra/Http.h"
#include "infra/Eth.h" #include "infra/Eth.h"
#include "domain/Controller.h" #include "domain/Recorder.h"
#include "domain/AidStrategy.h"
volatile bool loudnessFlag = false;
unsigned long beginMicros, endMicros; unsigned long beginMicros, endMicros;
@ -21,7 +18,7 @@ Eth eth;
EthernetClient* client = eth.getEthClient(); EthernetClient* client = eth.getEthClient();
Http http(&eth); Http http(&eth);
Controller& controller = Controller::getInstance(); Recorder* recorder;
Time timeService = Time::getInstance(); Time timeService = Time::getInstance();
@ -38,24 +35,6 @@ void setInitialTime() {
timeService.setInitialTime(currentTime); timeService.setInitialTime(currentTime);
} }
void writeToPrecedingBufferTask(void* parameter) {
while (true) {
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
void createWriteToPrecedingBufferTask() {
TaskHandle_t xHandle = NULL;
xTaskCreatePinnedToCore(writeToPrecedingBufferTask,
"writeToPrecedingBufferTask", 4096, NULL, 1, &xHandle, 0);
if (xHandle == NULL) {
ESP_LOGE("TASK1", "Failed to task create");
};
}
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
delay(1000); delay(1000);
@ -63,10 +42,7 @@ void setup() {
eth.initEthernet(); eth.initEthernet();
setInitialTime(); setInitialTime();
Serial.println(1); recorder = new Recorder(ADMP441, &http);
controller.setStrategy(new AidStrategy(ADMP441, &http));
//createWriteToPrecedingBufferTask();
beginMicros = micros(); beginMicros = micros();
} }
@ -74,5 +50,12 @@ void setup() {
void loop() { void loop() {
eth.readAndPrintData(true); // set to false for better speed measurement eth.readAndPrintData(true); // set to false for better speed measurement
controller.process(); if (recorder->isNoiseDetected(NOISE_THRESHOLD)) {
recorder->recordAudio();
}
//recorder->recordAudio();
//delay(15000);
//eth.handleDisconnect(beginMicros, micros());
} }

View File

@ -1,28 +0,0 @@
cmake_minimum_required(VERSION 2.8.8)
project(arduino-mock-test-all)
find_package(Threads REQUIRED)
add_subdirectory(arduino_mock)
include_directories(
${ARDUINO_MOCK_INCLUDE_DIRS}
${ARDUINO_MOCK_LIBS_DIR}/lib/gtest/gtest/src/gtest/googletest/include
${ARDUINO_MOCK_LIBS_DIR}/lib/gtest/gtest/src/gtest/googlemock/include
)
message(ARDUINO_MOCK_INCLUDE_DIRS="${ARDUINO_MOCK_INCLUDE_DIRS}")
file(GLOB LIBS_SRCS "libs/*.cpp")
file(GLOB SRCS "*.cpp")
add_executable(test-all ${SRCS} ${LIBS_SRCS})
target_link_libraries(test-all
${ARDUINO_MOCK_LIBS_DIR}/lib/gtest/gtest/src/gtest-build/googlemock/gtest/libgtest.a
${ARDUINO_MOCK_LIBS_DIR}/lib/gtest/gtest/src/gtest-build/googlemock/libgmock.a
${ARDUINO_MOCK_LIBS_DIR}/dist/lib/libarduino_mock.a
${CMAKE_THREAD_LIBS_INIT}
)
add_dependencies(test-all arduino_mock)
enable_testing()
add_test(TestAll test-all)

View File

@ -1,10 +0,0 @@
/*
* TestAll.cpp
*/
#include "gtest/gtest.h"
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -1,16 +0,0 @@
cmake_minimum_required(VERSION 2.8.8)
project(arduino_mock_builder C CXX)
include(ExternalProject)
ExternalProject_Add(arduino_mock
GIT_REPOSITORY https://github.com/wjszlachta/arduino-mock.git
GIT_TAG master
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/arduino_mock
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(arduino_mock source_dir)
set(ARDUINO_MOCK_INCLUDE_DIRS ${source_dir}/include/arduino-mock PARENT_SCOPE)
ExternalProject_Get_Property(arduino_mock binary_dir)
set(ARDUINO_MOCK_LIBS_DIR ${binary_dir} PARENT_SCOPE)

View File

@ -1,9 +0,0 @@
#!/bin/sh
set -eu
cd -- "$(dirname -- "$0")"
mkdir -p build
cd build
cmake ..
make
ctest -V

View File

@ -1,117 +0,0 @@
#include <gtest/gtest.h>
#include "../../src/libs/CircularBuffer.h"
// Test fixture for CircularBuffer
class CircularBufferTest : public ::testing::Test {
protected:
void SetUp() override {
// Initialization code that runs before each test
buffer = new CircularBuffer<int>(3); // Create a circular buffer of integers with capacity 5
}
void TearDown() override {
// Clean-up code that runs after each test
delete buffer;
}
// Member variables accessible in test cases
CircularBuffer<int>* buffer;
};
// Test CircularBuffer push and pop operations
TEST_F(CircularBufferTest, PushAndPop) {
// Push some integers into the circular buffer
EXPECT_EQ(buffer->pushBack(10), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(buffer->pushBack(20), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(buffer->pushBack(30), CircularBufferErrorCode::SUCCESS);
// Pop integers from the circular buffer and check values
int item;
EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(item, 10);
EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(item, 20);
EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(item, 30);
}
// Test CircularBuffer with different types
TEST_F(CircularBufferTest, DifferentTypes) {
// Create a circular buffer of characters with capacity 3
CircularBuffer<char> charBuffer(3);
// Push some characters into the circular buffer
EXPECT_EQ(charBuffer.pushBack('a'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charBuffer.pushBack('b'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charBuffer.pushBack('c'), CircularBufferErrorCode::SUCCESS);
// Pop characters from the circular buffer and check values
char c;
EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'a');
EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'b');
EXPECT_EQ(charBuffer.popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'c');
}
// Test CircularBuffer with full and empty conditions
TEST_F(CircularBufferTest, FullAndEmpty) {
// Fill up the buffer
buffer->pushBack(10);
buffer->pushBack(20);
buffer->pushBack(30);
// Try pushing into a full buffer
EXPECT_EQ(buffer->pushBack(40), CircularBufferErrorCode::BUFFER_FULL);
// Pop all elements from the buffer
int item;
EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::SUCCESS);
// Try popping from an empty buffer
EXPECT_EQ(buffer->popFront(item), CircularBufferErrorCode::BUFFER_EMPTY);
}
// Test CircularBuffer with nested CircularBuffer
TEST_F(CircularBufferTest, CircularBufferWithNestedCircularBuffer) {
CircularBuffer<CircularBuffer<char>*> charBuffer(2);
CircularBuffer<char> charSubBuffer1(3);
CircularBuffer<char> charSubBuffer2(3);
EXPECT_EQ(charSubBuffer1.pushBack('a'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer1.pushBack('b'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer1.pushBack('c'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer2.pushBack('d'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer2.pushBack('e'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charSubBuffer2.pushBack('f'), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charBuffer.pushBack(&charSubBuffer1), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(charBuffer.pushBack(&charSubBuffer2), CircularBufferErrorCode::SUCCESS);
CircularBuffer<char>* poppedCharSubBuffer;
EXPECT_EQ(charBuffer.popFront(poppedCharSubBuffer), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(poppedCharSubBuffer, &charSubBuffer1);
char c;
EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'a');
EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'b');
EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'c');
EXPECT_EQ(charBuffer.popFront(poppedCharSubBuffer), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(poppedCharSubBuffer, &charSubBuffer2);
EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'd');
EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'e');
EXPECT_EQ(poppedCharSubBuffer->popFront(c), CircularBufferErrorCode::SUCCESS);
EXPECT_EQ(c, 'f');
}