Safety, testing Exercise 21 README internal linking on Forgejo
This commit is contained in:
parent
e5d30469bc
commit
1d0a29f2a3
18 changed files with 2098 additions and 1 deletions
3
exercises/20_microphone/README.md
Normal file
3
exercises/20_microphone/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
https://github.com/Xinyuan-LilyGO/LilyGo-LoRa-Series/issues/295#issuecomment-4241577603
|
||||
|
||||
tells us that the Specification as of April13, 2026, incorrectly includes a microphone. There is no microphone. The code here was ChatGPT's Codex trying to find the correct pins, a task that could never be accomplished. This exercise documents the attempts for posterity.
|
||||
453
exercises/20_microphone/lib/mic_recorder/MicrophoneRecorder.cpp
Normal file
453
exercises/20_microphone/lib/mic_recorder/MicrophoneRecorder.cpp
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
#include "MicrophoneRecorder.h"
|
||||
|
||||
#include <SPI.h>
|
||||
#include <Wire.h>
|
||||
#include <driver/i2s.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "tbeam_supreme_adapter.h"
|
||||
|
||||
#ifndef MIC_SAMPLE_RATE
|
||||
#define MIC_SAMPLE_RATE 16000
|
||||
#endif
|
||||
|
||||
#ifndef MIC_BITS_PER_SAMPLE
|
||||
#define MIC_BITS_PER_SAMPLE 16
|
||||
#endif
|
||||
|
||||
#ifndef MIC_DATA_PIN
|
||||
#define MIC_DATA_PIN 38
|
||||
#endif
|
||||
|
||||
#ifndef MIC_CLK_PIN
|
||||
#define MIC_CLK_PIN 39
|
||||
#endif
|
||||
|
||||
#ifndef MIC_SELECT_PIN
|
||||
#define MIC_SELECT_PIN 48
|
||||
#endif
|
||||
|
||||
#ifndef MIC_SELECT_LEFT
|
||||
#define MIC_SELECT_LEFT 1
|
||||
#endif
|
||||
|
||||
#ifndef FW_BUILD_EPOCH
|
||||
#define FW_BUILD_EPOCH 0
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr i2s_port_t kI2sPort = I2S_NUM_0;
|
||||
constexpr uint32_t kChunkBytes = 2048;
|
||||
constexpr uint8_t kWavChannels = 1;
|
||||
constexpr uint32_t kValidEpochFloor = 1700000000UL;
|
||||
|
||||
} // namespace
|
||||
|
||||
MicrophoneRecorder::MicrophoneRecorder(Print& out) : out_(out) {}
|
||||
|
||||
MicrophoneRecorder::~MicrophoneRecorder() {
|
||||
close();
|
||||
deactivateMicrophone();
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::begin() {
|
||||
micCfg_ = {
|
||||
.dataPin = MIC_DATA_PIN,
|
||||
.clkPin = MIC_CLK_PIN,
|
||||
.selectPin = MIC_SELECT_PIN,
|
||||
.selectLeft = (MIC_SELECT_LEFT != 0),
|
||||
};
|
||||
|
||||
forceSpiDeselected();
|
||||
pmuReady_ = initPmu();
|
||||
if (!pmuReady_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cycleSdRail();
|
||||
sdReady_ = mountSdCard();
|
||||
if (!sdReady_) {
|
||||
logf("SD mount failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ensureDirectory("/recordings")) {
|
||||
logf("failed to create /recordings");
|
||||
return false;
|
||||
}
|
||||
|
||||
logf("ready: sample_rate=%u bits=%u data=%d clk=%d sel=%d",
|
||||
(unsigned)MIC_SAMPLE_RATE,
|
||||
(unsigned)MIC_BITS_PER_SAMPLE,
|
||||
micCfg_.dataPin,
|
||||
micCfg_.clkPin,
|
||||
micCfg_.selectPin);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MicrophoneRecorder::setRuntimeConfig(const MicRuntimeConfig& cfg) {
|
||||
deactivateMicrophone();
|
||||
micCfg_ = cfg;
|
||||
logf("config updated: data=%d clk=%d select=%d left=%d",
|
||||
micCfg_.dataPin,
|
||||
micCfg_.clkPin,
|
||||
micCfg_.selectPin,
|
||||
micCfg_.selectLeft ? 1 : 0);
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::activateMicrophone() {
|
||||
if (micActive_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
pinMode(micCfg_.selectPin, OUTPUT);
|
||||
digitalWrite(micCfg_.selectPin, micCfg_.selectLeft ? LOW : HIGH);
|
||||
|
||||
const i2s_config_t config = {
|
||||
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
|
||||
.sample_rate = MIC_SAMPLE_RATE,
|
||||
.bits_per_sample = (i2s_bits_per_sample_t)MIC_BITS_PER_SAMPLE,
|
||||
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
|
||||
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
||||
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
.dma_buf_count = 8,
|
||||
.dma_buf_len = 256,
|
||||
.use_apll = false,
|
||||
.tx_desc_auto_clear = false,
|
||||
.fixed_mclk = 0,
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
.mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
|
||||
.bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
|
||||
#endif
|
||||
};
|
||||
|
||||
const i2s_pin_config_t pins = {
|
||||
.bck_io_num = I2S_PIN_NO_CHANGE,
|
||||
.ws_io_num = micCfg_.clkPin,
|
||||
.data_out_num = I2S_PIN_NO_CHANGE,
|
||||
.data_in_num = micCfg_.dataPin,
|
||||
};
|
||||
|
||||
i2s_driver_uninstall(kI2sPort);
|
||||
esp_err_t err = i2s_driver_install(kI2sPort, &config, 0, nullptr);
|
||||
if (err != ESP_OK) {
|
||||
logf("i2s_driver_install failed err=%d", (int)err);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = i2s_set_pin(kI2sPort, &pins);
|
||||
if (err != ESP_OK) {
|
||||
logf("i2s_set_pin failed err=%d", (int)err);
|
||||
i2s_driver_uninstall(kI2sPort);
|
||||
return false;
|
||||
}
|
||||
|
||||
err = i2s_set_pdm_rx_down_sample(kI2sPort, I2S_PDM_DSR_8S);
|
||||
if (err != ESP_OK) {
|
||||
logf("i2s_set_pdm_rx_down_sample failed err=%d", (int)err);
|
||||
i2s_driver_uninstall(kI2sPort);
|
||||
return false;
|
||||
}
|
||||
|
||||
i2s_zero_dma_buffer(kI2sPort);
|
||||
micActive_ = true;
|
||||
logf("microphone active clk(ws)=%d data=%d select=%d left=%d",
|
||||
micCfg_.clkPin,
|
||||
micCfg_.dataPin,
|
||||
micCfg_.selectPin,
|
||||
micCfg_.selectLeft ? 1 : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MicrophoneRecorder::deactivateMicrophone() {
|
||||
if (!micActive_) {
|
||||
return;
|
||||
}
|
||||
|
||||
i2s_driver_uninstall(kI2sPort);
|
||||
micActive_ = false;
|
||||
logf("microphone inactive");
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::open(const char* path) {
|
||||
if (!sdReady_) {
|
||||
logf("open refused: SD not ready");
|
||||
return false;
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
strncpy(currentPath_, path, sizeof(currentPath_) - 1);
|
||||
currentPath_[sizeof(currentPath_) - 1] = '\0';
|
||||
|
||||
file_ = SD.open(currentPath_, FILE_WRITE);
|
||||
if (!file_) {
|
||||
logf("failed to open %s", currentPath_);
|
||||
currentPath_[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
wavDataBytes_ = 0;
|
||||
fileOpen_ = writeWavHeader();
|
||||
if (!fileOpen_) {
|
||||
file_.close();
|
||||
currentPath_[0] = '\0';
|
||||
return false;
|
||||
}
|
||||
|
||||
logf("opened %s", currentPath_);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::openTimestamped() {
|
||||
const uint32_t stamp = selectTimestamp();
|
||||
char path[96];
|
||||
snprintf(path, sizeof(path), "/recordings/mic_%lu.wav", (unsigned long)stamp);
|
||||
return open(path);
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::captureForMs(uint32_t durationMs) {
|
||||
if (!fileOpen_) {
|
||||
logf("capture refused: no open file");
|
||||
return false;
|
||||
}
|
||||
if (!activateMicrophone()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t samples[kChunkBytes / sizeof(int16_t)];
|
||||
const uint32_t startMs = millis();
|
||||
uint32_t lastProgressMs = startMs;
|
||||
int16_t secondMin = INT16_MAX;
|
||||
int16_t secondMax = INT16_MIN;
|
||||
uint32_t secondSampleCount = 0;
|
||||
uint64_t secondAbsSum = 0;
|
||||
|
||||
while ((uint32_t)(millis() - startMs) < durationMs) {
|
||||
size_t bytesRead = 0;
|
||||
const esp_err_t err =
|
||||
i2s_read(kI2sPort, samples, sizeof(samples), &bytesRead, pdMS_TO_TICKS(250));
|
||||
if (err != ESP_OK) {
|
||||
logf("i2s_read failed err=%d", (int)err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytesRead > 0) {
|
||||
const size_t sampleCount = bytesRead / sizeof(samples[0]);
|
||||
for (size_t i = 0; i < sampleCount; ++i) {
|
||||
const int16_t value = samples[i];
|
||||
if (value < secondMin) secondMin = value;
|
||||
if (value > secondMax) secondMax = value;
|
||||
secondAbsSum += (uint64_t)(value < 0 ? -(int32_t)value : value);
|
||||
}
|
||||
secondSampleCount += sampleCount;
|
||||
|
||||
const size_t wrote = file_.write((const uint8_t*)samples, bytesRead);
|
||||
if (wrote != bytesRead) {
|
||||
logf("file write failed wanted=%u wrote=%u", (unsigned)bytesRead, (unsigned)wrote);
|
||||
return false;
|
||||
}
|
||||
wavDataBytes_ += wrote;
|
||||
}
|
||||
|
||||
const uint32_t now = millis();
|
||||
if ((uint32_t)(now - lastProgressMs) >= 1000) {
|
||||
lastProgressMs = now;
|
||||
const unsigned long avgAbs =
|
||||
secondSampleCount > 0 ? (unsigned long)(secondAbsSum / secondSampleCount) : 0UL;
|
||||
const char* status =
|
||||
(secondSampleCount == 0) ? "nosamples"
|
||||
: (secondMin == secondMax) ? "flat"
|
||||
: (avgAbs < 8UL) ? "nearzero"
|
||||
: "active";
|
||||
logf("recording %s elapsed=%lus bytes=%lu min=%d max=%d avgabs=%lu samples=%lu status=%s",
|
||||
currentPath_,
|
||||
(unsigned long)((now - startMs) / 1000),
|
||||
(unsigned long)wavDataBytes_,
|
||||
secondSampleCount > 0 ? secondMin : 0,
|
||||
secondSampleCount > 0 ? secondMax : 0,
|
||||
avgAbs,
|
||||
(unsigned long)secondSampleCount,
|
||||
status);
|
||||
secondMin = INT16_MAX;
|
||||
secondMax = INT16_MIN;
|
||||
secondSampleCount = 0;
|
||||
secondAbsSum = 0;
|
||||
}
|
||||
}
|
||||
|
||||
file_.flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MicrophoneRecorder::close() {
|
||||
if (!fileOpen_) {
|
||||
return;
|
||||
}
|
||||
|
||||
finalizeWavHeader();
|
||||
file_.close();
|
||||
fileOpen_ = false;
|
||||
logf("closed %s bytes=%lu", currentPath_, (unsigned long)wavDataBytes_);
|
||||
currentPath_[0] = '\0';
|
||||
}
|
||||
|
||||
void MicrophoneRecorder::logf(const char* fmt, ...) {
|
||||
char msg[200];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
out_.printf("[%10lu][%06lu] %s\r\n",
|
||||
(unsigned long)millis(),
|
||||
(unsigned long)logSeq_++,
|
||||
msg);
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::initPmu() {
|
||||
if (!tbeam_supreme::initPmuForPeripherals(pmu_, &out_)) {
|
||||
logf("PMU init failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void MicrophoneRecorder::forceSpiDeselected() {
|
||||
pinMode(tbeam_supreme::sdCs(), OUTPUT);
|
||||
digitalWrite(tbeam_supreme::sdCs(), HIGH);
|
||||
pinMode(tbeam_supreme::imuCs(), OUTPUT);
|
||||
digitalWrite(tbeam_supreme::imuCs(), HIGH);
|
||||
}
|
||||
|
||||
void MicrophoneRecorder::cycleSdRail(uint32_t offMs, uint32_t onSettleMs) {
|
||||
if (!pmu_) {
|
||||
return;
|
||||
}
|
||||
|
||||
forceSpiDeselected();
|
||||
SD.end();
|
||||
sdSpiH_.end();
|
||||
sdSpiF_.end();
|
||||
|
||||
pmu_->disablePowerOutput(XPOWERS_BLDO1);
|
||||
delay(offMs);
|
||||
pmu_->setPowerChannelVoltage(XPOWERS_BLDO1, 3300);
|
||||
pmu_->enablePowerOutput(XPOWERS_BLDO1);
|
||||
delay(onSettleMs);
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::mountSdCard() {
|
||||
const uint32_t freqs[] = {400000, 1000000, 4000000, 10000000};
|
||||
|
||||
for (uint8_t i = 0; i < (sizeof(freqs) / sizeof(freqs[0])); ++i) {
|
||||
if (tryMountWithBus(sdSpiH_, "HSPI", freqs[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 0; i < (sizeof(freqs) / sizeof(freqs[0])); ++i) {
|
||||
if (tryMountWithBus(sdSpiF_, "FSPI", freqs[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::tryMountWithBus(SPIClass& bus, const char* busName, uint32_t hz) {
|
||||
SD.end();
|
||||
bus.end();
|
||||
delay(5);
|
||||
forceSpiDeselected();
|
||||
|
||||
bus.begin(tbeam_supreme::sdSck(),
|
||||
tbeam_supreme::sdMiso(),
|
||||
tbeam_supreme::sdMosi(),
|
||||
tbeam_supreme::sdCs());
|
||||
digitalWrite(tbeam_supreme::sdCs(), HIGH);
|
||||
delay(1);
|
||||
for (uint8_t i = 0; i < 10; ++i) {
|
||||
bus.transfer(0xFF);
|
||||
}
|
||||
|
||||
if (!SD.begin(tbeam_supreme::sdCs(), bus, hz)) {
|
||||
return false;
|
||||
}
|
||||
if (SD.cardType() == CARD_NONE) {
|
||||
SD.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
activeSdSpi_ = &bus;
|
||||
logf("SD mounted via %s @ %lu Hz", busName, (unsigned long)hz);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::ensureDirectory(const char* path) {
|
||||
if (SD.exists(path)) {
|
||||
return true;
|
||||
}
|
||||
return SD.mkdir(path);
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::writeWavHeader() {
|
||||
const WavHeader header = {
|
||||
.riff = {'R', 'I', 'F', 'F'},
|
||||
.chunkSize = 36,
|
||||
.wave = {'W', 'A', 'V', 'E'},
|
||||
.fmt = {'f', 'm', 't', ' '},
|
||||
.subchunk1Size = 16,
|
||||
.audioFormat = 1,
|
||||
.numChannels = kWavChannels,
|
||||
.sampleRate = MIC_SAMPLE_RATE,
|
||||
.byteRate = MIC_SAMPLE_RATE * kWavChannels * (MIC_BITS_PER_SAMPLE / 8),
|
||||
.blockAlign = (uint16_t)(kWavChannels * (MIC_BITS_PER_SAMPLE / 8)),
|
||||
.bitsPerSample = MIC_BITS_PER_SAMPLE,
|
||||
.data = {'d', 'a', 't', 'a'},
|
||||
.dataSize = 0,
|
||||
};
|
||||
|
||||
return file_.write((const uint8_t*)&header, sizeof(header)) == sizeof(header);
|
||||
}
|
||||
|
||||
bool MicrophoneRecorder::finalizeWavHeader() {
|
||||
if (!file_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
WavHeader header = {
|
||||
.riff = {'R', 'I', 'F', 'F'},
|
||||
.chunkSize = 36 + wavDataBytes_,
|
||||
.wave = {'W', 'A', 'V', 'E'},
|
||||
.fmt = {'f', 'm', 't', ' '},
|
||||
.subchunk1Size = 16,
|
||||
.audioFormat = 1,
|
||||
.numChannels = kWavChannels,
|
||||
.sampleRate = MIC_SAMPLE_RATE,
|
||||
.byteRate = MIC_SAMPLE_RATE * kWavChannels * (MIC_BITS_PER_SAMPLE / 8),
|
||||
.blockAlign = (uint16_t)(kWavChannels * (MIC_BITS_PER_SAMPLE / 8)),
|
||||
.bitsPerSample = MIC_BITS_PER_SAMPLE,
|
||||
.data = {'d', 'a', 't', 'a'},
|
||||
.dataSize = wavDataBytes_,
|
||||
};
|
||||
|
||||
file_.seek(0);
|
||||
const bool ok = file_.write((const uint8_t*)&header, sizeof(header)) == sizeof(header);
|
||||
file_.seek(file_.size());
|
||||
return ok;
|
||||
}
|
||||
|
||||
uint32_t MicrophoneRecorder::selectTimestamp() const {
|
||||
const time_t now = time(nullptr);
|
||||
if (now >= (time_t)kValidEpochFloor) {
|
||||
return (uint32_t)now;
|
||||
}
|
||||
if (FW_BUILD_EPOCH >= kValidEpochFloor) {
|
||||
return (uint32_t)FW_BUILD_EPOCH + (millis() / 1000UL);
|
||||
}
|
||||
return millis() / 1000UL;
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SD.h>
|
||||
#include <SPI.h>
|
||||
#include <XPowersLib.h>
|
||||
|
||||
class MicrophoneRecorder {
|
||||
public:
|
||||
struct MicRuntimeConfig {
|
||||
int dataPin;
|
||||
int clkPin;
|
||||
int selectPin;
|
||||
bool selectLeft;
|
||||
};
|
||||
|
||||
explicit MicrophoneRecorder(Print& out = Serial);
|
||||
~MicrophoneRecorder();
|
||||
|
||||
bool begin();
|
||||
void setRuntimeConfig(const MicRuntimeConfig& cfg);
|
||||
const MicRuntimeConfig& runtimeConfig() const { return micCfg_; }
|
||||
bool activateMicrophone();
|
||||
void deactivateMicrophone();
|
||||
|
||||
bool open(const char* path);
|
||||
bool openTimestamped();
|
||||
bool captureForMs(uint32_t durationMs);
|
||||
void close();
|
||||
|
||||
bool isSdReady() const { return sdReady_; }
|
||||
bool isMicActive() const { return micActive_; }
|
||||
bool isOpen() const { return fileOpen_; }
|
||||
const char* currentPath() const { return currentPath_; }
|
||||
|
||||
private:
|
||||
struct WavHeader {
|
||||
char riff[4];
|
||||
uint32_t chunkSize;
|
||||
char wave[4];
|
||||
char fmt[4];
|
||||
uint32_t subchunk1Size;
|
||||
uint16_t audioFormat;
|
||||
uint16_t numChannels;
|
||||
uint32_t sampleRate;
|
||||
uint32_t byteRate;
|
||||
uint16_t blockAlign;
|
||||
uint16_t bitsPerSample;
|
||||
char data[4];
|
||||
uint32_t dataSize;
|
||||
};
|
||||
|
||||
void logf(const char* fmt, ...);
|
||||
bool initPmu();
|
||||
void forceSpiDeselected();
|
||||
void cycleSdRail(uint32_t offMs = 250, uint32_t onSettleMs = 700);
|
||||
bool mountSdCard();
|
||||
bool tryMountWithBus(SPIClass& bus, const char* busName, uint32_t hz);
|
||||
bool ensureDirectory(const char* path);
|
||||
bool writeWavHeader();
|
||||
bool finalizeWavHeader();
|
||||
uint32_t selectTimestamp() const;
|
||||
|
||||
Print& out_;
|
||||
SPIClass sdSpiH_{HSPI};
|
||||
SPIClass sdSpiF_{FSPI};
|
||||
SPIClass* activeSdSpi_ = nullptr;
|
||||
XPowersLibInterface* pmu_ = nullptr;
|
||||
File file_;
|
||||
char currentPath_[96] = {0};
|
||||
uint32_t logSeq_ = 0;
|
||||
uint32_t wavDataBytes_ = 0;
|
||||
bool pmuReady_ = false;
|
||||
bool sdReady_ = false;
|
||||
bool micActive_ = false;
|
||||
bool fileOpen_ = false;
|
||||
MicRuntimeConfig micCfg_{};
|
||||
};
|
||||
7
exercises/20_microphone/lib/mic_recorder/library.json
Normal file
7
exercises/20_microphone/lib/mic_recorder/library.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "mic_recorder",
|
||||
"version": "0.1.0",
|
||||
"build": {
|
||||
"includeDir": "."
|
||||
}
|
||||
}
|
||||
81
exercises/20_microphone/platformio.ini
Normal file
81
exercises/20_microphone/platformio.ini
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
; 20260413 ChatGPT
|
||||
; Exercise 20_microphone
|
||||
|
||||
[platformio]
|
||||
default_envs = guy
|
||||
|
||||
[env]
|
||||
platform = espressif32
|
||||
framework = arduino
|
||||
board = esp32-s3-devkitc-1
|
||||
board_build.partitions = default_8MB.csv
|
||||
monitor_speed = 115200
|
||||
extra_scripts = pre:scripts/set_build_epoch.py
|
||||
lib_deps =
|
||||
Wire
|
||||
olikraus/U8g2@^2.36.4
|
||||
lewisxhe/XPowersLib@0.3.3
|
||||
|
||||
build_flags =
|
||||
-I ../../shared/boards
|
||||
-I ../../external/microReticulum_Firmware
|
||||
-D BOARD_MODEL=BOARD_TBEAM_S_V1
|
||||
-D ARDUINO_USB_MODE=1
|
||||
-D ARDUINO_USB_CDC_ON_BOOT=1
|
||||
-D MIC_SAMPLE_RATE=16000
|
||||
-D MIC_BITS_PER_SAMPLE=16
|
||||
-D MIC_RECORD_SECONDS=30
|
||||
-D MIC_IDLE_SECONDS=30
|
||||
-D MIC_DATA_PIN=39
|
||||
-D MIC_CLK_PIN=38
|
||||
-D MIC_SELECT_PIN=48
|
||||
-D MIC_SELECT_LEFT=1
|
||||
|
||||
[env:amy]
|
||||
extends = env
|
||||
build_flags =
|
||||
${env.build_flags}
|
||||
-D BOARD_ID=\"AMY\"
|
||||
-D NODE_LABEL=\"Amy\"
|
||||
|
||||
[env:bob]
|
||||
extends = env
|
||||
build_flags =
|
||||
${env.build_flags}
|
||||
-D BOARD_ID=\"BOB\"
|
||||
-D NODE_LABEL=\"Bob\"
|
||||
|
||||
[env:cy]
|
||||
extends = env
|
||||
build_flags =
|
||||
${env.build_flags}
|
||||
-D BOARD_ID=\"CY\"
|
||||
-D NODE_LABEL=\"Cy\"
|
||||
|
||||
[env:dan]
|
||||
extends = env
|
||||
build_flags =
|
||||
${env.build_flags}
|
||||
-D BOARD_ID=\"DAN\"
|
||||
-D NODE_LABEL=\"Dan\"
|
||||
|
||||
[env:ed]
|
||||
extends = env
|
||||
build_flags =
|
||||
${env.build_flags}
|
||||
-D BOARD_ID=\"ED\"
|
||||
-D NODE_LABEL=\"Ed\"
|
||||
|
||||
[env:flo]
|
||||
extends = env
|
||||
build_flags =
|
||||
${env.build_flags}
|
||||
-D BOARD_ID=\"FLO\"
|
||||
-D NODE_LABEL=\"Flo\"
|
||||
|
||||
[env:guy]
|
||||
extends = env
|
||||
build_flags =
|
||||
${env.build_flags}
|
||||
-D BOARD_ID=\"GUY\"
|
||||
-D NODE_LABEL=\"Guy\"
|
||||
13
exercises/20_microphone/scripts/set_build_epoch.py
Normal file
13
exercises/20_microphone/scripts/set_build_epoch.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import time
|
||||
Import("env")
|
||||
|
||||
epoch = int(time.time())
|
||||
utc_tag = time.strftime("%Y%m%d_%H%M%S_z", time.gmtime(epoch))
|
||||
|
||||
env.Append(
|
||||
CPPDEFINES=[
|
||||
("FW_BUILD_EPOCH", str(epoch)),
|
||||
("FW_BUILD_UTC", '\"%s\"' % utc_tag),
|
||||
]
|
||||
)
|
||||
|
||||
137
exercises/20_microphone/src/main.cpp
Normal file
137
exercises/20_microphone/src/main.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// 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);
|
||||
}
|
||||
172
exercises/20_microphone/trial_1_console.log
Normal file
172
exercises/20_microphone/trial_1_console.log
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
(rnsenv) jlpoole@jp ~/workstation $ date; pio device monitor -b 115200 --port /dev/ttytCY
|
||||
Mon Apr 13 20:32:15 PDT 2026
|
||||
/home/jlpoole/rnsenv/lib/python3.13/site-packages/requests/__init__.py:113: RequestsDependencyWarning: urllib3 (2.6.1) or chardet (6.0.0.post1)/charset_normalizer (3.4.4) doesn't match a supported version!
|
||||
warnings.warn(
|
||||
--- Terminal on /dev/ttytCY | 115200 8-N-1
|
||||
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
|
||||
--- More details at https://bit.ly/pio-monitor-filters
|
||||
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
|
||||
[ 13073][000002] config updated: data=39 clk=38 select=48 left=1
|
||||
|
||||
Starting case 1/4: base data=39 clk=38 select=48 left=1
|
||||
|
||||
Speak this sentence clearly 3 times during the test:
|
||||
"Testing T-Beam microphone case base. One two three four five."
|
||||
Look for console lines where:
|
||||
status=active
|
||||
min and max change over time and are not equal
|
||||
avgabs is not stuck at one repeated value
|
||||
If status=flat with min=max every second, that case is bad.
|
||||
[ 13152][000003] opened /recordings/mic_base_19.wav
|
||||
E (13037) I2S: i2s_driver_uninstall(2048): I2S port 0 has not installed
|
||||
[ 13153][000004] microphone active clk(ws)=38 data=39 select=48 left=1
|
||||
[ 14158][000005] recording /recordings/mic_base_19.wav elapsed=1s bytes=30720 min=-31424 max=41 avgabs=30930 samples=15360 status=active
|
||||
[ 15182][000006] recording /recordings/mic_base_19.wav elapsed=2s bytes=63488 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 16206][000007] recording /recordings/mic_base_19.wav elapsed=3s bytes=96256 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 17230][000008] recording /recordings/mic_base_19.wav elapsed=4s bytes=129024 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 18254][000009] recording /recordings/mic_base_19.wav elapsed=5s bytes=161792 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 19278][000010] recording /recordings/mic_base_19.wav elapsed=6s bytes=194560 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 20302][000011] recording /recordings/mic_base_19.wav elapsed=7s bytes=227328 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 21326][000012] recording /recordings/mic_base_19.wav elapsed=8s bytes=260096 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 22350][000013] recording /recordings/mic_base_19.wav elapsed=9s bytes=292864 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 23374][000014] recording /recordings/mic_base_19.wav elapsed=10s bytes=325632 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 24398][000015] recording /recordings/mic_base_19.wav elapsed=11s bytes=358400 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 25422][000016] recording /recordings/mic_base_19.wav elapsed=12s bytes=391168 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 26446][000017] recording /recordings/mic_base_19.wav elapsed=13s bytes=423936 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 27470][000018] recording /recordings/mic_base_19.wav elapsed=14s bytes=456704 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 28494][000019] recording /recordings/mic_base_19.wav elapsed=15s bytes=489472 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 29518][000020] recording /recordings/mic_base_19.wav elapsed=16s bytes=522240 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 30542][000021] recording /recordings/mic_base_19.wav elapsed=17s bytes=555008 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 31566][000022] recording /recordings/mic_base_19.wav elapsed=18s bytes=587776 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 32590][000023] recording /recordings/mic_base_19.wav elapsed=19s bytes=620544 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 33614][000024] recording /recordings/mic_base_19.wav elapsed=20s bytes=653312 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 34638][000025] recording /recordings/mic_base_19.wav elapsed=21s bytes=686080 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 35662][000026] recording /recordings/mic_base_19.wav elapsed=22s bytes=718848 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 36686][000027] recording /recordings/mic_base_19.wav elapsed=23s bytes=751616 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 37710][000028] recording /recordings/mic_base_19.wav elapsed=24s bytes=784384 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 38734][000029] recording /recordings/mic_base_19.wav elapsed=25s bytes=817152 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 39758][000030] recording /recordings/mic_base_19.wav elapsed=26s bytes=849920 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 40782][000031] recording /recordings/mic_base_19.wav elapsed=27s bytes=882688 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 41806][000032] recording /recordings/mic_base_19.wav elapsed=28s bytes=915456 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 42830][000033] recording /recordings/mic_base_19.wav elapsed=29s bytes=948224 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 43375][000034] closed /recordings/mic_base_19.wav bytes=960512
|
||||
case base status=ok next_case_in=30s
|
||||
[ 73375][000035] microphone inactive
|
||||
[ 73376][000036] config updated: data=39 clk=38 select=48 left=0
|
||||
|
||||
Starting case 2/4: flip_lr data=39 clk=38 select=48 left=0
|
||||
|
||||
Speak this sentence clearly 3 times during the test:
|
||||
"Testing T-Beam microphone case flip_lr. One two three four five."
|
||||
Look for console lines where:
|
||||
status=active
|
||||
min and max change over time and are not equal
|
||||
avgabs is not stuck at one repeated value
|
||||
If status=flat with min=max every second, that case is bad.
|
||||
[ 73465][000037] opened /recordings/mic_flip_lr_79.wav
|
||||
E (73350) I2S: i2s_driver_uninstall(2048): I2S port 0 has not installed
|
||||
[ 73466][000038] microphone active clk(ws)=38 data=39 select=48 left=0
|
||||
[ 74471][000039] recording /recordings/mic_flip_lr_79.wav elapsed=1s bytes=30720 min=-32768 max=25661 avgabs=30858 samples=15360 status=active
|
||||
[ 75495][000040] recording /recordings/mic_flip_lr_79.wav elapsed=2s bytes=63488 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 76519][000041] recording /recordings/mic_flip_lr_79.wav elapsed=3s bytes=96256 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 77543][000042] recording /recordings/mic_flip_lr_79.wav elapsed=4s bytes=129024 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 78567][000043] recording /recordings/mic_flip_lr_79.wav elapsed=5s bytes=161792 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 79591][000044] recording /recordings/mic_flip_lr_79.wav elapsed=6s bytes=194560 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 80615][000045] recording /recordings/mic_flip_lr_79.wav elapsed=7s bytes=227328 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 81639][000046] recording /recordings/mic_flip_lr_79.wav elapsed=8s bytes=260096 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 82663][000047] recording /recordings/mic_flip_lr_79.wav elapsed=9s bytes=292864 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 83687][000048] recording /recordings/mic_flip_lr_79.wav elapsed=10s bytes=325632 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 84711][000049] recording /recordings/mic_flip_lr_79.wav elapsed=11s bytes=358400 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 85735][000050] recording /recordings/mic_flip_lr_79.wav elapsed=12s bytes=391168 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 86759][000051] recording /recordings/mic_flip_lr_79.wav elapsed=13s bytes=423936 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 87783][000052] recording /recordings/mic_flip_lr_79.wav elapsed=14s bytes=456704 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 88807][000053] recording /recordings/mic_flip_lr_79.wav elapsed=15s bytes=489472 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 89831][000054] recording /recordings/mic_flip_lr_79.wav elapsed=16s bytes=522240 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 90855][000055] recording /recordings/mic_flip_lr_79.wav elapsed=17s bytes=555008 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 91879][000056] recording /recordings/mic_flip_lr_79.wav elapsed=18s bytes=587776 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 92903][000057] recording /recordings/mic_flip_lr_79.wav elapsed=19s bytes=620544 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 93927][000058] recording /recordings/mic_flip_lr_79.wav elapsed=20s bytes=653312 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 94951][000059] recording /recordings/mic_flip_lr_79.wav elapsed=21s bytes=686080 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 95975][000060] recording /recordings/mic_flip_lr_79.wav elapsed=22s bytes=718848 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 96999][000061] recording /recordings/mic_flip_lr_79.wav elapsed=23s bytes=751616 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 98023][000062] recording /recordings/mic_flip_lr_79.wav elapsed=24s bytes=784384 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 99047][000063] recording /recordings/mic_flip_lr_79.wav elapsed=25s bytes=817152 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 100071][000064] recording /recordings/mic_flip_lr_79.wav elapsed=26s bytes=849920 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 101095][000065] recording /recordings/mic_flip_lr_79.wav elapsed=27s bytes=882688 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 102119][000066] recording /recordings/mic_flip_lr_79.wav elapsed=28s bytes=915456 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 103143][000067] recording /recordings/mic_flip_lr_79.wav elapsed=29s bytes=948224 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 103689][000068] closed /recordings/mic_flip_lr_79.wav bytes=960512
|
||||
case flip_lr status=ok next_case_in=30s
|
||||
[ 133689][000069] microphone inactive
|
||||
[ 133690][000070] config updated: data=38 clk=39 select=48 left=1
|
||||
|
||||
Starting case 3/4: swap_pins data=38 clk=39 select=48 left=1
|
||||
|
||||
Speak this sentence clearly 3 times during the test:
|
||||
"Testing T-Beam microphone case swap_pins. One two three four five."
|
||||
Look for console lines where:
|
||||
status=active
|
||||
min and max change over time and are not equal
|
||||
avgabs is not stuck at one repeated value
|
||||
If status=flat with min=max every second, that case is bad.
|
||||
[ 133779][000071] opened /recordings/mic_swap_pins_139.wav
|
||||
E (133664) I2S: i2s_driver_uninstall(2048): I2S port 0 has not installed
|
||||
[ 133780][000072] microphone active clk(ws)=39 data=38 select=48 left=1
|
||||
[ 134785][000073] recording /recordings/mic_swap_pins_139.wav elapsed=1s bytes=30720 min=-32768 max=0 avgabs=30931 samples=15360 status=active
|
||||
[ 135809][000074] recording /recordings/mic_swap_pins_139.wav elapsed=2s bytes=63488 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 136833][000075] recording /recordings/mic_swap_pins_139.wav elapsed=3s bytes=96256 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 137857][000076] recording /recordings/mic_swap_pins_139.wav elapsed=4s bytes=129024 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 137922][000077] file write failed wanted=2048 wrote=468
|
||||
[ 137993][000078] closed /recordings/mic_swap_pins_139.wav bytes=129024
|
||||
case swap_pins status=failed next_case_in=30s
|
||||
[ 167993][000079] microphone inactive
|
||||
[ 167994][000080] config updated: data=38 clk=39 select=48 left=0
|
||||
|
||||
Starting case 4/4: swap_flip data=38 clk=39 select=48 left=0
|
||||
|
||||
Speak this sentence clearly 3 times during the test:
|
||||
"Testing T-Beam microphone case swap_flip. One two three four five."
|
||||
Look for console lines where:
|
||||
status=active
|
||||
min and max change over time and are not equal
|
||||
avgabs is not stuck at one repeated value
|
||||
If status=flat with min=max every second, that case is bad.
|
||||
[ 168083][000081] opened /recordings/mic_swap_flip_174.wav
|
||||
E (167968) I2S: i2s_driver_uninstall(2048): I2S port 0 has not installed
|
||||
[ 168084][000082] microphone active clk(ws)=39 data=38 select=48 left=0
|
||||
[ 169089][000083] recording /recordings/mic_swap_flip_174.wav elapsed=1s bytes=30720 min=-32768 max=0 avgabs=30931 samples=15360 status=active
|
||||
[ 170113][000084] recording /recordings/mic_swap_flip_174.wav elapsed=2s bytes=63488 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 171137][000085] recording /recordings/mic_swap_flip_174.wav elapsed=3s bytes=96256 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 172161][000086] recording /recordings/mic_swap_flip_174.wav elapsed=4s bytes=129024 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 173185][000087] recording /recordings/mic_swap_flip_174.wav elapsed=5s bytes=161792 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 174209][000088] recording /recordings/mic_swap_flip_174.wav elapsed=6s bytes=194560 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 175233][000089] recording /recordings/mic_swap_flip_174.wav elapsed=7s bytes=227328 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 176257][000090] recording /recordings/mic_swap_flip_174.wav elapsed=8s bytes=260096 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 177281][000091] recording /recordings/mic_swap_flip_174.wav elapsed=9s bytes=292864 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 178305][000092] recording /recordings/mic_swap_flip_174.wav elapsed=10s bytes=325632 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 179329][000093] recording /recordings/mic_swap_flip_174.wav elapsed=11s bytes=358400 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 180353][000094] recording /recordings/mic_swap_flip_174.wav elapsed=12s bytes=391168 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 181377][000095] recording /recordings/mic_swap_flip_174.wav elapsed=13s bytes=423936 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 182401][000096] recording /recordings/mic_swap_flip_174.wav elapsed=14s bytes=456704 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 183425][000097] recording /recordings/mic_swap_flip_174.wav elapsed=15s bytes=489472 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 184449][000098] recording /recordings/mic_swap_flip_174.wav elapsed=16s bytes=522240 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 185473][000099] recording /recordings/mic_swap_flip_174.wav elapsed=17s bytes=555008 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 186497][000100] recording /recordings/mic_swap_flip_174.wav elapsed=18s bytes=587776 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 187521][000101] recording /recordings/mic_swap_flip_174.wav elapsed=19s bytes=620544 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 188545][000102] recording /recordings/mic_swap_flip_174.wav elapsed=20s bytes=653312 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 189569][000103] recording /recordings/mic_swap_flip_174.wav elapsed=21s bytes=686080 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 190593][000104] recording /recordings/mic_swap_flip_174.wav elapsed=22s bytes=718848 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 191617][000105] recording /recordings/mic_swap_flip_174.wav elapsed=23s bytes=751616 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 192641][000106] recording /recordings/mic_swap_flip_174.wav elapsed=24s bytes=784384 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 193665][000107] recording /recordings/mic_swap_flip_174.wav elapsed=25s bytes=817152 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 194689][000108] recording /recordings/mic_swap_flip_174.wav elapsed=26s bytes=849920 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 195713][000109] recording /recordings/mic_swap_flip_174.wav elapsed=27s bytes=882688 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 196737][000110] recording /recordings/mic_swap_flip_174.wav elapsed=28s bytes=915456 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 197761][000111] recording /recordings/mic_swap_flip_174.wav elapsed=29s bytes=948224 min=-30935 max=-30935 avgabs=30935 samples=16384 status=flat
|
||||
[ 198306][000112] closed /recordings/mic_swap_flip_174.wav bytes=960512
|
||||
case swap_flip status=ok next_case_in=30s
|
||||
Disconnected ([Errno 5] Input/output error)
|
||||
Reconnecting to /dev/ttytCY ..^C
|
||||
(rnsenv) jlpoole@jp ~/workstation $ date
|
||||
Mon Apr 13 20:37:09 PDT 2026
|
||||
(rnsenv) jlpoole@jp ~/workstation $
|
||||
Loading…
Add table
Add a link
Reference in a new issue