microReticulumTbeam/exercises/20_microphone/src/main.cpp

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);
}