From c31b1908a786a638434c795e64145b1e346927da Mon Sep 17 00:00:00 2001 From: GlassOnTin Date: Fri, 27 Mar 2026 19:08:26 +0000 Subject: [PATCH] Add SPM1423 PDM microphone driver, fix speaker I2S port Microphone on I2S_NUM_0 (PDM hardware constraint), speaker moved to I2S_NUM_1. Both init at boot, shut down before deep sleep. Mic provides raw audio read and RMS level measurement. Boot beep disabled (speaker confirmed working). --- Microphone.h | 82 ++++++++++++++++++++++++++++++++++++++++++++++ RNode_Firmware.ino | 8 +++-- Speaker.h | 2 +- 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 Microphone.h diff --git a/Microphone.h b/Microphone.h new file mode 100644 index 0000000..2ab9787 --- /dev/null +++ b/Microphone.h @@ -0,0 +1,82 @@ +// SPM1423 PDM Microphone Driver — Minimal audio input for T-Watch Ultra +// PDM input on I2S_NUM_0 (hardware constraint for PDM on ESP32-S3) + +#ifndef MICROPHONE_H +#define MICROPHONE_H + +#if BOARD_MODEL == BOARD_TWATCH_ULT + +#include "driver/i2s.h" + +#define MIC_CLK_PIN 17 // PDM clock (WS pin in I2S terms) +#define MIC_DAT_PIN 18 // PDM data input +#define MIC_I2S_PORT I2S_NUM_0 // PDM only works on I2S0 +#define MIC_SAMPLE_RATE 16000 // 16kHz max for PDM mic +#define MIC_BUF_SIZE 1024 + +static bool mic_ready = false; + +bool mic_init() { + i2s_config_t i2s_config = {}; + i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM); + i2s_config.sample_rate = MIC_SAMPLE_RATE; + i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; + i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT; + i2s_config.communication_format = I2S_COMM_FORMAT_STAND_PCM_SHORT; + i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; + i2s_config.dma_buf_count = 6; + i2s_config.dma_buf_len = 512; + i2s_config.use_apll = true; + + i2s_pin_config_t pin_config = {}; + pin_config.bck_io_num = I2S_PIN_NO_CHANGE; + pin_config.ws_io_num = MIC_CLK_PIN; + pin_config.data_out_num = I2S_PIN_NO_CHANGE; + pin_config.data_in_num = MIC_DAT_PIN; + pin_config.mck_io_num = I2S_PIN_NO_CHANGE; + + if (i2s_driver_install(MIC_I2S_PORT, &i2s_config, 0, NULL) != ESP_OK) { + return false; + } + if (i2s_set_pin(MIC_I2S_PORT, &pin_config) != ESP_OK) { + i2s_driver_uninstall(MIC_I2S_PORT); + return false; + } + + mic_ready = true; + return true; +} + +void mic_end() { + if (mic_ready) { + i2s_driver_uninstall(MIC_I2S_PORT); + mic_ready = false; + } +} + +// Read raw audio samples into buffer. Returns bytes read. +size_t mic_read(int16_t *buf, size_t samples) { + if (!mic_ready) return 0; + size_t bytes_read = 0; + i2s_read(MIC_I2S_PORT, buf, samples * sizeof(int16_t), &bytes_read, portMAX_DELAY); + return bytes_read / sizeof(int16_t); +} + +// Get current audio level (RMS of a short sample). Returns 0-32767. +uint16_t mic_level() { + if (!mic_ready) return 0; + + int16_t buf[256]; + size_t samples = mic_read(buf, 256); + if (samples == 0) return 0; + + uint64_t sum_sq = 0; + for (size_t i = 0; i < samples; i++) { + int32_t s = buf[i]; + sum_sq += s * s; + } + return (uint16_t)sqrt((double)sum_sq / samples); +} + +#endif // BOARD_MODEL == BOARD_TWATCH_ULT +#endif // MICROPHONE_H diff --git a/RNode_Firmware.ino b/RNode_Firmware.ino index 8e97d15..d0c9ad4 100644 --- a/RNode_Firmware.ino +++ b/RNode_Firmware.ino @@ -29,8 +29,9 @@ SensorBHI260AP *bhi260 = NULL; bool bhi260_ready = false; - // MAX98357A I2S speaker + // MAX98357A I2S speaker + SPM1423 PDM microphone #include "Speaker.h" + #include "Microphone.h" // CST9217 capacitive touch panel #include @@ -307,9 +308,9 @@ void setup() { attachInterrupt(TP_INT, touch_isr, FALLING); } - // Init speaker (BLDO2 already enabled by PMU init) + // Init speaker (BLDO2 already enabled by PMU init) and microphone speaker_init(); - if (speaker_ready) speaker_beep(); // Boot audio feedback + mic_init(); // BHI260AP init deferred — firmware upload takes ~10s at 1MHz I2C // and blocks serial communication during boot. Will be initialized @@ -2078,6 +2079,7 @@ void twatch_enter_deep_sleep(bool beacon_timer) { } // 1. Shut down audio and display before closing buses + mic_end(); speaker_end(); #if HAS_DISPLAY co5300_sleep(); diff --git a/Speaker.h b/Speaker.h index 919043a..1236c4d 100644 --- a/Speaker.h +++ b/Speaker.h @@ -12,7 +12,7 @@ #define SPK_BCLK I2S_BCLK #define SPK_WCLK I2S_WCLK #define SPK_DOUT I2S_DOUT -#define SPK_I2S_PORT I2S_NUM_0 +#define SPK_I2S_PORT I2S_NUM_1 // I2S_NUM_0 reserved for PDM microphone #define SPK_SAMPLE_RATE 16000 #define SPK_TONE_BUF_SIZE 512