137 lines
4.1 KiB
C++
137 lines
4.1 KiB
C++
// 20260413 ChatGPT
|
|
// Exercise 20: microphone recorder
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include "MicrophoneRecorder.h"
|
|
|
|
#ifndef NODE_LABEL
|
|
#define NODE_LABEL "MIC"
|
|
#endif
|
|
|
|
#ifndef MIC_RECORD_SECONDS
|
|
#define MIC_RECORD_SECONDS 30
|
|
#endif
|
|
|
|
#ifndef MIC_IDLE_SECONDS
|
|
#define MIC_IDLE_SECONDS 30
|
|
#endif
|
|
|
|
#ifndef MIC_STARTUP_WAIT_SECONDS
|
|
#define MIC_STARTUP_WAIT_SECONDS 10
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
constexpr uint32_t kSerialDelayMs = 1500;
|
|
constexpr uint32_t kBetweenRecordingsMs = MIC_IDLE_SECONDS * 1000UL;
|
|
|
|
MicrophoneRecorder g_recorder(Serial);
|
|
bool g_started = false;
|
|
bool g_finished = false;
|
|
size_t g_caseIndex = 0;
|
|
uint32_t g_nextActionMs = 0;
|
|
|
|
struct SweepCase {
|
|
const char* label;
|
|
MicrophoneRecorder::MicRuntimeConfig cfg;
|
|
};
|
|
|
|
SweepCase g_cases[] = {
|
|
{"base", {.dataPin = MIC_DATA_PIN, .clkPin = MIC_CLK_PIN, .selectPin = MIC_SELECT_PIN, .selectLeft = (MIC_SELECT_LEFT != 0)}},
|
|
{"flip_lr", {.dataPin = MIC_DATA_PIN, .clkPin = MIC_CLK_PIN, .selectPin = MIC_SELECT_PIN, .selectLeft = (MIC_SELECT_LEFT == 0)}},
|
|
{"swap_pins", {.dataPin = MIC_CLK_PIN, .clkPin = MIC_DATA_PIN, .selectPin = MIC_SELECT_PIN, .selectLeft = (MIC_SELECT_LEFT != 0)}},
|
|
{"swap_flip", {.dataPin = MIC_CLK_PIN, .clkPin = MIC_DATA_PIN, .selectPin = MIC_SELECT_PIN, .selectLeft = (MIC_SELECT_LEFT == 0)}},
|
|
};
|
|
|
|
void printWhatToSay(const SweepCase& sweepCase) {
|
|
Serial.println();
|
|
Serial.println("Speak this sentence clearly 3 times during the test:");
|
|
Serial.println("\"Testing T-Beam microphone case "
|
|
+ String(sweepCase.label)
|
|
+ ". One two three four five.\"");
|
|
Serial.println("Look for console lines where:");
|
|
Serial.println(" status=active");
|
|
Serial.println(" min and max change over time and are not equal");
|
|
Serial.println(" avgabs is not stuck at one repeated value");
|
|
Serial.println("If status=flat with min=max every second, that case is bad.");
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
delay(kSerialDelayMs);
|
|
|
|
Serial.println();
|
|
Serial.println("============================================================");
|
|
Serial.printf("Exercise 20 Microphone Recorder [%s]\r\n", NODE_LABEL);
|
|
Serial.println("============================================================");
|
|
|
|
if (!g_recorder.begin()) {
|
|
Serial.println("Recorder init failed; loop will retry.");
|
|
}
|
|
|
|
Serial.printf("Waiting %u seconds before the first recording.\r\n",
|
|
(unsigned)MIC_STARTUP_WAIT_SECONDS);
|
|
g_nextActionMs = millis() + MIC_STARTUP_WAIT_SECONDS * 1000UL;
|
|
}
|
|
|
|
void loop() {
|
|
if (!g_recorder.isSdReady() || g_finished) {
|
|
delay(1000);
|
|
return;
|
|
}
|
|
|
|
const uint32_t now = millis();
|
|
if ((int32_t)(now - g_nextActionMs) < 0) {
|
|
delay(100);
|
|
return;
|
|
}
|
|
|
|
if (g_caseIndex >= (sizeof(g_cases) / sizeof(g_cases[0]))) {
|
|
Serial.println("Microphone sweep complete. No more recordings will be started.");
|
|
g_finished = true;
|
|
return;
|
|
}
|
|
|
|
SweepCase& sweepCase = g_cases[g_caseIndex];
|
|
g_recorder.setRuntimeConfig(sweepCase.cfg);
|
|
|
|
char path[96];
|
|
snprintf(path,
|
|
sizeof(path),
|
|
"/recordings/mic_%s_%lu.wav",
|
|
sweepCase.label,
|
|
(unsigned long)time(nullptr));
|
|
|
|
Serial.println();
|
|
Serial.printf("Starting case %u/%u: %s data=%d clk=%d select=%d left=%d\r\n",
|
|
(unsigned)(g_caseIndex + 1),
|
|
(unsigned)(sizeof(g_cases) / sizeof(g_cases[0])),
|
|
sweepCase.label,
|
|
sweepCase.cfg.dataPin,
|
|
sweepCase.cfg.clkPin,
|
|
sweepCase.cfg.selectPin,
|
|
sweepCase.cfg.selectLeft ? 1 : 0);
|
|
printWhatToSay(sweepCase);
|
|
|
|
if (!g_recorder.open(path)) {
|
|
Serial.println("Failed to open output file; delaying and retrying.");
|
|
g_nextActionMs = millis() + 1000;
|
|
delay(100);
|
|
return;
|
|
}
|
|
|
|
const bool ok = g_recorder.captureForMs(MIC_RECORD_SECONDS * 1000UL);
|
|
g_recorder.close();
|
|
|
|
Serial.printf("case %s status=%s next_case_in=%lus\r\n",
|
|
sweepCase.label,
|
|
ok ? "ok" : "failed",
|
|
(unsigned long)(kBetweenRecordingsMs / 1000UL));
|
|
|
|
g_caseIndex++;
|
|
g_nextActionMs = millis() + kBetweenRecordingsMs;
|
|
delay(100);
|
|
}
|