Implemented recording audio with threshold and sending it to Rat
This commit is contained in:
parent
5d74460e65
commit
c0bf4286d1
@ -19,3 +19,5 @@ board_build.partitions = no_ota.csv
|
||||
lib_deps =
|
||||
ottowinter/AsyncMqttClient-esphome@^0.8.6
|
||||
arduino-libraries/Ethernet@^2.0.2
|
||||
paulstoffregen/Time@^1.6.1
|
||||
bblanchon/ArduinoJson@^6.21.3
|
||||
|
@ -1,84 +0,0 @@
|
||||
#include "Audio.h"
|
||||
|
||||
Audio::Audio(MicType micType) {
|
||||
wavData = new char*[wavDataSize/dividedWavDataSize];
|
||||
for (int i = 0; i < wavDataSize/dividedWavDataSize; ++i) wavData[i] = new char[dividedWavDataSize];
|
||||
i2s = new I2S(micType);
|
||||
}
|
||||
|
||||
Audio::~Audio() {
|
||||
for (int i = 0; i < wavDataSize/dividedWavDataSize; ++i) delete[] wavData[i];
|
||||
delete[] wavData;
|
||||
delete i2s;
|
||||
}
|
||||
|
||||
void Audio::CreateWavHeader(byte* header, int waveDataSize){
|
||||
header[0] = 'R';
|
||||
header[1] = 'I';
|
||||
header[2] = 'F';
|
||||
header[3] = 'F';
|
||||
unsigned int fileSizeMinus8 = waveDataSize + 44 - 8;
|
||||
header[4] = (byte)(fileSizeMinus8 & 0xFF);
|
||||
header[5] = (byte)((fileSizeMinus8 >> 8) & 0xFF);
|
||||
header[6] = (byte)((fileSizeMinus8 >> 16) & 0xFF);
|
||||
header[7] = (byte)((fileSizeMinus8 >> 24) & 0xFF);
|
||||
header[8] = 'W';
|
||||
header[9] = 'A';
|
||||
header[10] = 'V';
|
||||
header[11] = 'E';
|
||||
header[12] = 'f';
|
||||
header[13] = 'm';
|
||||
header[14] = 't';
|
||||
header[15] = ' ';
|
||||
header[16] = 0x10; // linear PCM
|
||||
header[17] = 0x00;
|
||||
header[18] = 0x00;
|
||||
header[19] = 0x00;
|
||||
header[20] = 0x01; // linear PCM
|
||||
header[21] = 0x00;
|
||||
header[22] = 0x01; // monoral
|
||||
header[23] = 0x00;
|
||||
header[24] = 0x80; // sampling rate 16000
|
||||
header[25] = 0x3E;
|
||||
header[26] = 0x00;
|
||||
header[27] = 0x00;
|
||||
header[28] = 0x00; // Byte/sec = 16000x2x1 = 32000
|
||||
header[29] = 0x7D;
|
||||
header[30] = 0x00;
|
||||
header[31] = 0x00;
|
||||
header[32] = 0x02; // 16bit monoral
|
||||
header[33] = 0x00;
|
||||
header[34] = 0x10; // 16bit
|
||||
header[35] = 0x00;
|
||||
header[36] = 'd';
|
||||
header[37] = 'a';
|
||||
header[38] = 't';
|
||||
header[39] = 'a';
|
||||
header[40] = (byte)(waveDataSize & 0xFF);
|
||||
header[41] = (byte)((waveDataSize >> 8) & 0xFF);
|
||||
header[42] = (byte)((waveDataSize >> 16) & 0xFF);
|
||||
header[43] = (byte)((waveDataSize >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
void Audio::Record() {
|
||||
CreateWavHeader(paddedHeader, wavDataSize);
|
||||
int bitBitPerSample = i2s->GetBitPerSample();
|
||||
if (bitBitPerSample == 16) {
|
||||
for (int j = 0; j < wavDataSize/dividedWavDataSize; ++j) {
|
||||
i2s->Read(i2sBuffer, i2sBufferSize/2);
|
||||
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) {
|
||||
for (int j = 0; j < wavDataSize/dividedWavDataSize; ++j) {
|
||||
i2s->Read(i2sBuffer, i2sBufferSize);
|
||||
for (int i = 0; i < i2sBufferSize/8; ++i) {
|
||||
wavData[j][2*i] = i2sBuffer[8*i + 2] << 4;
|
||||
wavData[j][2*i + 1] = i2sBuffer[8*i + 3] << 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
src/Audio.h
26
src/Audio.h
@ -1,26 +0,0 @@
|
||||
#ifndef _AUDIO_H
|
||||
#define _AUDIO_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "I2S.h"
|
||||
|
||||
// 16bit, monoral, 16000Hz, linear PCM
|
||||
class Audio {
|
||||
I2S* i2s;
|
||||
static const int headerSize = 44;
|
||||
static const int i2sBufferSize = 6000;
|
||||
char i2sBuffer[i2sBufferSize];
|
||||
void CreateWavHeader(byte* header, int waveDataSize);
|
||||
|
||||
public:
|
||||
static const int wavDataSize = 90000; // It must be multiple of dividedWavDataSize. Recording time is about 1.9 second.
|
||||
static const int dividedWavDataSize = i2sBufferSize/4;
|
||||
char** wavData; // It's divided. Because large continuous memory area can't be allocated in esp32.
|
||||
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.
|
||||
|
||||
Audio(MicType micType);
|
||||
~Audio();
|
||||
void Record();
|
||||
};
|
||||
|
||||
#endif // _AUDIO_H
|
29
src/config/config.h
Normal file
29
src/config/config.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define ETH_MISO 19
|
||||
#define ETH_MOSI 23
|
||||
#define ETH_SCK 18
|
||||
#define ETH_SS 5
|
||||
|
||||
#define SD_CS 5
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
#define MYIPADDR 192,168,1,28
|
||||
#define MYIPMASK 255,255,255,0
|
||||
#define MYDNS 192,168,1,1
|
||||
#define MYGW 192,168,1,1
|
||||
#define MYMAC { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }
|
||||
|
||||
#define RAT_IP "192.168.1.173"
|
||||
#define RAT_PORT 8081
|
||||
|
||||
// make it configurable with some panel
|
||||
//#define NOISE_THRESHOLD 3000000
|
||||
#define NOISE_THRESHOLD 10000000
|
83
src/domain/Recorder.cpp
Normal file
83
src/domain/Recorder.cpp
Normal 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_audio", 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
25
src/domain/Recorder.h
Normal 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);
|
||||
};
|
123
src/infra/Audio.cpp
Normal file
123
src/infra/Audio.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
#include "infra/Audio.h"
|
||||
|
||||
#include <base64.h>
|
||||
|
||||
Audio::Audio(MicType micType) {
|
||||
_wavData = new char*[_wavDataSize/_dividedWavDataSize];
|
||||
for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) _wavData[i] = new char[_dividedWavDataSize];
|
||||
i2s = new I2S(micType);
|
||||
}
|
||||
|
||||
Audio::~Audio() {
|
||||
for (int i = 0; i < _wavDataSize/_dividedWavDataSize; ++i) delete[] _wavData[i];
|
||||
delete[] _wavData;
|
||||
delete i2s;
|
||||
}
|
||||
|
||||
void Audio::_createWavHeader(byte* header, int waveDataSize){
|
||||
header[0] = 'R';
|
||||
header[1] = 'I';
|
||||
header[2] = 'F';
|
||||
header[3] = 'F';
|
||||
unsigned int fileSizeMinus8 = waveDataSize + 44 - 8;
|
||||
header[4] = (byte)(fileSizeMinus8 & 0xFF);
|
||||
header[5] = (byte)((fileSizeMinus8 >> 8) & 0xFF);
|
||||
header[6] = (byte)((fileSizeMinus8 >> 16) & 0xFF);
|
||||
header[7] = (byte)((fileSizeMinus8 >> 24) & 0xFF);
|
||||
header[8] = 'W';
|
||||
header[9] = 'A';
|
||||
header[10] = 'V';
|
||||
header[11] = 'E';
|
||||
header[12] = 'f';
|
||||
header[13] = 'm';
|
||||
header[14] = 't';
|
||||
header[15] = ' ';
|
||||
header[16] = 0x10; // linear PCM
|
||||
header[17] = 0x00;
|
||||
header[18] = 0x00;
|
||||
header[19] = 0x00;
|
||||
header[20] = 0x01; // linear PCM
|
||||
header[21] = 0x00;
|
||||
header[22] = 0x01; // monoral
|
||||
header[23] = 0x00;
|
||||
header[24] = 0x80; // sampling rate 16000
|
||||
header[25] = 0x3E;
|
||||
header[26] = 0x00;
|
||||
header[27] = 0x00;
|
||||
header[28] = 0x00; // Byte/sec = 16000x2x1 = 32000
|
||||
header[29] = 0x7D;
|
||||
header[30] = 0x00;
|
||||
header[31] = 0x00;
|
||||
header[32] = 0x02; // 16bit monoral
|
||||
header[33] = 0x00;
|
||||
header[34] = 0x10; // 16bit
|
||||
header[35] = 0x00;
|
||||
header[36] = 'd';
|
||||
header[37] = 'a';
|
||||
header[38] = 't';
|
||||
header[39] = 'a';
|
||||
header[40] = (byte)(waveDataSize & 0xFF);
|
||||
header[41] = (byte)((waveDataSize >> 8) & 0xFF);
|
||||
header[42] = (byte)((waveDataSize >> 16) & 0xFF);
|
||||
header[43] = (byte)((waveDataSize >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
char** Audio::record() {
|
||||
_createWavHeader(_paddedHeader, _wavDataSize);
|
||||
int bitBitPerSample = i2s->GetBitPerSample();
|
||||
|
||||
_startMicros = micros();
|
||||
|
||||
if (bitBitPerSample == 16) {
|
||||
for (int j = 0; j < _wavDataSize/_dividedWavDataSize; ++j) {
|
||||
i2s->Read(_i2sBuffer, _i2sBufferSize/2);
|
||||
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) {
|
||||
for (int j = 0; j < _wavDataSize/_dividedWavDataSize; ++j) {
|
||||
i2s->Read(_i2sBuffer, _i2sBufferSize);
|
||||
for (int i = 0; i < _i2sBufferSize/8; ++i) {
|
||||
_wavData[j][2*i] = _i2sBuffer[8*i + 2] << 4;
|
||||
_wavData[j][2*i + 1] = _i2sBuffer[8*i + 3] << 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _wavData;
|
||||
}
|
||||
|
||||
int Audio::getSize() {
|
||||
return (_wavDataSize + sizeof(_paddedHeader)) * 4 / 3; // 4/3 is from base64 encoding
|
||||
}
|
||||
|
||||
String Audio::print(Stream* stream) {
|
||||
String enc = base64::encode(_paddedHeader, sizeof(_paddedHeader));
|
||||
enc.replace("\n", "");
|
||||
stream->print(enc);
|
||||
|
||||
for (int j = 0; j < _wavDataSize / _dividedWavDataSize; ++j) {
|
||||
enc = base64::encode((byte*)_wavData[j], _dividedWavDataSize);
|
||||
enc.replace("\n", "");
|
||||
stream->print(enc);
|
||||
}
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
unsigned long Audio::getStartMicros() {
|
||||
return _startMicros;
|
||||
}
|
||||
|
||||
bool Audio::isNoiseDetected(int threshold) {
|
||||
i2s->Read(_i2sBuffer2, _i2sBufferSize/2);
|
||||
if (*(int*)_i2sBuffer2 > threshold || *(int*)_i2sBuffer2 < -threshold) {
|
||||
Serial.print(*(int*)_i2sBuffer2);
|
||||
Serial.print(" ");
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
36
src/infra/Audio.h
Normal file
36
src/infra/Audio.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef _AUDIO_H
|
||||
#define _AUDIO_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "infra/I2S.h"
|
||||
|
||||
// 16bit, monoral, 16000Hz, linear PCM
|
||||
class Audio {
|
||||
private:
|
||||
I2S* i2s;
|
||||
static const int _headerSize = 44;
|
||||
static const int _i2sBufferSize = 6000;
|
||||
char _i2sBuffer[_i2sBufferSize];
|
||||
char _i2sBuffer2[_i2sBufferSize];
|
||||
|
||||
static const int _wavDataSize = 90000; // It must be multiple of _dividedWavDataSize. Recording time is about 1.9 second.
|
||||
static const int _dividedWavDataSize = _i2sBufferSize/4;
|
||||
char** _wavData; // It's divided. Because large continuous memory area can't be allocated in esp32.
|
||||
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;
|
||||
|
||||
void _createWavHeader(byte* header, int waveDataSize);
|
||||
|
||||
public:
|
||||
Audio(MicType micType);
|
||||
~Audio();
|
||||
char** record();
|
||||
int getSize();
|
||||
String print(Stream* stream);
|
||||
unsigned long getStartMicros();
|
||||
bool isNoiseDetected(int threshold);
|
||||
};
|
||||
|
||||
#endif // _AUDIO_H
|
92
src/infra/Eth.cpp
Normal file
92
src/infra/Eth.cpp
Normal file
@ -0,0 +1,92 @@
|
||||
#include <SPI.h>
|
||||
|
||||
#include "config/config.h"
|
||||
#include "infra/Eth.h"
|
||||
|
||||
EthernetClient* Eth::getEthClient() {
|
||||
return &_client;
|
||||
}
|
||||
|
||||
void Eth::initEthernet() {
|
||||
Serial.println("| Begin Ethernet");
|
||||
|
||||
Ethernet.init(5); // MKR ETH Shield
|
||||
|
||||
SPI.begin(ETH_SCK, ETH_MISO, ETH_MOSI, ETH_SS);
|
||||
|
||||
Serial.println("| SCK = " + String(ETH_SCK));
|
||||
Serial.println("| MISO = " + String(ETH_MISO));
|
||||
Serial.println("| MOSI = " + String(ETH_MOSI));
|
||||
Serial.println("| SS = " + String(ETH_SS));
|
||||
|
||||
byte mac[] = MYMAC;
|
||||
|
||||
if (Ethernet.begin(mac)) { // Dynamic IP setup
|
||||
Serial.println("| DHCP OK!");
|
||||
} else {
|
||||
Serial.println("| Failed to configure Ethernet using DHCP");
|
||||
// Check for Ethernet hardware present
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
Serial.println("| Ethernet shield was not found. Sorry, can't run without hardware. :(");
|
||||
while (true) {
|
||||
delay(1); // do nothing, no point running without Ethernet hardware
|
||||
}
|
||||
}
|
||||
if (Ethernet.linkStatus() == LinkOFF) {
|
||||
Serial.println("| Ethernet cable is not connected.");
|
||||
}
|
||||
|
||||
IPAddress ip(MYIPADDR);
|
||||
IPAddress dns(MYDNS);
|
||||
IPAddress gw(MYGW);
|
||||
IPAddress sn(MYIPMASK);
|
||||
Ethernet.begin(mac, ip, dns, gw, sn);
|
||||
Serial.println("| STATIC OK!");
|
||||
}
|
||||
delay(5000);
|
||||
|
||||
Serial.print("| Local IP : ");
|
||||
Serial.println(Ethernet.localIP());
|
||||
Serial.print("| Subnet Mask : ");
|
||||
Serial.println(Ethernet.subnetMask());
|
||||
Serial.print("| Gateway IP : ");
|
||||
Serial.println(Ethernet.gatewayIP());
|
||||
Serial.print("| DNS Server : ");
|
||||
Serial.println(Ethernet.dnsServerIP());
|
||||
|
||||
Serial.println("| Ethernet Successfully Initialized");
|
||||
}
|
||||
|
||||
void Eth::readAndPrintData(bool printWebData) {
|
||||
int len = _client.available();
|
||||
if (len > 0) {
|
||||
byte buffer[80];
|
||||
if (len > 80) len = 80;
|
||||
_client.read(buffer, len);
|
||||
if (printWebData) {
|
||||
Serial.write(buffer, len);
|
||||
}
|
||||
_byteCount = _byteCount + len;
|
||||
}
|
||||
}
|
||||
|
||||
void Eth::handleDisconnect(unsigned long beginMicros, unsigned long endMicros) {
|
||||
if (!_client.connected() && !_disconnected) {
|
||||
endMicros = micros();
|
||||
Serial.println();
|
||||
Serial.println("| disconnecting.");
|
||||
_client.stop();
|
||||
Serial.print("| Received ");
|
||||
Serial.print(_byteCount);
|
||||
Serial.print(" bytes in ");
|
||||
float seconds = (float)(endMicros - beginMicros) / 1000000.0;
|
||||
Serial.print(seconds, 4);
|
||||
float rate = (float)_byteCount / seconds / 1000.0;
|
||||
Serial.print(", rate = ");
|
||||
Serial.print(rate);
|
||||
Serial.print(" kbytes/second");
|
||||
Serial.println();
|
||||
|
||||
_disconnected = true;
|
||||
}
|
||||
}
|
15
src/infra/Eth.h
Normal file
15
src/infra/Eth.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <Ethernet.h>
|
||||
|
||||
class Eth {
|
||||
EthernetClient _client;
|
||||
unsigned long _byteCount = 0;
|
||||
bool _disconnected = false;
|
||||
|
||||
public:
|
||||
EthernetClient* getEthClient();
|
||||
void initEthernet();
|
||||
void readAndPrintData(bool printWebData);
|
||||
void handleDisconnect(unsigned long beginMicros, unsigned long endMicros);
|
||||
};
|
135
src/infra/Http.cpp
Normal file
135
src/infra/Http.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
#include "infra/Http.h"
|
||||
#include "infra/Eth.h"
|
||||
#include "Http.h"
|
||||
|
||||
Http::Http(Eth* eth) {
|
||||
_client = eth->getEthClient();
|
||||
}
|
||||
|
||||
String Http::send(String method, String endpoint, String host, int port, String payload, String contentType) {
|
||||
String body = "";
|
||||
|
||||
if (_client->connect(host.c_str(), port)) {
|
||||
_logConnected(host, port);
|
||||
|
||||
this->sendRequest(method, endpoint, host, contentType, payload);
|
||||
String response = this->readResponse();
|
||||
|
||||
body = Http::extractBody(response);
|
||||
|
||||
Serial.println("Response:");
|
||||
Serial.println(response);
|
||||
} else {
|
||||
Serial.println("| Connection failed");
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
void Http::sendRequest(String method, String endpoint, String host, String contentType, String payload) {
|
||||
this->printHeader(method, endpoint, host, contentType);
|
||||
this->printPayload(payload);
|
||||
}
|
||||
|
||||
void Http::printHeader(String method, String endpoint, String host, String contentType) {
|
||||
_client->print(method);
|
||||
_client->print(" ");
|
||||
_client->print(endpoint);
|
||||
_client->println(" HTTP/1.1");
|
||||
_client->print("Host: ");
|
||||
_client->println(host);
|
||||
|
||||
if (contentType != "") {
|
||||
_client->print("Content-Type: ");
|
||||
_client->println(contentType);
|
||||
}
|
||||
|
||||
_logHeader(method, endpoint, host, contentType);
|
||||
}
|
||||
|
||||
void Http::printPayload(String payload) {
|
||||
if (payload != "") {
|
||||
this->printContentLength(payload.length());
|
||||
_client->println(payload);
|
||||
} else {
|
||||
_client->println();
|
||||
}
|
||||
|
||||
_logPayload(payload);
|
||||
}
|
||||
|
||||
void Http::printContentLength(int contentLength) {
|
||||
_client->print("Content-Length: ");
|
||||
_client->println(contentLength);
|
||||
_client->println();
|
||||
}
|
||||
|
||||
String Http::readResponse() {
|
||||
String response = "";
|
||||
|
||||
while (_client->connected()) {
|
||||
if (_client->available()) {
|
||||
char c = _client->read();
|
||||
response += c;
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
int Http::connect(const char *host, uint16_t port) {
|
||||
return _client->connect(host, port);
|
||||
}
|
||||
|
||||
void Http::stop() {
|
||||
_client->stop();
|
||||
Serial.println("| Disconnected from server");
|
||||
}
|
||||
|
||||
EthernetClient* Http::getClient() {
|
||||
return _client;
|
||||
}
|
||||
|
||||
String Http::extractBody(String httpResponse) {
|
||||
String body = "";
|
||||
|
||||
const char* bodyStart = strstr(httpResponse.c_str(), "\r\n\r\n");
|
||||
|
||||
if (bodyStart != NULL) {
|
||||
body = bodyStart + 4;
|
||||
} else {
|
||||
Serial.println("| Body not found in response.");
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
void Http::_logConnected(String host, int port) {
|
||||
Serial.println();
|
||||
Serial.print("| Connected to server ");
|
||||
Serial.print(host);
|
||||
Serial.print(":");
|
||||
Serial.println(port);
|
||||
}
|
||||
|
||||
void Http::_logHeader(String method, String endpoint, String host, String contentType) {
|
||||
Serial.println("========================================");
|
||||
|
||||
Serial.print("| ");
|
||||
Serial.print(method);
|
||||
Serial.print(" ");
|
||||
Serial.print(host);
|
||||
Serial.println(endpoint);
|
||||
|
||||
if (contentType && contentType != "") {
|
||||
Serial.print("| Content-Type: ");
|
||||
Serial.println(contentType);
|
||||
}
|
||||
}
|
||||
|
||||
void Http::_logPayload(String payload) {
|
||||
if (payload && payload != "") {
|
||||
Serial.println("| ");
|
||||
Serial.println(payload);
|
||||
}
|
||||
}
|
32
src/infra/Http.h
Normal file
32
src/infra/Http.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "infra/Eth.h"
|
||||
|
||||
class Http {
|
||||
private:
|
||||
EthernetClient* _client;
|
||||
|
||||
void _logHeader(String method, String endpoint, String host, String contentType);
|
||||
void _logPayload(String payload);
|
||||
void _logConnected(String host, int port);
|
||||
|
||||
public:
|
||||
Http(Eth* eth);
|
||||
|
||||
String send(String method, String endpoint, String host, int port, String payload, String contentType);
|
||||
void sendRequest(String method, String endpoint, String host, String contentType, String payload);
|
||||
|
||||
void printHeader(String method, String endpoint, String host, String contentType);
|
||||
void printContentLength(int contentLength);
|
||||
void printPayload(String payload);
|
||||
|
||||
String readResponse();
|
||||
|
||||
int connect(const char *host, uint16_t port);
|
||||
void stop();
|
||||
EthernetClient* getClient();
|
||||
|
||||
static String extractBody(String httpResponse);
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
#include "I2S.h"
|
||||
#include "infra/I2S.h"
|
||||
#define SAMPLE_RATE (16000)
|
||||
#define PIN_I2S_BCLK (GPIO_NUM_26)
|
||||
#define PIN_I2S_LRC (GPIO_NUM_25)
|
261
src/main.cpp
261
src/main.cpp
@ -1,242 +1,61 @@
|
||||
/*
|
||||
Web client
|
||||
|
||||
This sketch connects to a test website (httpbin.org)
|
||||
and try to do a GET request, the output is printed
|
||||
on Serial
|
||||
|
||||
by Renzo Mischianti <www.mischianti.org>
|
||||
|
||||
https://www.mischianti.org
|
||||
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
|
||||
#include <Ethernet.h>
|
||||
#include <base64.h>
|
||||
#include "Audio.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#define ETH_MISO 19
|
||||
#define ETH_MOSI 23
|
||||
#define ETH_SCK 18
|
||||
#define ETH_SS 5
|
||||
#include "config/config.h"
|
||||
#include "utils/Time.h"
|
||||
|
||||
#include "infra/Audio.h"
|
||||
#include "infra/Http.h"
|
||||
#include "infra/Eth.h"
|
||||
|
||||
#define SD_CS 5
|
||||
#define SPI_MOSI 23
|
||||
#define SPI_MISO 19
|
||||
#define SPI_SCK 18
|
||||
#define I2S_DOUT 25
|
||||
#define I2S_BCLK 27
|
||||
#define I2S_LRC 26
|
||||
|
||||
// if you don't want to use DNS (and reduce your sketch size)
|
||||
// use the numeric IP instead of the name for the server:
|
||||
//IPAddress server(74,125,232,128); // numeric IP for Google (no DNS)
|
||||
//char server[] = "www.google.com"; // name address for Google (using DNS)
|
||||
char server[] = "httpbin.org"; // name address for Google (using DNS)
|
||||
|
||||
// Enter a MAC address for your controller below.
|
||||
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
|
||||
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||
|
||||
// Set the static IP address to use if the DHCP fails to assign
|
||||
#define MYIPADDR 192,168,1,28
|
||||
#define MYIPMASK 255,255,255,0
|
||||
#define MYDNS 192,168,1,1
|
||||
#define MYGW 192,168,1,1
|
||||
#include "domain/Recorder.h"
|
||||
|
||||
|
||||
//#define ALTERNATE_PINS
|
||||
|
||||
|
||||
// Initialize the Ethernet client library
|
||||
// with the IP address and port of the server
|
||||
// that you want to connect to (port 80 is default for HTTP):
|
||||
EthernetClient client;
|
||||
|
||||
// Variables to measure the speed
|
||||
unsigned long beginMicros, endMicros;
|
||||
unsigned long byteCount = 0;
|
||||
bool printWebData = true; // set to false for better speed measurement
|
||||
|
||||
void PrintHttpBody2(Audio* audio)
|
||||
{
|
||||
String enc = base64::encode(audio->paddedHeader, sizeof(audio->paddedHeader));
|
||||
enc.replace("\n", ""); // delete last "\n"
|
||||
client.print(enc); // HttpBody2
|
||||
char** wavData = audio->wavData;
|
||||
for (int j = 0; j < audio->wavDataSize / audio->dividedWavDataSize; ++j) {
|
||||
enc = base64::encode((byte*)wavData[j], audio->dividedWavDataSize);
|
||||
enc.replace("\n", "");// delete last "\n"
|
||||
client.print(enc); // HttpBody2
|
||||
}
|
||||
}
|
||||
Eth eth;
|
||||
EthernetClient* client = eth.getEthClient();
|
||||
Http http(ð);
|
||||
|
||||
void Transcribe(Audio* audio) {
|
||||
String HttpBody1 = "{\"audio\":{\"content\":\"";
|
||||
String HttpBody3 = "\"}}\r\n\r\n";
|
||||
int httpBody2Length = (audio->wavDataSize + sizeof(audio->paddedHeader)) * 4 / 3; // 4/3 is from base64 encoding
|
||||
String ContentLength = String(HttpBody1.length() + httpBody2Length + HttpBody3.length());
|
||||
String HttpHeader;
|
||||
// if (authentication == USE_APIKEY)
|
||||
HttpHeader = String("POST /save_audio HTTP/1.1\r\nHost: 192.168.1.173\r\nContent-Type: application/json\r\nContent-Length: ") + ContentLength + String("\r\n\r\n");
|
||||
// else if (authentication == USE_ACCESSTOKEN)
|
||||
// HttpHeader = String("POST /v1beta1/speech:syncrecognize HTTP/1.1\r\nHost: speech.googleapis.com\r\nContent-Type: application/json\r\nAuthorization: Bearer ")
|
||||
// + AccessToken + String("\r\nContent-Length: ") + ContentLength + String("\r\n\r\n");
|
||||
client.print(HttpHeader);
|
||||
client.print(HttpBody1);
|
||||
PrintHttpBody2(audio);
|
||||
client.print(HttpBody3);
|
||||
for (int i = 0; i < httpBody2Length; i++) {
|
||||
client.print(" ");
|
||||
}
|
||||
String My_Answer="";
|
||||
Recorder* recorder;
|
||||
|
||||
Time timeService = Time::getInstance();
|
||||
|
||||
void setInitialTime() {
|
||||
String response = http.send("GET", "/time", RAT_IP, RAT_PORT, "", "");
|
||||
|
||||
DynamicJsonDocument jsonDoc(1024);
|
||||
DeserializationError error = deserializeJson(jsonDoc, response);
|
||||
JsonVariant root = jsonDoc.as<JsonVariant>();
|
||||
|
||||
unsigned long currentTime = root["current_timestamp"].as<unsigned long>();
|
||||
|
||||
timeService.setInitialMicros(micros());
|
||||
timeService.setInitialTime(currentTime);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
|
||||
eth.initEthernet();
|
||||
|
||||
Serial.begin(115200);
|
||||
delay(1000);
|
||||
Serial.println("Begin Ethernet");
|
||||
Serial.println("COM0 setup OK!");
|
||||
|
||||
|
||||
|
||||
// You can use Ethernet.init(pin) to configure the CS pin
|
||||
//Ethernet.init(10); // Most Arduino shields
|
||||
Ethernet.init(5); // MKR ETH Shield
|
||||
//Ethernet.init(0); // Teensy 2.0
|
||||
//Ethernet.init(20); // Teensy++ 2.0
|
||||
//Ethernet.init(15); // ESP8266 with Adafruit FeatherWing Ethernet
|
||||
//Ethernet.init(33); // ESP32 with Adafruit FeatherWing Ethernet
|
||||
|
||||
SPI.begin(ETH_SCK, ETH_MISO, ETH_MOSI, ETH_SS);
|
||||
|
||||
//SCLK = 18;
|
||||
//MISO = 19;
|
||||
// MOSI = 23;
|
||||
// SS = 5;
|
||||
//vspi->begin();
|
||||
//pinMode(LED_BUILTIN, OUTPUT);
|
||||
//Serial.println("LED_BUILTIN = " + String(LED_BUILTIN));
|
||||
|
||||
Serial.println("SCK = " + String(ETH_SCK));
|
||||
Serial.println("MISO = " + String(ETH_MISO));
|
||||
Serial.println("MOSI = " + String(ETH_MOSI));
|
||||
Serial.println("SS = " + String(ETH_SS));
|
||||
|
||||
|
||||
|
||||
|
||||
if (Ethernet.begin(mac)) { // Dynamic IP setup
|
||||
Serial.println("DHCP OK!");
|
||||
}else{
|
||||
Serial.println("Failed to configure Ethernet using DHCP");
|
||||
// Check for Ethernet hardware present
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
|
||||
while (true) {
|
||||
delay(1); // do nothing, no point running without Ethernet hardware
|
||||
}
|
||||
}
|
||||
if (Ethernet.linkStatus() == LinkOFF) {
|
||||
Serial.println("Ethernet cable is not connected.");
|
||||
}
|
||||
|
||||
IPAddress ip(MYIPADDR);
|
||||
IPAddress dns(MYDNS);
|
||||
IPAddress gw(MYGW);
|
||||
IPAddress sn(MYIPMASK);
|
||||
Ethernet.begin(mac, ip, dns, gw, sn);
|
||||
Serial.println("STATIC OK!");
|
||||
}
|
||||
delay(5000);
|
||||
|
||||
|
||||
Serial.print("Local IP : ");
|
||||
Serial.println(Ethernet.localIP());
|
||||
Serial.print("Subnet Mask : ");
|
||||
Serial.println(Ethernet.subnetMask());
|
||||
Serial.print("Gateway IP : ");
|
||||
Serial.println(Ethernet.gatewayIP());
|
||||
Serial.print("DNS Server : ");
|
||||
Serial.println(Ethernet.dnsServerIP());
|
||||
|
||||
Serial.println("Ethernet Successfully Initialized");
|
||||
// if you get a connection, report back via serial:
|
||||
/*if (client.connect(server, 80)) {
|
||||
Serial.println("Connected!");
|
||||
// Make a HTTP request:
|
||||
client.println("GET /get HTTP/1.1");
|
||||
client.println("Host: httpbin.org");
|
||||
client.println("Connection: close");
|
||||
client.println();
|
||||
} else {
|
||||
// if you didn't get a connection to the server:
|
||||
Serial.println("1 connection failed");
|
||||
}*/
|
||||
|
||||
if (client.connect("192.168.1.173", 5000)) {
|
||||
Serial.println("Connected!");
|
||||
// Make a HTTP request:
|
||||
// client.println("GET /get HTTP/1.1");
|
||||
// client.println("Host: 192.168.1.173");
|
||||
// client.println("Connection: close");
|
||||
// client.println();
|
||||
|
||||
Audio* audio = new Audio(ADMP441);
|
||||
|
||||
Serial.println("RECORDING");
|
||||
audio->Record();
|
||||
Serial.println("Recoding Complited Processing");
|
||||
|
||||
Transcribe(audio);
|
||||
|
||||
delete audio;
|
||||
} else {
|
||||
// if you didn't get a connection to the server:
|
||||
Serial.println("connection failed");
|
||||
}
|
||||
setInitialTime();
|
||||
recorder = new Recorder(ADMP441, &http);
|
||||
|
||||
beginMicros = micros();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// if there are incoming bytes available
|
||||
// from the server, read them and print them:
|
||||
int len = client.available();
|
||||
if (len > 0) {
|
||||
byte buffer[80];
|
||||
if (len > 80) len = 80;
|
||||
client.read(buffer, len);
|
||||
if (printWebData) {
|
||||
Serial.write(buffer, len); // show in the serial monitor (slows some boards)
|
||||
}
|
||||
byteCount = byteCount + len;
|
||||
}
|
||||
|
||||
// if the server's disconnected, stop the client:
|
||||
if (!client.connected()) {
|
||||
endMicros = micros();
|
||||
Serial.println();
|
||||
Serial.println("disconnecting.");
|
||||
client.stop();
|
||||
Serial.print("Received ");
|
||||
Serial.print(byteCount);
|
||||
Serial.print(" bytes in ");
|
||||
float seconds = (float)(endMicros - beginMicros) / 1000000.0;
|
||||
Serial.print(seconds, 4);
|
||||
float rate = (float)byteCount / seconds / 1000.0;
|
||||
Serial.print(", rate = ");
|
||||
Serial.print(rate);
|
||||
Serial.print(" kbytes/second");
|
||||
Serial.println();
|
||||
|
||||
// do nothing forevermore:
|
||||
while (true) {
|
||||
delay(1);
|
||||
}
|
||||
eth.readAndPrintData(true); // set to false for better speed measurement
|
||||
|
||||
if (recorder->isNoiseDetected(NOISE_THRESHOLD)) {
|
||||
recorder->recordAudio();
|
||||
}
|
||||
|
||||
//recorder->recordAudio();
|
||||
//delay(15000);
|
||||
|
||||
//eth.handleDisconnect(beginMicros, micros());
|
||||
}
|
||||
|
69
src/utils/Time.cpp
Normal file
69
src/utils/Time.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include <TimeLib.h>
|
||||
#include "utils/time.h"
|
||||
|
||||
unsigned long Time::_initialTime = 0;
|
||||
unsigned long Time::_initialMicros = 0;
|
||||
|
||||
Time::Time() {}
|
||||
|
||||
Time& Time::getInstance() {
|
||||
static Time instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void Time::setInitialTime(unsigned long time) {
|
||||
_initialTime = time;
|
||||
}
|
||||
|
||||
unsigned long Time::getInitialTime() const {
|
||||
return _initialTime;
|
||||
}
|
||||
|
||||
void Time::setInitialMicros(unsigned long time) {
|
||||
_initialMicros = time;
|
||||
}
|
||||
|
||||
unsigned long Time::getInitialMicros() const {
|
||||
return _initialMicros;
|
||||
}
|
||||
|
||||
unsigned long Time::getCurrentMillis() const {
|
||||
return millis();
|
||||
}
|
||||
|
||||
unsigned long Time::getCurrentTime() const {
|
||||
return _initialTime + this->getCurrentMillis() / 1000;
|
||||
}
|
||||
|
||||
unsigned long Time::getMillisDifference(unsigned long otherTimeMillis) const {
|
||||
return millis() - otherTimeMillis;
|
||||
}
|
||||
|
||||
String Time::getFormattedTime(unsigned long time) const {
|
||||
time_t currentTime = _initialTime + time;
|
||||
|
||||
struct tm *timeStruct = gmtime(¤tTime);
|
||||
|
||||
char buffer[20];
|
||||
snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d %02d:%02d:%02d",
|
||||
timeStruct->tm_year + 1900, timeStruct->tm_mon + 1, timeStruct->tm_mday,
|
||||
timeStruct->tm_hour, timeStruct->tm_min, timeStruct->tm_sec);
|
||||
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
String Time::getFormattedTimeAgo(unsigned long time) const {
|
||||
time_t currentTime = _initialTime + time;
|
||||
|
||||
struct tm *timeStruct = gmtime(¤tTime);
|
||||
|
||||
if (timeStruct->tm_yday > 0) {
|
||||
return String(timeStruct->tm_yday) + " days ago";
|
||||
} else if (timeStruct->tm_hour > 0) {
|
||||
return String(timeStruct->tm_hour) + " hours ago";
|
||||
} else if (timeStruct->tm_min > 0) {
|
||||
return String(timeStruct->tm_min) + " minutes ago";
|
||||
} else {
|
||||
return String(timeStruct->tm_sec) + " seconds ago";
|
||||
}
|
||||
}
|
22
src/utils/Time.h
Normal file
22
src/utils/Time.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class Time {
|
||||
public:
|
||||
static Time& getInstance();
|
||||
void setInitialTime(unsigned long time);
|
||||
unsigned long getInitialTime() const;
|
||||
void setInitialMicros(unsigned long time);
|
||||
unsigned long getInitialMicros() const;
|
||||
unsigned long getCurrentMillis() const;
|
||||
unsigned long getCurrentTime() const;
|
||||
unsigned long getMillisDifference(unsigned long otherTimeMillis) const;
|
||||
String getFormattedTime(unsigned long timeMillis) const;
|
||||
String getFormattedTimeAgo(unsigned long timeMillis) const;
|
||||
|
||||
private:
|
||||
Time();
|
||||
static unsigned long _initialTime;
|
||||
static unsigned long _initialMicros;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user